@astrosheep/keiyaku 2.9.13 → 4.0.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/README.md +10 -130
- package/build/integrations/marketplace/.agents/plugins/marketplace.json +12 -0
- package/build/integrations/marketplace/.claude-plugin/marketplace.json +5 -0
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +7 -0
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +18 -0
- package/build/integrations/marketplace/plugins/keiyaku/opencode.js +16 -0
- package/build/integrations/marketplace/plugins/keiyaku/package.json +8 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +34 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +50 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +62 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +72 -0
- package/build/src/akuma/activity.d.ts +66 -0
- package/build/src/akuma/activity.js +139 -0
- package/build/src/akuma/akuma.d.ts +116 -0
- package/build/src/akuma/akuma.js +382 -0
- package/build/src/akuma/archetype.d.ts +31 -0
- package/build/src/akuma/archetype.js +188 -0
- package/build/src/akuma/body.d.ts +20 -0
- package/build/src/akuma/body.js +305 -0
- package/build/src/akuma/heart/facts.d.ts +150 -0
- package/build/src/akuma/heart/facts.js +9 -0
- package/build/src/akuma/heart/index.d.ts +78 -0
- package/build/src/akuma/heart/index.js +361 -0
- package/build/src/akuma/heart/rows.d.ts +189 -0
- package/build/src/akuma/heart/rows.js +319 -0
- package/build/src/akuma/heart/schema.d.ts +5 -0
- package/build/src/akuma/heart/schema.js +113 -0
- package/build/src/akuma/identity.d.ts +46 -0
- package/build/src/akuma/identity.js +89 -0
- package/build/src/akuma/index.d.ts +7 -0
- package/build/src/akuma/index.js +3 -0
- package/build/src/akuma/provider.d.ts +123 -0
- package/build/src/akuma/provider.js +235 -0
- package/build/src/akuma/providers/claude.d.ts +84 -0
- package/build/src/akuma/providers/claude.js +305 -0
- package/build/src/akuma/providers/codex-app-server/events.d.ts +110 -0
- package/build/src/akuma/providers/codex-app-server/events.js +337 -0
- package/build/src/akuma/providers/codex-app-server/index.d.ts +27 -0
- package/build/src/akuma/providers/codex-app-server/index.js +162 -0
- package/build/src/akuma/providers/index.d.ts +4 -0
- package/build/src/akuma/providers/index.js +71 -0
- package/build/src/akuma/publication.d.ts +10 -0
- package/build/src/akuma/publication.js +107 -0
- package/build/src/akuma/requests.d.ts +46 -0
- package/build/src/akuma/requests.js +388 -0
- package/build/src/alias/index.d.ts +17 -0
- package/build/src/alias/index.js +104 -0
- package/build/src/body/amend.d.ts +2 -0
- package/build/src/body/amend.js +289 -0
- package/build/src/body/arc.d.ts +2 -0
- package/build/src/body/arc.js +54 -0
- package/build/src/body/decode.d.ts +4 -0
- package/build/src/body/decode.js +146 -0
- package/build/src/body/keys.d.ts +4 -0
- package/build/src/body/keys.js +11 -0
- package/build/src/body/region.d.ts +6 -0
- package/build/src/body/region.js +139 -0
- package/build/src/body/render.d.ts +4 -0
- package/build/src/body/render.js +73 -0
- package/build/src/body/shape.d.ts +29 -0
- package/build/src/body/shape.js +16 -0
- package/build/src/body/types.d.ts +28 -0
- package/build/src/body/verification.d.ts +7 -0
- package/build/src/body/verification.js +25 -0
- package/build/src/cli/accepted.d.ts +16 -0
- package/build/src/cli/accepted.js +65 -0
- package/build/src/cli/actor.d.ts +8 -0
- package/build/src/cli/actor.js +11 -0
- package/build/src/cli/commands/akuma-invoke.d.ts +53 -0
- package/build/src/cli/commands/akuma-invoke.js +68 -0
- package/build/src/cli/commands/akuma.d.ts +42 -0
- package/build/src/cli/commands/akuma.js +224 -0
- package/build/src/cli/commands/amend.d.ts +13 -0
- package/build/src/cli/commands/amend.js +13 -0
- package/build/src/cli/commands/bind.d.ts +12 -0
- package/build/src/cli/commands/bind.js +17 -0
- package/build/src/cli/commands/install.d.ts +25 -0
- package/build/src/cli/commands/install.js +103 -0
- package/build/src/cli/commands/task-invoke.d.ts +9 -0
- package/build/src/cli/commands/task-invoke.js +92 -0
- package/build/src/cli/commands/task.d.ts +16 -0
- package/build/src/cli/commands/task.js +144 -0
- package/build/src/cli/index.d.ts +2 -0
- package/build/src/cli/index.js +3 -0
- package/build/src/cli/invoke.d.ts +17 -0
- package/build/src/cli/invoke.js +265 -0
- package/build/src/cli/main.d.ts +1 -0
- package/build/src/cli/main.js +79 -0
- package/build/src/cli/parse.d.ts +210 -0
- package/build/src/cli/parse.js +375 -0
- package/build/src/cli/render/akuma-tool.d.ts +11 -0
- package/build/src/cli/render/akuma-tool.js +68 -0
- package/build/src/cli/render/akuma.d.ts +6 -0
- package/build/src/cli/render/akuma.js +254 -0
- package/build/src/cli/render/board.d.ts +2 -0
- package/build/src/cli/render/board.js +4 -0
- package/build/src/cli/render/contract.d.ts +4 -0
- package/build/src/cli/render/contract.js +69 -0
- package/build/src/cli/render/kanshi.d.ts +3 -0
- package/build/src/cli/render/kanshi.js +204 -0
- package/build/src/cli/render/refusal.d.ts +2 -0
- package/build/src/cli/render/refusal.js +4 -0
- package/build/src/cli/render/settings.d.ts +3 -0
- package/build/src/cli/render/settings.js +30 -0
- package/build/src/cli/render/task.d.ts +6 -0
- package/build/src/cli/render/task.js +105 -0
- package/build/src/cli/render/terminal.d.ts +10 -0
- package/build/src/cli/render/terminal.js +76 -0
- package/build/src/cli/render/text.d.ts +3 -0
- package/build/src/cli/render/text.js +15 -0
- package/build/src/cli/result.d.ts +55 -0
- package/build/src/cli/selectors.d.ts +9 -0
- package/build/src/cli/selectors.js +66 -0
- package/build/src/cli/usage.d.ts +6 -0
- package/build/src/cli/usage.js +19 -0
- package/build/src/context-root.d.ts +4 -0
- package/build/src/context-root.js +15 -0
- package/build/src/coordination/durable-file.d.ts +1 -0
- package/build/src/coordination/durable-file.js +36 -0
- package/build/src/coordination/sqlite-transaction-lock.d.ts +14 -0
- package/build/src/coordination/sqlite-transaction-lock.js +119 -0
- package/build/src/core/decide.d.ts +45 -0
- package/build/src/core/facts/codec.d.ts +3 -0
- package/build/src/core/facts/codec.js +258 -0
- package/build/src/core/facts/eligibility.d.ts +16 -0
- package/build/src/core/facts/eligibility.js +75 -0
- package/build/src/core/facts/errors.d.ts +3 -0
- package/build/src/core/facts/errors.js +6 -0
- package/build/src/core/facts/fold.d.ts +2 -0
- package/build/src/core/facts/fold.js +97 -0
- package/build/src/core/facts/gate.d.ts +24 -0
- package/build/src/core/facts/gate.js +52 -0
- package/build/src/core/facts/observation.d.ts +12 -0
- package/build/src/core/facts/observation.js +23 -0
- package/build/src/core/facts/offer.d.ts +21 -0
- package/build/src/core/facts/types.d.ts +130 -0
- package/build/src/core/facts/types.js +66 -0
- package/build/src/core/subject.d.ts +21 -0
- package/build/src/core/subject.js +89 -0
- package/build/src/core/verbs/abandon.d.ts +13 -0
- package/build/src/core/verbs/abandon.js +17 -0
- package/build/src/core/verbs/amend.d.ts +14 -0
- package/build/src/core/verbs/amend.js +56 -0
- package/build/src/core/verbs/arc.d.ts +13 -0
- package/build/src/core/verbs/arc.js +25 -0
- package/build/src/core/verbs/attestation.d.ts +13 -0
- package/build/src/core/verbs/attestation.js +21 -0
- package/build/src/core/verbs/bind.d.ts +13 -0
- package/build/src/core/verbs/bind.js +40 -0
- package/build/src/core/verbs/deliver.d.ts +13 -0
- package/build/src/core/verbs/deliver.js +26 -0
- package/build/src/core/verbs/placement.d.ts +13 -0
- package/build/src/core/verbs/placement.js +42 -0
- package/build/src/dispatch/index.d.ts +31 -0
- package/build/src/dispatch/index.js +159 -0
- package/build/src/git/admission.d.ts +18 -0
- package/build/src/git/admission.js +116 -0
- package/build/src/git/delivery.d.ts +20 -0
- package/build/src/git/delivery.js +109 -0
- package/build/src/git/hooks.d.ts +34 -0
- package/build/src/git/hooks.js +258 -0
- package/build/src/git/identity.d.ts +13 -0
- package/build/src/git/identity.js +28 -0
- package/build/src/git/observe.d.ts +38 -0
- package/build/src/git/observe.js +227 -0
- package/build/src/git/reconcile.d.ts +49 -0
- package/build/src/git/reconcile.js +223 -0
- package/build/src/git/repository.d.ts +80 -0
- package/build/src/git/repository.js +456 -0
- package/build/src/git/tree.d.ts +34 -0
- package/build/src/git/tree.js +112 -0
- package/build/src/git/verification.d.ts +14 -0
- package/build/src/git/verification.js +56 -0
- package/build/src/identity/coordinates.d.ts +9 -0
- package/build/src/identity/coordinates.js +20 -0
- package/build/src/identity/normalize.d.ts +8 -0
- package/build/src/identity/normalize.js +45 -0
- package/build/src/identity/selector.d.ts +6 -0
- package/build/src/identity/selector.js +7 -0
- package/build/src/index.d.ts +4 -0
- package/build/src/index.js +2 -0
- package/build/src/kanshi/index.d.ts +5 -0
- package/build/src/kanshi/index.js +2 -0
- package/build/src/kanshi/read.d.ts +5 -0
- package/build/src/kanshi/read.js +100 -0
- package/build/src/kanshi/report.d.ts +35 -0
- package/build/src/kanshi/select.d.ts +8 -0
- package/build/src/kanshi/select.js +21 -0
- package/build/src/library/akuma-creation.d.ts +79 -0
- package/build/src/library/akuma-creation.js +151 -0
- package/build/src/library/configuration.d.ts +26 -0
- package/build/src/library/configuration.js +100 -0
- package/build/src/library/contract.d.ts +145 -0
- package/build/src/library/contract.js +325 -0
- package/build/src/library/input.d.ts +13 -0
- package/build/src/library/input.js +82 -0
- package/build/src/library/keiyaku.d.ts +28 -0
- package/build/src/library/keiyaku.js +16 -0
- package/build/src/library/region.d.ts +17 -0
- package/build/src/library/region.js +30 -0
- package/build/src/library/repo.d.ts +33 -0
- package/build/src/library/repo.js +61 -0
- package/build/src/markdown/diff.d.ts +1 -0
- package/build/src/markdown/diff.js +4 -0
- package/build/src/markdown/lex.d.ts +3 -0
- package/build/src/markdown/lex.js +221 -0
- package/build/src/markdown/parse.d.ts +2 -0
- package/build/src/markdown/parse.js +221 -0
- package/build/src/markdown/query.d.ts +21 -0
- package/build/src/markdown/query.js +51 -0
- package/build/src/markdown/types.d.ts +118 -0
- package/build/src/markdown/types.js +6 -0
- package/build/src/protocol/attempt.d.ts +36 -0
- package/build/src/protocol/attempt.js +119 -0
- package/build/src/protocol/bind.d.ts +25 -0
- package/build/src/protocol/bind.js +62 -0
- package/build/src/protocol/intent.d.ts +52 -0
- package/build/src/protocol/intent.js +129 -0
- package/build/src/protocol/operations.d.ts +140 -0
- package/build/src/protocol/operations.js +373 -0
- package/build/src/protocol/outcome.d.ts +18 -0
- package/build/src/protocol/outcome.js +15 -0
- package/build/src/protocol/read/audit.d.ts +25 -0
- package/build/src/protocol/read/audit.js +39 -0
- package/build/src/protocol/read/documents.d.ts +8 -0
- package/build/src/protocol/read/documents.js +16 -0
- package/build/src/protocol/read/status.d.ts +38 -0
- package/build/src/protocol/read/status.js +47 -0
- package/build/src/protocol/run.d.ts +39 -0
- package/build/src/protocol/run.js +36 -0
- package/build/src/runtime/proc/line-rpc.d.ts +37 -0
- package/build/src/runtime/proc/line-rpc.js +124 -0
- package/build/src/runtime/proc/run.d.ts +54 -0
- package/build/src/runtime/proc/run.js +200 -0
- package/build/src/settings.d.ts +45 -0
- package/build/src/settings.js +99 -0
- package/build/src/settlement/holder.d.ts +15 -0
- package/build/src/settlement/holder.js +113 -0
- package/build/src/settlement/settle.d.ts +36 -0
- package/build/src/settlement/settle.js +91 -0
- package/build/src/task/board.d.ts +65 -0
- package/build/src/task/board.js +155 -0
- package/build/src/task/compose.d.ts +25 -0
- package/build/src/task/compose.js +232 -0
- package/build/src/task/context.d.ts +4 -0
- package/build/src/task/context.js +56 -0
- package/build/src/task/document.d.ts +24 -0
- package/build/src/task/document.js +118 -0
- package/build/src/task/identity.d.ts +12 -0
- package/build/src/task/identity.js +47 -0
- package/build/src/task/index.d.ts +90 -0
- package/build/src/task/index.js +179 -0
- package/build/src/task/operations.d.ts +115 -0
- package/build/src/task/operations.js +194 -0
- package/build/src/task/store.d.ts +22 -0
- package/build/src/task/store.js +114 -0
- package/build/src/verification/declaration.d.ts +22 -0
- package/build/src/verification/declaration.js +14 -0
- package/build/src/verification/producer.d.ts +23 -0
- package/build/src/verification/producer.js +54 -0
- package/package.json +62 -44
- package/build/.tsbuildinfo +0 -1
- package/build/agents/call-terms.js +0 -32
- package/build/agents/harness/activity-values.js +0 -70
- package/build/agents/harness/control-types.js +0 -18
- package/build/agents/harness/event-channel.js +0 -213
- package/build/agents/harness/event-persistence.js +0 -220
- package/build/agents/harness/events.js +0 -48
- package/build/agents/harness/execution-handle.js +0 -172
- package/build/agents/harness/index.js +0 -11
- package/build/agents/harness/outcome-error.js +0 -81
- package/build/agents/harness/outcome.js +0 -220
- package/build/agents/harness/projection.js +0 -369
- package/build/agents/harness/provider-adapter.js +0 -58
- package/build/agents/harness/router.js +0 -30
- package/build/agents/harness/runtime.js +0 -85
- package/build/agents/harness/session.js +0 -15
- package/build/agents/launch-snapshot/model.js +0 -127
- package/build/agents/launch-snapshot/parse-helpers.js +0 -114
- package/build/agents/launch-snapshot/resolve.js +0 -46
- package/build/agents/opencode-sdk.js +0 -159
- package/build/agents/providers/claude-agent-sdk/adapter.js +0 -396
- package/build/agents/providers/claude-agent-sdk/events.js +0 -218
- package/build/agents/providers/claude-agent-sdk/registration.js +0 -225
- package/build/agents/providers/claude-agent-sdk/session-schema.js +0 -2
- package/build/agents/providers/claude-agent-sdk/session.js +0 -172
- package/build/agents/providers/codex-app-server/adapter.js +0 -387
- package/build/agents/providers/codex-app-server/events.js +0 -492
- package/build/agents/providers/codex-app-server/identity.js +0 -1
- package/build/agents/providers/codex-app-server/policy.js +0 -29
- package/build/agents/providers/codex-app-server/registration.js +0 -259
- package/build/agents/providers/codex-app-server/session-schema.js +0 -6
- package/build/agents/providers/codex-app-server/session.js +0 -273
- package/build/agents/providers/event-type-tail.js +0 -16
- package/build/agents/providers/opencode-sdk/adapter.js +0 -402
- package/build/agents/providers/opencode-sdk/events.js +0 -380
- package/build/agents/providers/opencode-sdk/registration.js +0 -146
- package/build/agents/providers/opencode-sdk/session-schema.js +0 -2
- package/build/agents/providers/opencode-sdk/session.js +0 -187
- package/build/agents/providers/pi/adapter.js +0 -285
- package/build/agents/providers/pi/checkpoint.js +0 -49
- package/build/agents/providers/pi/events.js +0 -285
- package/build/agents/providers/pi/registration.js +0 -160
- package/build/agents/providers/pi/session-schema.js +0 -6
- package/build/agents/providers/registration.js +0 -7
- package/build/agents/providers/registry.js +0 -76
- package/build/agents/selector.js +0 -120
- package/build/agents/session.js +0 -29
- package/build/agents/string-tail-buffer.js +0 -44
- package/build/agents/types.js +0 -30
- package/build/artifact-directories.js +0 -22
- package/build/cli/commands/akuma/akuma/handler.js +0 -5
- package/build/cli/commands/akuma/akuma/meta.js +0 -17
- package/build/cli/commands/akuma/catalog.js +0 -10
- package/build/cli/commands/akuma/list/handler.js +0 -15
- package/build/cli/commands/akuma/list/meta-list.js +0 -12
- package/build/cli/commands/akuma/list/meta-ls.js +0 -9
- package/build/cli/commands/akuma/show/handler.js +0 -27
- package/build/cli/commands/akuma/show/meta.js +0 -8
- package/build/cli/commands/akuma.js +0 -186
- package/build/cli/commands/contract/amend/handler.js +0 -17
- package/build/cli/commands/contract/amend/meta.js +0 -13
- package/build/cli/commands/contract/arc/handler.js +0 -16
- package/build/cli/commands/contract/arc/meta.js +0 -28
- package/build/cli/commands/contract/audit/handler.js +0 -42
- package/build/cli/commands/contract/audit/meta.js +0 -17
- package/build/cli/commands/contract/bind/handler.js +0 -39
- package/build/cli/commands/contract/bind/meta.js +0 -96
- package/build/cli/commands/contract/catalog.js +0 -18
- package/build/cli/commands/contract/forfeit/handler.js +0 -17
- package/build/cli/commands/contract/forfeit/meta.js +0 -12
- package/build/cli/commands/contract/log/handler.js +0 -12
- package/build/cli/commands/contract/log/meta.js +0 -12
- package/build/cli/commands/contract/petition/handler.js +0 -64
- package/build/cli/commands/contract/petition/meta.js +0 -23
- package/build/cli/commands/contract/renew/handler.js +0 -65
- package/build/cli/commands/contract/renew/meta.js +0 -19
- package/build/cli/commands/metadata.js +0 -68
- package/build/cli/commands/projection/call/handler.js +0 -43
- package/build/cli/commands/projection/call/meta.js +0 -12
- package/build/cli/commands/projection/catalog.js +0 -16
- package/build/cli/commands/projection/fork/handler.js +0 -61
- package/build/cli/commands/projection/fork/meta.js +0 -12
- package/build/cli/commands/projection/history/handler.js +0 -65
- package/build/cli/commands/projection/history/meta.js +0 -9
- package/build/cli/commands/projection/kill/handler.js +0 -20
- package/build/cli/commands/projection/kill/meta.js +0 -11
- package/build/cli/commands/projection/shared.js +0 -27
- package/build/cli/commands/projection/status/handler.js +0 -42
- package/build/cli/commands/projection/status/meta.js +0 -20
- package/build/cli/commands/projection/tell/handler.js +0 -32
- package/build/cli/commands/projection/tell/meta.js +0 -12
- package/build/cli/commands/projection/wait/handler.js +0 -80
- package/build/cli/commands/projection/wait/meta.js +0 -16
- package/build/cli/commands/registry.js +0 -48
- package/build/cli/commands/shared.js +0 -74
- package/build/cli/commands/skills/catalog.js +0 -6
- package/build/cli/commands/skills/install/handler.js +0 -7
- package/build/cli/commands/skills/install/meta.js +0 -9
- package/build/cli/commands/skills/skills/handler.js +0 -4
- package/build/cli/commands/skills/skills/meta.js +0 -10
- package/build/cli/commands/system/catalog.js +0 -10
- package/build/cli/commands/system/completion/handler.js +0 -10
- package/build/cli/commands/system/completion/meta.js +0 -9
- package/build/cli/commands/system/dump-env/handler.js +0 -3
- package/build/cli/commands/system/dump-env/meta.js +0 -10
- package/build/cli/commands/system/guide/handler.js +0 -4
- package/build/cli/commands/system/guide/meta.js +0 -9
- package/build/cli/commands/task/add/handler.js +0 -38
- package/build/cli/commands/task/add/meta.js +0 -31
- package/build/cli/commands/task/catalog.js +0 -32
- package/build/cli/commands/task/compose/handler.js +0 -18
- package/build/cli/commands/task/compose/meta.js +0 -10
- package/build/cli/commands/task/doctor/handler.js +0 -3
- package/build/cli/commands/task/doctor/meta.js +0 -8
- package/build/cli/commands/task/done/handler.js +0 -10
- package/build/cli/commands/task/done/meta.js +0 -8
- package/build/cli/commands/task/drop/handler.js +0 -8
- package/build/cli/commands/task/drop/meta.js +0 -11
- package/build/cli/commands/task/hold/handler.js +0 -3
- package/build/cli/commands/task/hold/meta.js +0 -8
- package/build/cli/commands/task/log/handler.js +0 -12
- package/build/cli/commands/task/log/meta.js +0 -8
- package/build/cli/commands/task/ls/handler.js +0 -17
- package/build/cli/commands/task/ls/meta.js +0 -8
- package/build/cli/commands/task/note/handler.js +0 -6
- package/build/cli/commands/task/note/meta.js +0 -11
- package/build/cli/commands/task/resume/handler.js +0 -3
- package/build/cli/commands/task/resume/meta.js +0 -8
- package/build/cli/commands/task/shared.js +0 -181
- package/build/cli/commands/task/show/handler.js +0 -13
- package/build/cli/commands/task/show/meta.js +0 -8
- package/build/cli/commands/task/start/handler.js +0 -3
- package/build/cli/commands/task/start/meta.js +0 -8
- package/build/cli/commands/task/stop/handler.js +0 -10
- package/build/cli/commands/task/stop/meta.js +0 -8
- package/build/cli/commands/task/task/handler.js +0 -5
- package/build/cli/commands/task/task/meta.js +0 -11
- package/build/cli/commands/task/update/handler.js +0 -21
- package/build/cli/commands/task/update/meta.js +0 -12
- package/build/cli/commands/verification/handler.js +0 -43
- package/build/cli/commands/verification/meta.js +0 -13
- package/build/cli/completion.js +0 -91
- package/build/cli/draft-artifact.js +0 -50
- package/build/cli/flags.js +0 -321
- package/build/cli/help.js +0 -99
- package/build/cli/index.js +0 -333
- package/build/cli/missing-subcommand.js +0 -7
- package/build/cli/output-stream-error-policy.js +0 -31
- package/build/cli/parse-flags.js +0 -301
- package/build/cli/parse-metadata.js +0 -132
- package/build/cli/parse-selectors.js +0 -83
- package/build/cli/parse.js +0 -250
- package/build/cli/projection-address.js +0 -105
- package/build/cli/render/address.js +0 -101
- package/build/cli/render/amendment-warning.js +0 -8
- package/build/cli/render/arc.js +0 -88
- package/build/cli/render/audit.js +0 -440
- package/build/cli/render/call.js +0 -70
- package/build/cli/render/compact-text.js +0 -3
- package/build/cli/render/errors.js +0 -101
- package/build/cli/render/format.js +0 -226
- package/build/cli/render/kanshi.js +0 -300
- package/build/cli/render/kill.js +0 -9
- package/build/cli/render/lifecycle.js +0 -56
- package/build/cli/render/line-width.js +0 -103
- package/build/cli/render/misc.js +0 -84
- package/build/cli/render/path-prefix-compaction.js +0 -131
- package/build/cli/render/petition.js +0 -85
- package/build/cli/render/projection-activity.js +0 -332
- package/build/cli/render/projection-history.js +0 -77
- package/build/cli/render/response-style.js +0 -27
- package/build/cli/render/shared.js +0 -232
- package/build/cli/render/skills.js +0 -10
- package/build/cli/render/status-indicator.js +0 -47
- package/build/cli/render/status.js +0 -243
- package/build/cli/render/success-response.js +0 -95
- package/build/cli/render/task.js +0 -175
- package/build/cli/render/tell.js +0 -11
- package/build/cli/render/terminal-failure.js +0 -65
- package/build/cli/render/tool-command-normalization.js +0 -137
- package/build/cli/render/tool-ledger-rollup.js +0 -74
- package/build/cli/render/tool-presentation.js +0 -98
- package/build/cli/render/types.js +0 -2
- package/build/cli/render/verification.js +0 -23
- package/build/cli/render/wait.js +0 -343
- package/build/cli/skills-install.js +0 -348
- package/build/cli/subagent-guard.js +0 -25
- package/build/cli/types.js +0 -2
- package/build/config/akuma-loader.js +0 -240
- package/build/config/env-keys.js +0 -57
- package/build/config/env.js +0 -304
- package/build/config/provider-policy-ownership.js +0 -10
- package/build/config/settings/disease.js +0 -33
- package/build/config/settings/knobs.js +0 -14
- package/build/config/settings/loader.js +0 -275
- package/build/config/settings/provider-instance-fields.js +0 -51
- package/build/config/settings/schema.js +0 -113
- package/build/core/address-carrier.js +0 -89
- package/build/core/addressing.js +0 -407
- package/build/core/amend.js +0 -321
- package/build/core/amendment-criteria.js +0 -17
- package/build/core/amendment-warning.js +0 -10
- package/build/core/arc.js +0 -293
- package/build/core/atomic-publish.js +0 -64
- package/build/core/audit/candidate.js +0 -141
- package/build/core/audit/coordinates.js +0 -230
- package/build/core/audit/evidence.js +0 -156
- package/build/core/audit/facade.js +0 -65
- package/build/core/audit/readiness.js +0 -4
- package/build/core/audit/report.js +0 -38
- package/build/core/audit.js +0 -1
- package/build/core/bind-reconciliation.js +0 -409
- package/build/core/bind-workspace.js +0 -115
- package/build/core/bind.js +0 -503
- package/build/core/call/call.js +0 -235
- package/build/core/call/context.js +0 -210
- package/build/core/call/execution.js +0 -255
- package/build/core/call/prompt.js +0 -27
- package/build/core/call/reference.js +0 -24
- package/build/core/call-persist.js +0 -76
- package/build/core/command-markdown.js +0 -45
- package/build/core/context.js +0 -104
- package/build/core/contract-carrier-runtime.js +0 -512
- package/build/core/contract-carrier.js +0 -353
- package/build/core/contract-view.js +0 -319
- package/build/core/contract.js +0 -19
- package/build/core/derived-replay.js +0 -340
- package/build/core/draft.js +0 -400
- package/build/core/execution-coordinate.js +0 -46
- package/build/core/execution-pact.js +0 -126
- package/build/core/forfeit.js +0 -132
- package/build/core/hints.js +0 -34
- package/build/core/identifier-slug.js +0 -116
- package/build/core/ids.js +0 -90
- package/build/core/lifecycle-history.js +0 -48
- package/build/core/lifecycle-recovery.js +0 -122
- package/build/core/lifecycle-runner.js +0 -377
- package/build/core/log.js +0 -50
- package/build/core/markdown/lex.js +0 -274
- package/build/core/markdown/parser.js +0 -260
- package/build/core/markdown/render.js +0 -68
- package/build/core/markdown/sections.js +0 -80
- package/build/core/markdown/titles.js +0 -9
- package/build/core/markdown/types.js +0 -6
- package/build/core/outcome-base.js +0 -185
- package/build/core/places.js +0 -175
- package/build/core/projection/akuma-name.js +0 -43
- package/build/core/projection/generation/database.js +0 -473
- package/build/core/projection/generation/identity.js +0 -84
- package/build/core/projection/generation/ledger.js +0 -91
- package/build/core/projection/generation/model.js +0 -261
- package/build/core/projection/generation/projection-generation-continuation.js +0 -84
- package/build/core/projection/generation/projection-generation-doorbell.js +0 -88
- package/build/core/projection/generation/projection-generation-execution.js +0 -241
- package/build/core/projection/generation/projection-generation-identity.js +0 -21
- package/build/core/projection/generation/projection-generation-launcher.js +0 -154
- package/build/core/projection/generation/projection-generation-process.js +0 -47
- package/build/core/projection/generation/projection-generation-runner.js +0 -489
- package/build/core/projection/generation/projection-generation-runtime.js +0 -46
- package/build/core/projection/generation/protocol.js +0 -68
- package/build/core/projection/generation/store.js +0 -108
- package/build/core/projection/generation/transitions.js +0 -463
- package/build/core/projection/heart.js +0 -9
- package/build/core/projection/index.js +0 -28
- package/build/core/projection/projection-activity.js +0 -273
- package/build/core/projection/projection-alias.js +0 -131
- package/build/core/projection/projection-coordinate.js +0 -61
- package/build/core/projection/projection-core.js +0 -127
- package/build/core/projection/projection-execution-observer.js +0 -270
- package/build/core/projection/projection-history.js +0 -474
- package/build/core/projection/projection-identity.js +0 -11
- package/build/core/projection/projection-kill.js +0 -112
- package/build/core/projection/projection-life-observer.js +0 -101
- package/build/core/projection/projection-mint.js +0 -170
- package/build/core/projection/projection-status-observation.js +0 -434
- package/build/core/projection/projection-status.js +0 -442
- package/build/core/projection/projection-terminal-failure.js +0 -111
- package/build/core/projection/projection-wait.js +0 -337
- package/build/core/projection/projection-wake.js +0 -298
- package/build/core/projection/tell/launch-store.js +0 -244
- package/build/core/projection/tell/model.js +0 -98
- package/build/core/projection/tell/store.js +0 -245
- package/build/core/provider-loader-ports.js +0 -4
- package/build/core/registry.js +0 -100
- package/build/core/renew-build.js +0 -554
- package/build/core/renew-plan.js +0 -264
- package/build/core/renew-rewrite.js +0 -118
- package/build/core/renew.js +0 -345
- package/build/core/repository-ledger/accepted-fold-read.js +0 -22
- package/build/core/repository-ledger/accepted-tail.js +0 -116
- package/build/core/repository-ledger/admission.js +0 -38
- package/build/core/repository-ledger/authoritative-transaction.js +0 -1
- package/build/core/repository-ledger/claim-evidence.js +0 -147
- package/build/core/repository-ledger/codec.js +0 -303
- package/build/core/repository-ledger/current-state-store.js +0 -968
- package/build/core/repository-ledger/fold-repository.js +0 -374
- package/build/core/repository-ledger/fold.js +0 -248
- package/build/core/repository-ledger/git-object-store.js +0 -243
- package/build/core/repository-ledger/history-read.js +0 -15
- package/build/core/repository-ledger/identity.js +0 -60
- package/build/core/repository-ledger/incremental-read.js +0 -153
- package/build/core/repository-ledger/index.js +0 -15
- package/build/core/repository-ledger/inventory-plan.js +0 -75
- package/build/core/repository-ledger/inventory.js +0 -310
- package/build/core/repository-ledger/publication-admission.js +0 -125
- package/build/core/repository-ledger/publication.js +0 -353
- package/build/core/repository-ledger/read-model.js +0 -517
- package/build/core/repository-ledger/transaction-membership.js +0 -78
- package/build/core/repository-ledger/traversal.js +0 -404
- package/build/core/repository-ledger/write-transaction.js +0 -346
- package/build/core/response-artifact-catalog-sqlite.js +0 -112
- package/build/core/response-artifact-catalog.js +0 -7
- package/build/core/response-artifact-id.js +0 -5
- package/build/core/run-control/connection-bound-close.js +0 -41
- package/build/core/run-control/detached-lease-bootstrap.js +0 -208
- package/build/core/run-control/detached-process.js +0 -14
- package/build/core/run-control/index.js +0 -6
- package/build/core/run-control/process-tree.js +0 -139
- package/build/core/run-control/runner-lease.js +0 -172
- package/build/core/run-control/sqlite-process-lifetime-lock.js +0 -174
- package/build/core/scope.js +0 -416
- package/build/core/seal.js +0 -364
- package/build/core/settlement/claim-delivery.js +0 -82
- package/build/core/settlement/claim.js +0 -258
- package/build/core/settlement/index.js +0 -19
- package/build/core/settlement/petition-claim-gates.js +0 -247
- package/build/core/settlement/petition-forfeit.js +0 -46
- package/build/core/settlement/petition-head-guard.js +0 -12
- package/build/core/settlement/petition-io.js +0 -1
- package/build/core/settlement/petition-preview.js +0 -125
- package/build/core/settlement/petition-window.js +0 -50
- package/build/core/settlement/petition.js +0 -315
- package/build/core/settlement/queue-read-model.js +0 -120
- package/build/core/settlement/queue-seat-allocation.js +0 -65
- package/build/core/settlement/queue.js +0 -6
- package/build/core/settlement/settlement.js +0 -385
- package/build/core/settlement/verdict.js +0 -126
- package/build/core/settlement/verification.js +0 -554
- package/build/core/status/board.js +0 -455
- package/build/core/status/drift.js +0 -113
- package/build/core/status/lifecycle.js +0 -11
- package/build/core/stored-agent-event.js +0 -129
- package/build/core/structured-query.js +0 -23
- package/build/core/target-ref.js +0 -10
- package/build/core/task/board.js +0 -248
- package/build/core/task/commands.js +0 -401
- package/build/core/task/compose.js +0 -328
- package/build/core/task/coordinate.js +0 -56
- package/build/core/task/document.js +0 -273
- package/build/core/task/index.js +0 -16
- package/build/core/task/model.js +0 -8
- package/build/core/task/query.js +0 -204
- package/build/core/task/settlement-git.js +0 -423
- package/build/core/task/settlement-policy.js +0 -137
- package/build/core/task/source-board.js +0 -104
- package/build/core/task/source-model.js +0 -1
- package/build/core/task/task-bind-preparation.js +0 -243
- package/build/core/task/task-contract.js +0 -371
- package/build/core/task/task-doctor.js +0 -14
- package/build/core/task/task-file-transaction.js +0 -171
- package/build/core/task/task-git-actor.js +0 -33
- package/build/core/task/task-git-runtime.js +0 -97
- package/build/core/task/task-git-store.js +0 -400
- package/build/core/task/task-repository.js +0 -1
- package/build/core/task/task-store-repository.js +0 -97
- package/build/core/task/task-store.js +0 -1
- package/build/core/task/task-worktree.js +0 -25
- package/build/core/task/task.js +0 -7
- package/build/core/task/tree.js +0 -37
- package/build/core/task/validation.js +0 -48
- package/build/core/transcripts.js +0 -485
- package/build/core/validation.js +0 -17
- package/build/core/verification-declaration.js +0 -95
- package/build/core/worktree-bootstrap.js +0 -195
- package/build/core/worktree-path.js +0 -371
- package/build/errno.js +0 -3
- package/build/flow-error.js +0 -191
- package/build/fs/durability.js +0 -27
- package/build/fs/path-coordinate.js +0 -16
- package/build/generated/version.js +0 -3
- package/build/git/branches.js +0 -84
- package/build/git/commits.js +0 -151
- package/build/git/core.js +0 -86
- package/build/git/diff/parsers.js +0 -181
- package/build/git/diff/pathspec.js +0 -3
- package/build/git/diff/preview.js +0 -38
- package/build/git/diff/read.js +0 -105
- package/build/git/diff/structured.js +0 -127
- package/build/git/diff/types.js +0 -1
- package/build/git/path-coordinate.js +0 -17
- package/build/git/process.js +0 -229
- package/build/git/refs.js +0 -250
- package/build/git/staging.js +0 -79
- package/build/git/streaming-batch.js +0 -354
- package/build/git/worktree.js +0 -175
- package/build/index.js +0 -67
- package/build/keiyaku-constants.js +0 -2
- package/build/keiyaku.js +0 -30
- package/build/telemetry/debug-log.js +0 -40
- package/build/telemetry/logger.js +0 -48
- package/skills/keiyaku/SKILL.md +0 -46
- package/skills/keiyaku-akuma/SKILL.md +0 -97
- package/skills/keiyaku-task/SKILL.md +0 -118
- package/skills/keiyaku-workflow/SKILL.md +0 -191
- /package/build/{agents/launch-snapshot → src/body}/types.js +0 -0
- /package/build/{agents/providers/claude-agent-sdk/terms.js → src/cli/result.js} +0 -0
- /package/build/{agents/providers/codex-app-server/terms.js → src/core/decide.js} +0 -0
- /package/build/{agents/providers/opencode-sdk/terms.js → src/core/facts/offer.js} +0 -0
- /package/build/{agents/providers/pi/terms.js → src/kanshi/report.js} +0 -0
|
@@ -1,968 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
-
import { DatabaseSync } from "node:sqlite";
|
|
5
|
-
import { runGitProcess } from "../../git/core.js";
|
|
6
|
-
import { acquireSqliteProcessLifetimeLock } from "../run-control/sqlite-process-lifetime-lock.js";
|
|
7
|
-
import { applyFoldEntry } from "./fold.js";
|
|
8
|
-
import { createLifecycleEntrySchema, evidenceObjectId, repositoryDeliveryRef } from "./codec.js";
|
|
9
|
-
import { repositoryLedgerHead } from "./identity.js";
|
|
10
|
-
export const REPOSITORY_CURRENT_STATE_VERSION = 4;
|
|
11
|
-
export const REPOSITORY_CURRENT_STATE_PREFIX = "keiyaku-current-state-";
|
|
12
|
-
export const REPOSITORY_CURRENT_STATE_SUFFIX = ".sqlite";
|
|
13
|
-
const SNAPSHOT_READER_BUSY_MS = 5_000;
|
|
14
|
-
const SNAPSHOT_WRITER_LOCK_FILE = "keiyaku-current-state-writer-coordination.sqlite";
|
|
15
|
-
const SCHEMA = `
|
|
16
|
-
CREATE TABLE snapshot_schema (
|
|
17
|
-
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
18
|
-
version INTEGER NOT NULL CHECK (version = ${REPOSITORY_CURRENT_STATE_VERSION})
|
|
19
|
-
) STRICT;
|
|
20
|
-
CREATE TABLE snapshot_pin (
|
|
21
|
-
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
22
|
-
ledger_head TEXT,
|
|
23
|
-
object_format TEXT CHECK (object_format IS NULL OR object_format IN ('sha1', 'sha256')),
|
|
24
|
-
empty_tree TEXT
|
|
25
|
-
) STRICT;
|
|
26
|
-
CREATE TABLE snapshot_contracts (
|
|
27
|
-
contract TEXT PRIMARY KEY CHECK (length(contract) > 0),
|
|
28
|
-
status TEXT NOT NULL CHECK (status IN ('healthy', 'diseased', 'isolated')),
|
|
29
|
-
current_json TEXT CHECK (current_json IS NULL OR json_valid(current_json)),
|
|
30
|
-
fold_json TEXT CHECK (fold_json IS NULL OR json_valid(fold_json)),
|
|
31
|
-
diagnostics_json TEXT NOT NULL CHECK (json_valid(diagnostics_json))
|
|
32
|
-
) STRICT;
|
|
33
|
-
CREATE TABLE snapshot_history (
|
|
34
|
-
contract TEXT NOT NULL,
|
|
35
|
-
position INTEGER NOT NULL CHECK (position >= 0),
|
|
36
|
-
object_id TEXT NOT NULL,
|
|
37
|
-
entry_json TEXT NOT NULL CHECK (json_valid(entry_json)),
|
|
38
|
-
PRIMARY KEY(contract, position),
|
|
39
|
-
UNIQUE(object_id)
|
|
40
|
-
) STRICT;
|
|
41
|
-
CREATE TABLE snapshot_summaries (
|
|
42
|
-
contract TEXT PRIMARY KEY,
|
|
43
|
-
history_json TEXT NOT NULL CHECK (json_valid(history_json))
|
|
44
|
-
) STRICT;
|
|
45
|
-
CREATE TABLE snapshot_repository (
|
|
46
|
-
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
47
|
-
diagnostics_json TEXT NOT NULL CHECK (json_valid(diagnostics_json))
|
|
48
|
-
) STRICT;
|
|
49
|
-
CREATE TABLE snapshot_topology (
|
|
50
|
-
object_id TEXT PRIMARY KEY,
|
|
51
|
-
node_json TEXT NOT NULL CHECK (json_valid(node_json))
|
|
52
|
-
) STRICT;
|
|
53
|
-
CREATE TABLE snapshot_resources (
|
|
54
|
-
kind TEXT NOT NULL CHECK (kind IN ('worktree', 'task')),
|
|
55
|
-
resource_key TEXT NOT NULL,
|
|
56
|
-
contract TEXT NOT NULL,
|
|
57
|
-
PRIMARY KEY(kind, resource_key, contract)
|
|
58
|
-
) STRICT;
|
|
59
|
-
CREATE TABLE snapshot_integrity (
|
|
60
|
-
receipt_key TEXT PRIMARY KEY,
|
|
61
|
-
digest TEXT NOT NULL CHECK (length(digest) = 64)
|
|
62
|
-
) STRICT;
|
|
63
|
-
`;
|
|
64
|
-
function digest(value) {
|
|
65
|
-
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
66
|
-
}
|
|
67
|
-
function exactRows(database, sql, ...params) {
|
|
68
|
-
return database.prepare(sql).all(...params);
|
|
69
|
-
}
|
|
70
|
-
function pinDigest(pin) {
|
|
71
|
-
return digest(["keiyaku-current-state-v4", REPOSITORY_CURRENT_STATE_VERSION, pin.head, pin.objectFormat, pin.emptyTree]);
|
|
72
|
-
}
|
|
73
|
-
function repositoryDigest(database) {
|
|
74
|
-
return digest(["repository", exactRows(database, "SELECT singleton, diagnostics_json FROM snapshot_repository ORDER BY singleton")]);
|
|
75
|
-
}
|
|
76
|
-
function topologyDigest(database) {
|
|
77
|
-
return digest(["topology", exactRows(database, "SELECT object_id, node_json FROM snapshot_topology ORDER BY object_id")]);
|
|
78
|
-
}
|
|
79
|
-
function resourcesDigest(database, contract) {
|
|
80
|
-
return digest(["resources", contract ?? null, contract === undefined
|
|
81
|
-
? exactRows(database, "SELECT kind, resource_key, contract FROM snapshot_resources ORDER BY kind, resource_key, contract")
|
|
82
|
-
: exactRows(database, "SELECT kind, resource_key, contract FROM snapshot_resources WHERE contract = ? ORDER BY kind, resource_key", contract)]);
|
|
83
|
-
}
|
|
84
|
-
function contractDigest(database, contract, summaryOnly) {
|
|
85
|
-
const contractRows = summaryOnly
|
|
86
|
-
? exactRows(database, "SELECT contract, status, current_json, diagnostics_json FROM snapshot_contracts WHERE contract = ?", contract)
|
|
87
|
-
: exactRows(database, "SELECT contract, status, current_json, fold_json, diagnostics_json FROM snapshot_contracts WHERE contract = ?", contract);
|
|
88
|
-
const historyRows = summaryOnly
|
|
89
|
-
? exactRows(database, "SELECT contract, history_json FROM snapshot_summaries WHERE contract = ?", contract)
|
|
90
|
-
: exactRows(database, "SELECT contract, position, object_id, entry_json FROM snapshot_history WHERE contract = ? ORDER BY position", contract);
|
|
91
|
-
return digest([summaryOnly ? "contract-summary" : "contract-full", contract, contractRows, historyRows, resourcesDigest(database, contract)]);
|
|
92
|
-
}
|
|
93
|
-
function receipt(database, key) {
|
|
94
|
-
return database.prepare("SELECT digest FROM snapshot_integrity WHERE receipt_key = ?").get(key)?.digest ?? null;
|
|
95
|
-
}
|
|
96
|
-
function assertReceipt(database, key, actual) {
|
|
97
|
-
if (receipt(database, key) !== actual)
|
|
98
|
-
throw new Error(`snapshot integrity receipt ${key} is missing or mismatched`);
|
|
99
|
-
}
|
|
100
|
-
function writeIntegrityReceipts(database) {
|
|
101
|
-
database.exec("DELETE FROM snapshot_integrity");
|
|
102
|
-
const insert = database.prepare("INSERT INTO snapshot_integrity(receipt_key, digest) VALUES (?, ?)");
|
|
103
|
-
const pin = metadata(database);
|
|
104
|
-
if (!pin)
|
|
105
|
-
throw new Error("snapshot pin is missing while signing current state");
|
|
106
|
-
insert.run("pin", pinDigest(pin));
|
|
107
|
-
insert.run("repository", repositoryDigest(database));
|
|
108
|
-
insert.run("topology", topologyDigest(database));
|
|
109
|
-
insert.run("resources", resourcesDigest(database));
|
|
110
|
-
const contracts = database.prepare("SELECT contract FROM snapshot_contracts ORDER BY contract").all().map((row) => row.contract);
|
|
111
|
-
for (const contract of contracts) {
|
|
112
|
-
insert.run(`contract:${contract}:full`, contractDigest(database, contract, false));
|
|
113
|
-
insert.run(`contract:${contract}:summary`, contractDigest(database, contract, true));
|
|
114
|
-
}
|
|
115
|
-
insert.run("contracts", digest(["contracts", contracts.map((contract) => [contract, receipt(database, `contract:${contract}:full`), receipt(database, `contract:${contract}:summary`)])]));
|
|
116
|
-
}
|
|
117
|
-
function validatePinReceipt(database, pin) {
|
|
118
|
-
assertReceipt(database, "pin", pinDigest(pin));
|
|
119
|
-
}
|
|
120
|
-
function validateContractReceipt(database, contract, summaryOnly) {
|
|
121
|
-
assertReceipt(database, `contract:${contract}:${summaryOnly ? "summary" : "full"}`, contractDigest(database, contract, summaryOnly));
|
|
122
|
-
}
|
|
123
|
-
function validateRepositoryReceipts(database, contracts, summaryOnly, includeTopology) {
|
|
124
|
-
assertReceipt(database, "repository", repositoryDigest(database));
|
|
125
|
-
assertReceipt(database, "resources", resourcesDigest(database));
|
|
126
|
-
for (const contract of contracts)
|
|
127
|
-
validateContractReceipt(database, contract, summaryOnly);
|
|
128
|
-
const signedContracts = contracts.map((contract) => [contract, receipt(database, `contract:${contract}:full`), receipt(database, `contract:${contract}:summary`)]);
|
|
129
|
-
assertReceipt(database, "contracts", digest(["contracts", signedContracts]));
|
|
130
|
-
if (includeTopology)
|
|
131
|
-
assertReceipt(database, "topology", topologyDigest(database));
|
|
132
|
-
}
|
|
133
|
-
function snapshotFileName(head) {
|
|
134
|
-
return `${REPOSITORY_CURRENT_STATE_PREFIX}${head ?? "empty"}${REPOSITORY_CURRENT_STATE_SUFFIX}`;
|
|
135
|
-
}
|
|
136
|
-
export async function repositoryCurrentStateSnapshotPath(cwd, head, signal) {
|
|
137
|
-
// Addressing callers already pass the resolved primary worktree root. Reuse
|
|
138
|
-
// that physical coordinate within this request instead of probing Git again.
|
|
139
|
-
const localGitDirectory = path.join(cwd, ".git");
|
|
140
|
-
try {
|
|
141
|
-
if ((await fs.stat(localGitDirectory)).isDirectory()) {
|
|
142
|
-
return path.join(await fs.realpath(localGitDirectory), snapshotFileName(head));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// A subdirectory or linked-worktree .git file still needs Git's common-dir witness.
|
|
147
|
-
}
|
|
148
|
-
const result = await runGitProcess(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir"], { signal });
|
|
149
|
-
if (result.error?.name === "AbortError")
|
|
150
|
-
throw result.error;
|
|
151
|
-
const commonDir = result.stdout.trim();
|
|
152
|
-
if (result.status !== 0 || result.error || !path.isAbsolute(commonDir)) {
|
|
153
|
-
throw new Error(`cannot resolve repository common directory: ${result.stderr.trim() || result.error?.message || `exit ${String(result.status)}`}`);
|
|
154
|
-
}
|
|
155
|
-
return path.join(commonDir, snapshotFileName(head));
|
|
156
|
-
}
|
|
157
|
-
function classifySnapshotFailure(error) {
|
|
158
|
-
const code = error.code;
|
|
159
|
-
const message = error instanceof Error ? error.message.toLowerCase() : "";
|
|
160
|
-
if (code === "ENOENT" || message.includes("no such file or directory"))
|
|
161
|
-
return "missing";
|
|
162
|
-
if (code === "ERR_SQLITE_BUSY" || code === "ERR_SQLITE_LOCKED" || message.includes("database is locked") || message.includes("database is busy"))
|
|
163
|
-
return "transient";
|
|
164
|
-
if (code === "ERR_SQLITE_CORRUPT" || code === "ERR_SQLITE_NOTADB" || message.includes("database disk image is malformed") || message.includes("file is not a database"))
|
|
165
|
-
return "corrupt";
|
|
166
|
-
return "validation-failure";
|
|
167
|
-
}
|
|
168
|
-
function isCantOpen(error) {
|
|
169
|
-
const code = error.code;
|
|
170
|
-
const message = error instanceof Error ? error.message.toLowerCase() : "";
|
|
171
|
-
return code === "ERR_SQLITE_CANTOPEN" || message.includes("unable to open database file");
|
|
172
|
-
}
|
|
173
|
-
async function snapshotExists(databasePath) {
|
|
174
|
-
try {
|
|
175
|
-
await fs.lstat(databasePath);
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
if (error.code === "ENOENT")
|
|
180
|
-
return false;
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
async function openValidated(databasePath) {
|
|
185
|
-
const existedBeforeOpen = await snapshotExists(databasePath);
|
|
186
|
-
let database;
|
|
187
|
-
try {
|
|
188
|
-
database = new DatabaseSync(databasePath, { readOnly: true });
|
|
189
|
-
database.exec(`PRAGMA busy_timeout = ${SNAPSHOT_READER_BUSY_MS}; PRAGMA query_only = ON`);
|
|
190
|
-
const rows = database.prepare("SELECT version FROM snapshot_schema WHERE singleton = 1").all();
|
|
191
|
-
if (rows.length !== 1 || rows[0]?.version !== REPOSITORY_CURRENT_STATE_VERSION) {
|
|
192
|
-
database.close();
|
|
193
|
-
return { kind: "validation-failure" };
|
|
194
|
-
}
|
|
195
|
-
return { kind: "opened", database };
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
try {
|
|
199
|
-
database.close();
|
|
200
|
-
}
|
|
201
|
-
catch { /* open did not yield a usable connection */ }
|
|
202
|
-
if (isCantOpen(error)) {
|
|
203
|
-
const existsAfterFailure = await snapshotExists(databasePath);
|
|
204
|
-
if (existedBeforeOpen === false || existsAfterFailure === false)
|
|
205
|
-
return { kind: "missing", error };
|
|
206
|
-
}
|
|
207
|
-
return { kind: classifySnapshotFailure(error), error };
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
async function readSnapshot(databasePath, read) {
|
|
211
|
-
const opened = await openValidated(databasePath);
|
|
212
|
-
if (opened.kind !== "opened")
|
|
213
|
-
return opened;
|
|
214
|
-
try {
|
|
215
|
-
return { kind: "read", value: read(opened.database) };
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
return { kind: classifySnapshotFailure(error), error };
|
|
219
|
-
}
|
|
220
|
-
finally {
|
|
221
|
-
try {
|
|
222
|
-
opened.database.close();
|
|
223
|
-
}
|
|
224
|
-
catch { /* validation failure already has its outcome */ }
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
export function currentStateWriterCoordination(databasePath) {
|
|
228
|
-
const lockPath = path.join(path.dirname(databasePath), SNAPSHOT_WRITER_LOCK_FILE);
|
|
229
|
-
return {
|
|
230
|
-
path: lockPath,
|
|
231
|
-
async acquire(signal) {
|
|
232
|
-
const lease = await acquireSqliteProcessLifetimeLock({ path: lockPath, signal });
|
|
233
|
-
return { release: () => lease.close() };
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
async function withSnapshotWriter(databasePath, signal, operation) {
|
|
238
|
-
const lock = await currentStateWriterCoordination(databasePath).acquire(signal);
|
|
239
|
-
try {
|
|
240
|
-
return await operation();
|
|
241
|
-
}
|
|
242
|
-
finally {
|
|
243
|
-
lock.release();
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function temporarySnapshotPath(databasePath) {
|
|
247
|
-
return `${databasePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
248
|
-
}
|
|
249
|
-
async function discardTemporarySnapshot(databasePath) {
|
|
250
|
-
await fs.unlink(databasePath).catch((error) => {
|
|
251
|
-
if (error.code !== "ENOENT")
|
|
252
|
-
throw error;
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
/** Writers alone publish or retire cache bytes; readers have no filesystem mutation path. */
|
|
256
|
-
async function publishImmutableSnapshot(buildPath, databasePath) {
|
|
257
|
-
if (await snapshotExists(databasePath))
|
|
258
|
-
return false;
|
|
259
|
-
await fs.rename(buildPath, databasePath);
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* A filename becomes published only after its complete signed contents validate.
|
|
264
|
-
* This runs under writer coordination; readers merely report an unusable cache.
|
|
265
|
-
*/
|
|
266
|
-
async function preparePublicationTarget(databasePath, expectedHead) {
|
|
267
|
-
const snapshot = await readSnapshot(databasePath, (database) => {
|
|
268
|
-
const pin = metadata(database);
|
|
269
|
-
if (!pin || pin.head !== expectedHead)
|
|
270
|
-
throw new Error("snapshot pin does not match its immutable filename");
|
|
271
|
-
validatePinReceipt(database, pin);
|
|
272
|
-
const rows = database.prepare(`
|
|
273
|
-
SELECT contract, status, current_json AS currentJson, fold_json AS foldJson, diagnostics_json AS diagnosticsJson
|
|
274
|
-
FROM snapshot_contracts ORDER BY contract
|
|
275
|
-
`).all();
|
|
276
|
-
const repository = database.prepare("SELECT diagnostics_json AS diagnosticsJson FROM snapshot_repository WHERE singleton = 1").get();
|
|
277
|
-
if (!repository)
|
|
278
|
-
throw new Error("snapshot repository row is missing");
|
|
279
|
-
validateRepositoryReceipts(database, rows.map((row) => row.contract), false, true);
|
|
280
|
-
parseDiagnostics(repository.diagnosticsJson);
|
|
281
|
-
for (const row of rows) {
|
|
282
|
-
parseDiagnostics(row.diagnosticsJson, row.contract);
|
|
283
|
-
validateContractConsistency(database, row, pin);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
if (snapshot.kind === "read")
|
|
287
|
-
return "published";
|
|
288
|
-
if (snapshot.kind === "missing")
|
|
289
|
-
return "missing";
|
|
290
|
-
if (snapshot.kind === "transient")
|
|
291
|
-
return "transient";
|
|
292
|
-
await fs.unlink(databasePath).catch((error) => {
|
|
293
|
-
if (error.code !== "ENOENT")
|
|
294
|
-
throw error;
|
|
295
|
-
});
|
|
296
|
-
return "missing";
|
|
297
|
-
}
|
|
298
|
-
async function garbageCollectSnapshots(databasePath) {
|
|
299
|
-
const directory = path.dirname(databasePath);
|
|
300
|
-
const retained = path.basename(databasePath);
|
|
301
|
-
let entries;
|
|
302
|
-
try {
|
|
303
|
-
entries = await fs.readdir(directory);
|
|
304
|
-
}
|
|
305
|
-
catch {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
const cacheFile = /^keiyaku-current-state-(?:[0-9a-f]{40}|[0-9a-f]{64}|empty)\.sqlite(?:\..+\.tmp)?$/;
|
|
309
|
-
await Promise.all(entries
|
|
310
|
-
.filter((entry) => entry !== retained && cacheFile.test(entry))
|
|
311
|
-
.map((entry) => fs.unlink(path.join(directory, entry)).catch(() => undefined)));
|
|
312
|
-
}
|
|
313
|
-
function parseJson(text) {
|
|
314
|
-
return JSON.parse(text);
|
|
315
|
-
}
|
|
316
|
-
function parseDiagnostics(text, contract) {
|
|
317
|
-
const value = JSON.parse(text);
|
|
318
|
-
if (!Array.isArray(value) || value.some((item) => !isRecord(item)
|
|
319
|
-
|| typeof item.objectId !== "string" || typeof item.code !== "string" || typeof item.detail !== "string"
|
|
320
|
-
|| (contract !== undefined && item.contract !== contract))) {
|
|
321
|
-
throw new Error("snapshot diagnostics are invalid or disagree with their owner");
|
|
322
|
-
}
|
|
323
|
-
return value;
|
|
324
|
-
}
|
|
325
|
-
function isRecord(value) {
|
|
326
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
327
|
-
}
|
|
328
|
-
function assertClosedKeys(value, keys, label) {
|
|
329
|
-
const actual = Object.keys(value).sort();
|
|
330
|
-
const expected = [...keys].sort();
|
|
331
|
-
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
332
|
-
throw new Error(`${label} has an unknown or missing field`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function parseCurrent(text, contract) {
|
|
336
|
-
const value = JSON.parse(text);
|
|
337
|
-
if (!isRecord(value))
|
|
338
|
-
throw new Error("snapshot current facts are not an object");
|
|
339
|
-
assertClosedKeys(value, ["contractHead", "contract", "phase", "intent", "receiptSegments", "seal", "bootstrapReplay", "petition", "verdict", "pins", "effectiveScope", "causal", "resources"], "snapshot current facts");
|
|
340
|
-
if (value.contract !== contract || typeof value.contractHead !== "string" || !isRecord(value.resources)) {
|
|
341
|
-
throw new Error("snapshot current facts disagree with their row key");
|
|
342
|
-
}
|
|
343
|
-
return value;
|
|
344
|
-
}
|
|
345
|
-
function readHistory(database, contract, summaryOnly = false, objectFormat = "sha1") {
|
|
346
|
-
if (summaryOnly) {
|
|
347
|
-
const row = database.prepare("SELECT history_json AS historyJson FROM snapshot_summaries WHERE contract = ?").get(contract);
|
|
348
|
-
if (!row)
|
|
349
|
-
throw new Error("snapshot current summary is missing");
|
|
350
|
-
const value = JSON.parse(row.historyJson);
|
|
351
|
-
if (!Array.isArray(value) || value.some((item) => !isRecord(item) || typeof item.objectId !== "string" || !createLifecycleEntrySchema(objectFormat).safeParse(item.entry).success || item.entry.contract !== contract)) {
|
|
352
|
-
throw new Error("snapshot current summary is invalid");
|
|
353
|
-
}
|
|
354
|
-
return value;
|
|
355
|
-
}
|
|
356
|
-
const rows = database.prepare("SELECT position, object_id AS objectId, entry_json AS entryJson FROM snapshot_history WHERE contract = ? ORDER BY position").all(contract);
|
|
357
|
-
if (!summaryOnly && rows.some((row, index) => row.position !== index))
|
|
358
|
-
throw new Error("snapshot lifecycle history positions are not contiguous");
|
|
359
|
-
const history = rows.map((row) => {
|
|
360
|
-
const parsed = createLifecycleEntrySchema(objectFormat).safeParse(JSON.parse(row.entryJson));
|
|
361
|
-
if (!parsed.success || parsed.data.contract !== contract)
|
|
362
|
-
throw new Error("snapshot lifecycle history row is invalid");
|
|
363
|
-
return { objectId: row.objectId, entry: parsed.data };
|
|
364
|
-
});
|
|
365
|
-
return history;
|
|
366
|
-
}
|
|
367
|
-
function summaryHistory(history) {
|
|
368
|
-
return history.filter(({ entry }, index) => entry.kind === "bind" || entry.kind === "petition" || index === history.length - 1)
|
|
369
|
-
.filter((row, index, rows) => rows.findIndex((candidate) => candidate.objectId === row.objectId) === index);
|
|
370
|
-
}
|
|
371
|
-
function currentModel(database, contract, currentJson, summaryOnly = false, objectFormat = "sha1") {
|
|
372
|
-
return { ...parseCurrent(currentJson, contract), history: readHistory(database, contract, summaryOnly, objectFormat) };
|
|
373
|
-
}
|
|
374
|
-
function validateContractConsistency(database, input, pin, summaryOnly = false) {
|
|
375
|
-
if ((input.currentJson === null) !== (input.foldJson === null))
|
|
376
|
-
throw new Error("snapshot current/fold presence disagrees");
|
|
377
|
-
if (input.status !== "isolated" && input.currentJson === null)
|
|
378
|
-
throw new Error("non-isolated snapshot row has no current facts");
|
|
379
|
-
if (input.currentJson === null) {
|
|
380
|
-
if (readHistory(database, input.contract).length !== 0)
|
|
381
|
-
throw new Error("isolated snapshot row retains lifecycle history");
|
|
382
|
-
return null;
|
|
383
|
-
}
|
|
384
|
-
if (!pin.objectFormat)
|
|
385
|
-
throw new Error("snapshot contract row has no object format");
|
|
386
|
-
const model = currentModel(database, input.contract, input.currentJson, summaryOnly, pin.objectFormat);
|
|
387
|
-
const diagnosticRow = database.prepare("SELECT diagnostics_json AS diagnosticsJson FROM snapshot_contracts WHERE contract = ?").get(input.contract);
|
|
388
|
-
const diagnostics = parseDiagnostics(diagnosticRow.diagnosticsJson, input.contract);
|
|
389
|
-
if ((input.status === "healthy") !== (diagnostics.length === 0))
|
|
390
|
-
throw new Error("snapshot status disagrees with diagnostics");
|
|
391
|
-
if (summaryOnly)
|
|
392
|
-
return model;
|
|
393
|
-
const fold = JSON.parse(input.foldJson);
|
|
394
|
-
if (!isRecord(fold) || fold.contract !== input.contract || !Array.isArray(fold.entryObjectIds))
|
|
395
|
-
throw new Error("snapshot fold is invalid");
|
|
396
|
-
const objectIds = model.history.map((row) => row.objectId);
|
|
397
|
-
if (fold.entryObjectIds.length !== objectIds.length || fold.entryObjectIds.some((oid, index) => oid !== objectIds[index])) {
|
|
398
|
-
throw new Error("snapshot fold and lifecycle history disagree");
|
|
399
|
-
}
|
|
400
|
-
if (model.contractHead !== objectIds.at(-1) || model.phase !== fold.phase || model.pins.base !== fold.bind?.data?.base) {
|
|
401
|
-
throw new Error("snapshot current facts disagree with fold or pin");
|
|
402
|
-
}
|
|
403
|
-
const typedFold = fold;
|
|
404
|
-
const expectedWorktree = typedFold.bind.data.workspace === "worktree"
|
|
405
|
-
? { place: typedFold.bind.data.place, state: typedFold.terminalObjectId === null ? "held" : "released" }
|
|
406
|
-
: null;
|
|
407
|
-
const expectedTasks = (typedFold.bind.data.taskIds ?? []).map((taskId) => ({ taskId, state: typedFold.terminal?.kind === "forfeit" ? "released" : "held" }));
|
|
408
|
-
const petitionRow = [...model.history].reverse().find(({ entry }) => entry.kind === "petition");
|
|
409
|
-
const verdictRow = [...model.history].reverse().find(({ entry }) => entry.kind === "verdict");
|
|
410
|
-
const arcRow = [...model.history].reverse().find(({ entry }) => entry.kind === "arc");
|
|
411
|
-
const expectedIntent = arcRow?.entry.kind === "arc"
|
|
412
|
-
? { arc: arcRow.entry.data.arc, openedBy: arcRow.objectId, title: arcRow.entry.data.title, objective: arcRow.entry.data.objective, brief: arcRow.entry.data.brief }
|
|
413
|
-
: { arc: 0, openedBy: typedFold.entryObjectIds[0], title: typedFold.bind.data.title, objective: typedFold.bind.data.objective, brief: null };
|
|
414
|
-
const expectedPetition = typedFold.terminalObjectId !== null || !typedFold.petition || !petitionRow || petitionRow.entry.kind !== "petition"
|
|
415
|
-
? { state: "none" }
|
|
416
|
-
: {
|
|
417
|
-
state: typedFold.phase === "approved" && typedFold.verdict ? "approved" : "awaiting-verdict",
|
|
418
|
-
objectId: petitionRow.objectId,
|
|
419
|
-
intent: typedFold.petition.data.intent,
|
|
420
|
-
seat: typedFold.petition.data.seat,
|
|
421
|
-
target: typedFold.petition.data.target,
|
|
422
|
-
targetHead: typedFold.petition.data.intent === "claim" ? typedFold.petition.data.targetHead : null,
|
|
423
|
-
...(typedFold.phase === "approved" && typedFold.verdict && verdictRow ? { verdictObjectId: verdictRow.objectId, summary: typedFold.verdict.data.summary } : {}),
|
|
424
|
-
};
|
|
425
|
-
const expectedVerdict = verdictRow?.entry.kind === "verdict"
|
|
426
|
-
? { objectId: verdictRow.objectId, result: verdictRow.entry.data.result, summary: verdictRow.entry.data.summary }
|
|
427
|
-
: null;
|
|
428
|
-
if (model.pins.delivery !== typedFold.deliveryHead
|
|
429
|
-
|| model.pins.target.ref !== typedFold.target
|
|
430
|
-
|| model.pins.target.approvedHead !== (typedFold.approvedTargetSnapshot?.targetHead ?? null)
|
|
431
|
-
|| JSON.stringify(model.effectiveScope) !== JSON.stringify(typedFold.effectiveScope)
|
|
432
|
-
|| JSON.stringify(model.receiptSegments) !== JSON.stringify(typedFold.segments)
|
|
433
|
-
|| JSON.stringify(model.seal) !== JSON.stringify(typedFold.segments.at(-1)?.seal ?? null)
|
|
434
|
-
|| JSON.stringify(model.intent) !== JSON.stringify(expectedIntent)
|
|
435
|
-
|| JSON.stringify(model.petition) !== JSON.stringify(expectedPetition)
|
|
436
|
-
|| JSON.stringify(model.verdict) !== JSON.stringify(expectedVerdict)
|
|
437
|
-
|| JSON.stringify(model.causal.after) !== JSON.stringify(typedFold.bind.data.after ?? [])
|
|
438
|
-
|| model.resources.deliveryRef !== repositoryDeliveryRef(input.contract)
|
|
439
|
-
|| JSON.stringify(model.resources.worktree) !== JSON.stringify(expectedWorktree)
|
|
440
|
-
|| JSON.stringify(model.resources.tasks) !== JSON.stringify(expectedTasks)) {
|
|
441
|
-
throw new Error("snapshot nested current facts disagree with the accepted fold");
|
|
442
|
-
}
|
|
443
|
-
const actualResources = database.prepare("SELECT kind, resource_key AS resourceKey FROM snapshot_resources WHERE contract = ? ORDER BY kind, resource_key").all(input.contract);
|
|
444
|
-
const expectedResources = [
|
|
445
|
-
...(model.resources.worktree?.state === "held" ? [{ kind: "worktree", resourceKey: model.resources.worktree.place }] : []),
|
|
446
|
-
...model.resources.tasks.filter((task) => task.state === "held").map((task) => ({ kind: "task", resourceKey: task.taskId })),
|
|
447
|
-
].sort((left, right) => left.kind.localeCompare(right.kind) || left.resourceKey.localeCompare(right.resourceKey));
|
|
448
|
-
if (JSON.stringify(actualResources) !== JSON.stringify(expectedResources))
|
|
449
|
-
throw new Error("snapshot resource rows disagree with current facts");
|
|
450
|
-
return model;
|
|
451
|
-
}
|
|
452
|
-
function metadata(database) {
|
|
453
|
-
const rows = database.prepare("SELECT ledger_head AS head, object_format AS objectFormat, empty_tree AS emptyTree FROM snapshot_pin WHERE singleton = 1").all();
|
|
454
|
-
return rows.length === 1 ? rows[0] : null;
|
|
455
|
-
}
|
|
456
|
-
function storedContract(database, contract) {
|
|
457
|
-
const row = database.prepare(`
|
|
458
|
-
SELECT contract, status, current_json AS currentJson, fold_json AS foldJson, diagnostics_json AS diagnosticsJson
|
|
459
|
-
FROM snapshot_contracts WHERE contract = ?
|
|
460
|
-
`).get(contract);
|
|
461
|
-
if (!row)
|
|
462
|
-
return null;
|
|
463
|
-
const pin = metadata(database);
|
|
464
|
-
if (!pin)
|
|
465
|
-
throw new Error("snapshot pin is missing");
|
|
466
|
-
return {
|
|
467
|
-
contract: row.contract,
|
|
468
|
-
status: row.status,
|
|
469
|
-
model: validateContractConsistency(database, row, pin),
|
|
470
|
-
diagnostics: parseDiagnostics(row.diagnosticsJson, contract),
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
function atPin(model, _head) {
|
|
474
|
-
return model;
|
|
475
|
-
}
|
|
476
|
-
export async function readPinnedCurrentContract(cwd, observedHead, contract, signal) {
|
|
477
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, signal);
|
|
478
|
-
const snapshot = await readSnapshot(databasePath, (database) => {
|
|
479
|
-
const pin = metadata(database);
|
|
480
|
-
if (!pin || pin.head !== observedHead)
|
|
481
|
-
return null;
|
|
482
|
-
validatePinReceipt(database, pin);
|
|
483
|
-
const row = storedContract(database, contract);
|
|
484
|
-
if (row)
|
|
485
|
-
validateContractReceipt(database, contract, false);
|
|
486
|
-
if (row?.status === "healthy" && row.model)
|
|
487
|
-
return { status: "healthy", model: atPin(row.model, observedHead), diagnostics: [] };
|
|
488
|
-
if (row?.status === "diseased" && row.model)
|
|
489
|
-
return { status: "diseased", model: atPin(row.model, observedHead), diagnostics: row.diagnostics };
|
|
490
|
-
if (row?.status === "isolated")
|
|
491
|
-
return { status: "isolated", contract, diagnostics: row.diagnostics };
|
|
492
|
-
assertReceipt(database, "repository", repositoryDigest(database));
|
|
493
|
-
const repository = database.prepare("SELECT diagnostics_json AS diagnosticsJson FROM snapshot_repository WHERE singleton = 1").get();
|
|
494
|
-
const unattributed = repository ? parseDiagnostics(repository.diagnosticsJson).filter((disease) => disease.contract === undefined) : [];
|
|
495
|
-
return unattributed.length > 0
|
|
496
|
-
? { status: "indeterminate", contract, diagnostics: unattributed }
|
|
497
|
-
: { status: "absent", contract, diagnostics: [] };
|
|
498
|
-
});
|
|
499
|
-
return snapshot.kind === "read" ? snapshot.value : null;
|
|
500
|
-
}
|
|
501
|
-
export async function readRepositoryCurrentStatePin(cwd, observedHead, signal) {
|
|
502
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, signal);
|
|
503
|
-
const snapshot = await readSnapshot(databasePath, (database) => {
|
|
504
|
-
const pin = metadata(database);
|
|
505
|
-
if (!pin)
|
|
506
|
-
return undefined;
|
|
507
|
-
validatePinReceipt(database, pin);
|
|
508
|
-
return pin.head;
|
|
509
|
-
});
|
|
510
|
-
return snapshot.kind === "read" ? snapshot.value : undefined;
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* The first commit parent is the only inexpensive candidate for a linear
|
|
514
|
-
* incremental predecessor. It is not a current pointer: the caller still
|
|
515
|
-
* validates the cache addressed by that exact OID before using it.
|
|
516
|
-
*/
|
|
517
|
-
export async function readRepositoryCurrentStatePredecessor(cwd, observedHead, signal) {
|
|
518
|
-
const result = await runGitProcess(cwd, ["show", "-s", "--format=%P", observedHead], { signal });
|
|
519
|
-
if (result.error?.name === "AbortError")
|
|
520
|
-
throw result.error;
|
|
521
|
-
if (result.status !== 0 || result.error)
|
|
522
|
-
return null;
|
|
523
|
-
return result.stdout.trim().split(/\s+/)[0] || null;
|
|
524
|
-
}
|
|
525
|
-
export async function readRepositoryCurrentStateNodeIds(cwd, observedHead, signal) {
|
|
526
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, signal);
|
|
527
|
-
const snapshot = await readSnapshot(databasePath, (database) => {
|
|
528
|
-
const pin = metadata(database);
|
|
529
|
-
if (!pin || pin.head !== observedHead)
|
|
530
|
-
return null;
|
|
531
|
-
validatePinReceipt(database, pin);
|
|
532
|
-
assertReceipt(database, "topology", topologyDigest(database));
|
|
533
|
-
const rows = database.prepare("SELECT object_id AS objectId, node_json AS nodeJson FROM snapshot_topology").all();
|
|
534
|
-
const ledgerParents = new Map();
|
|
535
|
-
const entryIntroductions = new Map();
|
|
536
|
-
for (const row of rows) {
|
|
537
|
-
const node = JSON.parse(row.nodeJson);
|
|
538
|
-
if (node.objectId !== row.objectId || !Array.isArray(node.ledgerParents) || node.ledgerParents.some((parent) => typeof parent !== "string"))
|
|
539
|
-
throw new Error("snapshot topology row is invalid");
|
|
540
|
-
ledgerParents.set(row.objectId, node.ledgerParents);
|
|
541
|
-
if (node.type === "transaction") {
|
|
542
|
-
if (!Array.isArray(node.entryObjectIds) || node.entryObjectIds.some((entry) => typeof entry !== "string"))
|
|
543
|
-
throw new Error("snapshot transaction topology is invalid");
|
|
544
|
-
for (const entry of node.entryObjectIds) {
|
|
545
|
-
if (entryIntroductions.has(entry))
|
|
546
|
-
throw new Error("snapshot topology repeats an entry introduction");
|
|
547
|
-
entryIntroductions.set(entry, row.objectId);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return { ledgerParents, entryIntroductions };
|
|
552
|
-
});
|
|
553
|
-
return snapshot.kind === "read" ? snapshot.value : null;
|
|
554
|
-
}
|
|
555
|
-
/** Rehydrate the accepted typed topology for publication admission without Git history traversal. */
|
|
556
|
-
export async function readPinnedAcceptedTraversal(cwd, observedHead, signal) {
|
|
557
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, signal);
|
|
558
|
-
const snapshot = await readSnapshot(databasePath, (database) => {
|
|
559
|
-
const pin = metadata(database);
|
|
560
|
-
if (!pin || pin.head !== observedHead || !pin.objectFormat || !pin.emptyTree)
|
|
561
|
-
return null;
|
|
562
|
-
validatePinReceipt(database, pin);
|
|
563
|
-
assertReceipt(database, "repository", repositoryDigest(database));
|
|
564
|
-
assertReceipt(database, "topology", topologyDigest(database));
|
|
565
|
-
assertReceipt(database, "resources", resourcesDigest(database));
|
|
566
|
-
const repository = database.prepare("SELECT diagnostics_json AS diagnosticsJson FROM snapshot_repository WHERE singleton = 1").get();
|
|
567
|
-
const contractRows = database.prepare("SELECT contract, status, diagnostics_json AS diagnosticsJson FROM snapshot_contracts").all();
|
|
568
|
-
validateRepositoryReceipts(database, contractRows.map((row) => row.contract).sort(), false, true);
|
|
569
|
-
if (!repository || parseDiagnostics(repository.diagnosticsJson).length > 0 || contractRows.some((row) => row.status !== "healthy" || parseDiagnostics(row.diagnosticsJson, row.contract).length > 0))
|
|
570
|
-
return null;
|
|
571
|
-
const historyRows = database.prepare("SELECT object_id AS objectId, entry_json AS entryJson FROM snapshot_history").all();
|
|
572
|
-
const entries = new Map(historyRows.map((row) => {
|
|
573
|
-
const parsed = createLifecycleEntrySchema(pin.objectFormat).parse(JSON.parse(row.entryJson));
|
|
574
|
-
return [row.objectId, parsed];
|
|
575
|
-
}));
|
|
576
|
-
const topologyRows = database.prepare("SELECT object_id AS objectId, node_json AS nodeJson FROM snapshot_topology ORDER BY rowid").all();
|
|
577
|
-
const nodes = new Map();
|
|
578
|
-
const order = [];
|
|
579
|
-
for (const row of topologyRows) {
|
|
580
|
-
const node = JSON.parse(row.nodeJson);
|
|
581
|
-
if (node.objectId !== row.objectId || !Array.isArray(node.ledgerParents))
|
|
582
|
-
return null;
|
|
583
|
-
let typed;
|
|
584
|
-
if (node.type === "lifecycle") {
|
|
585
|
-
const entry = entries.get(row.objectId);
|
|
586
|
-
if (!entry || node.ledgerParents.length > 1)
|
|
587
|
-
return null;
|
|
588
|
-
typed = { type: "lifecycle", objectId: row.objectId, entry, ledgerParents: node.ledgerParents, evidenceParent: evidenceObjectId(entry) };
|
|
589
|
-
}
|
|
590
|
-
else if (node.type === "transaction" && node.verb && node.contract && node.entryObjectIds && node.finalEntryObjectId) {
|
|
591
|
-
typed = { type: "transaction", objectId: row.objectId, transaction: { v: 1, kind: "transaction", verb: node.verb, predecessor: node.ledgerParents.length === 0 ? "none" : "p1" }, ledgerParents: node.ledgerParents, entryObjectIds: node.entryObjectIds, contract: node.contract, finalEntryObjectId: node.finalEntryObjectId, evidenceParent: null };
|
|
592
|
-
}
|
|
593
|
-
else if (node.type === "join" && node.ledgerParents.length === 2) {
|
|
594
|
-
typed = { type: "join", objectId: row.objectId, join: { v: 1, kind: "join", at: "1970-01-01T00:00:00.000Z", actor: "snapshot" }, ledgerParents: node.ledgerParents, evidenceParent: null };
|
|
595
|
-
}
|
|
596
|
-
else
|
|
597
|
-
return null;
|
|
598
|
-
nodes.set(row.objectId, typed);
|
|
599
|
-
}
|
|
600
|
-
const visited = new Set();
|
|
601
|
-
const visit = (objectId) => {
|
|
602
|
-
if (visited.has(objectId))
|
|
603
|
-
return;
|
|
604
|
-
visited.add(objectId);
|
|
605
|
-
const node = nodes.get(objectId);
|
|
606
|
-
if (!node)
|
|
607
|
-
return;
|
|
608
|
-
const dependencies = node.type === "transaction" ? [...node.ledgerParents, ...node.entryObjectIds] : [...node.ledgerParents];
|
|
609
|
-
for (const dependency of dependencies)
|
|
610
|
-
visit(dependency);
|
|
611
|
-
order.push(objectId);
|
|
612
|
-
};
|
|
613
|
-
visit(observedHead);
|
|
614
|
-
return { head: observedHead, coordinates: { objectFormat: pin.objectFormat, objectIdLength: pin.objectFormat === "sha1" ? 40 : 64, emptyTree: pin.emptyTree }, nodes, order, diseases: [] };
|
|
615
|
-
});
|
|
616
|
-
return snapshot.kind === "read" ? snapshot.value : null;
|
|
617
|
-
}
|
|
618
|
-
export async function readPinnedRepositoryLifecycle(cwd, observedHead, signal, options = {}) {
|
|
619
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, signal);
|
|
620
|
-
const snapshot = await readSnapshot(databasePath, (database) => {
|
|
621
|
-
const pin = metadata(database);
|
|
622
|
-
if (!pin || pin.head !== observedHead)
|
|
623
|
-
return null;
|
|
624
|
-
const rows = database.prepare(`
|
|
625
|
-
SELECT contract, status, current_json AS currentJson, fold_json AS foldJson, diagnostics_json AS diagnosticsJson
|
|
626
|
-
FROM snapshot_contracts ORDER BY contract
|
|
627
|
-
`).all();
|
|
628
|
-
const repository = database.prepare("SELECT diagnostics_json AS diagnosticsJson FROM snapshot_repository WHERE singleton = 1").get();
|
|
629
|
-
if (!repository)
|
|
630
|
-
throw new Error("snapshot repository row is missing");
|
|
631
|
-
validatePinReceipt(database, pin);
|
|
632
|
-
validateRepositoryReceipts(database, rows.map((row) => row.contract), options.summaryOnly === true, options.includeTopology !== false);
|
|
633
|
-
return {
|
|
634
|
-
observedLedgerHead: pin.head === null ? null : repositoryLedgerHead(pin.head),
|
|
635
|
-
objectFormat: pin.objectFormat,
|
|
636
|
-
emptyTree: pin.emptyTree,
|
|
637
|
-
contracts: rows.flatMap((row) => {
|
|
638
|
-
const model = validateContractConsistency(database, row, pin, options.summaryOnly);
|
|
639
|
-
if (!model)
|
|
640
|
-
return [];
|
|
641
|
-
return [atPin(model, pin.head)];
|
|
642
|
-
}),
|
|
643
|
-
isolatedContracts: rows.filter((row) => row.status === "isolated").map((row) => row.contract),
|
|
644
|
-
diagnostics: [
|
|
645
|
-
...parseDiagnostics(repository.diagnosticsJson),
|
|
646
|
-
...rows.flatMap((row) => parseDiagnostics(row.diagnosticsJson, row.contract)),
|
|
647
|
-
],
|
|
648
|
-
topology: {
|
|
649
|
-
head: pin.head,
|
|
650
|
-
nodes: options.includeTopology === false
|
|
651
|
-
? []
|
|
652
|
-
: database.prepare("SELECT node_json AS nodeJson FROM snapshot_topology ORDER BY rowid").all().map((row) => parseJson(row.nodeJson)),
|
|
653
|
-
},
|
|
654
|
-
};
|
|
655
|
-
});
|
|
656
|
-
return snapshot.kind === "read" ? snapshot.value : null;
|
|
657
|
-
}
|
|
658
|
-
export async function publishRepositoryCurrentState(cwd, read, options) {
|
|
659
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, read.observedLedgerHead, options.signal);
|
|
660
|
-
await fs.mkdir(path.dirname(databasePath), { recursive: true });
|
|
661
|
-
if (await options.confirmHead() !== read.observedLedgerHead)
|
|
662
|
-
return false;
|
|
663
|
-
return withSnapshotWriter(databasePath, options.signal, async () => {
|
|
664
|
-
if (await options.confirmHead() !== read.observedLedgerHead)
|
|
665
|
-
return false;
|
|
666
|
-
const existing = await preparePublicationTarget(databasePath, read.observedLedgerHead);
|
|
667
|
-
if (existing === "published")
|
|
668
|
-
return true;
|
|
669
|
-
if (existing === "transient")
|
|
670
|
-
return false;
|
|
671
|
-
const buildPath = temporarySnapshotPath(databasePath);
|
|
672
|
-
let database;
|
|
673
|
-
try {
|
|
674
|
-
await fs.unlink(buildPath).catch((error) => {
|
|
675
|
-
if (error.code !== "ENOENT")
|
|
676
|
-
throw error;
|
|
677
|
-
});
|
|
678
|
-
database = new DatabaseSync(buildPath);
|
|
679
|
-
database.exec("PRAGMA synchronous = FULL; PRAGMA journal_mode = DELETE; BEGIN IMMEDIATE");
|
|
680
|
-
database.exec("DROP TABLE IF EXISTS snapshot_integrity; DROP TABLE IF EXISTS snapshot_resources; DROP TABLE IF EXISTS snapshot_topology; DROP TABLE IF EXISTS snapshot_summaries; DROP TABLE IF EXISTS snapshot_history; DROP TABLE IF EXISTS snapshot_contracts; DROP TABLE IF EXISTS snapshot_repository; DROP TABLE IF EXISTS snapshot_pin; DROP TABLE IF EXISTS snapshot_schema");
|
|
681
|
-
database.exec(SCHEMA);
|
|
682
|
-
database.prepare("INSERT INTO snapshot_schema(singleton, version) VALUES (1, ?)").run(REPOSITORY_CURRENT_STATE_VERSION);
|
|
683
|
-
database.prepare("INSERT INTO snapshot_pin(singleton, ledger_head, object_format, empty_tree) VALUES (1, ?, ?, ?)").run(read.observedLedgerHead, read.objectFormat, read.emptyTree);
|
|
684
|
-
const insert = database.prepare("INSERT INTO snapshot_contracts(contract, status, current_json, fold_json, diagnostics_json) VALUES (?, ?, ?, ?, ?)");
|
|
685
|
-
const insertHistory = database.prepare("INSERT INTO snapshot_history(contract, position, object_id, entry_json) VALUES (?, ?, ?, ?)");
|
|
686
|
-
const insertSummary = database.prepare("INSERT INTO snapshot_summaries(contract, history_json) VALUES (?, ?)");
|
|
687
|
-
const isolated = new Set(read.isolatedContracts);
|
|
688
|
-
const models = new Map(read.contracts.map((model) => [model.contract, model]));
|
|
689
|
-
for (const contract of new Set([...models.keys(), ...isolated])) {
|
|
690
|
-
const model = models.get(contract) ?? null;
|
|
691
|
-
const diagnostics = read.diagnostics.filter((disease) => disease.contract === contract);
|
|
692
|
-
const status = isolated.has(contract) ? "isolated" : diagnostics.length > 0 ? "diseased" : "healthy";
|
|
693
|
-
const folded = options.fold.contracts.get(contract) ?? null;
|
|
694
|
-
const current = model === null ? null : Object.fromEntries(Object.entries(model).filter(([key]) => key !== "history"));
|
|
695
|
-
insert.run(contract, status, current === null ? null : JSON.stringify(current), folded === null ? null : JSON.stringify(folded), JSON.stringify(diagnostics));
|
|
696
|
-
model?.history.forEach((row, position) => insertHistory.run(contract, position, row.objectId, JSON.stringify(row.entry)));
|
|
697
|
-
if (model)
|
|
698
|
-
insertSummary.run(contract, JSON.stringify(summaryHistory(model.history)));
|
|
699
|
-
}
|
|
700
|
-
database.prepare("INSERT INTO snapshot_repository(singleton, diagnostics_json) VALUES (1, ?)").run(JSON.stringify(read.diagnostics.filter((disease) => disease.contract === undefined)));
|
|
701
|
-
const insertTopology = database.prepare("INSERT INTO snapshot_topology(object_id, node_json) VALUES (?, ?)");
|
|
702
|
-
for (const node of read.topology.nodes)
|
|
703
|
-
insertTopology.run(node.objectId, JSON.stringify(node));
|
|
704
|
-
const insertResource = database.prepare("INSERT INTO snapshot_resources(kind, resource_key, contract) VALUES (?, ?, ?)");
|
|
705
|
-
for (const model of read.contracts) {
|
|
706
|
-
if (model.resources.worktree?.state === "held")
|
|
707
|
-
insertResource.run("worktree", model.resources.worktree.place, model.contract);
|
|
708
|
-
for (const task of model.resources.tasks)
|
|
709
|
-
if (task.state === "held")
|
|
710
|
-
insertResource.run("task", task.taskId, model.contract);
|
|
711
|
-
}
|
|
712
|
-
writeIntegrityReceipts(database);
|
|
713
|
-
await options.beforeCommit?.();
|
|
714
|
-
database.exec("COMMIT");
|
|
715
|
-
database.close();
|
|
716
|
-
// No reader can be holding this new name. An existing same-head file is
|
|
717
|
-
// convergence, never a target for replacement.
|
|
718
|
-
await publishImmutableSnapshot(buildPath, databasePath);
|
|
719
|
-
await garbageCollectSnapshots(databasePath);
|
|
720
|
-
return true;
|
|
721
|
-
}
|
|
722
|
-
catch (error) {
|
|
723
|
-
try {
|
|
724
|
-
database.exec("ROLLBACK");
|
|
725
|
-
}
|
|
726
|
-
catch { /* database may not have opened */ }
|
|
727
|
-
try {
|
|
728
|
-
database.close();
|
|
729
|
-
}
|
|
730
|
-
catch { /* database may not have opened */ }
|
|
731
|
-
await discardTemporarySnapshot(buildPath);
|
|
732
|
-
throw error;
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
export async function advanceRepositoryCurrentState(cwd, pinnedHead, observedHead, segment, options) {
|
|
737
|
-
const pinnedDatabasePath = await repositoryCurrentStateSnapshotPath(cwd, pinnedHead, options.signal);
|
|
738
|
-
const databasePath = await repositoryCurrentStateSnapshotPath(cwd, observedHead, options.signal);
|
|
739
|
-
if (await options.confirmHead() !== observedHead)
|
|
740
|
-
return false;
|
|
741
|
-
return withSnapshotWriter(databasePath, options.signal, async () => {
|
|
742
|
-
if (await options.confirmHead() !== observedHead)
|
|
743
|
-
return false;
|
|
744
|
-
const existing = await preparePublicationTarget(databasePath, observedHead);
|
|
745
|
-
if (existing === "published")
|
|
746
|
-
return true;
|
|
747
|
-
if (existing === "transient")
|
|
748
|
-
return false;
|
|
749
|
-
const buildPath = temporarySnapshotPath(databasePath);
|
|
750
|
-
let database;
|
|
751
|
-
try {
|
|
752
|
-
await fs.copyFile(pinnedDatabasePath, buildPath);
|
|
753
|
-
database = new DatabaseSync(buildPath);
|
|
754
|
-
database.exec("PRAGMA synchronous = FULL; BEGIN IMMEDIATE");
|
|
755
|
-
const versions = database.prepare("SELECT version FROM snapshot_schema WHERE singleton = 1").all();
|
|
756
|
-
if (versions.length !== 1 || versions[0]?.version !== REPOSITORY_CURRENT_STATE_VERSION) {
|
|
757
|
-
database.exec("ROLLBACK");
|
|
758
|
-
database.close();
|
|
759
|
-
return false;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
catch {
|
|
763
|
-
try {
|
|
764
|
-
database.close();
|
|
765
|
-
}
|
|
766
|
-
catch { /* replacement recovery owns unusable bytes */ }
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
769
|
-
try {
|
|
770
|
-
const pin = metadata(database);
|
|
771
|
-
if (!pin || pin.head !== pinnedHead) {
|
|
772
|
-
database.exec("ROLLBACK");
|
|
773
|
-
return false;
|
|
774
|
-
}
|
|
775
|
-
validatePinReceipt(database, pin);
|
|
776
|
-
assertReceipt(database, "topology", topologyDigest(database));
|
|
777
|
-
assertReceipt(database, "resources", resourcesDigest(database));
|
|
778
|
-
for (const contract of new Set(segment.transactions.map((transaction) => transaction.entries[0]?.entry.contract).filter((contract) => contract !== undefined))) {
|
|
779
|
-
if (database.prepare("SELECT 1 FROM snapshot_contracts WHERE contract = ?").get(contract))
|
|
780
|
-
validateContractReceipt(database, contract, false);
|
|
781
|
-
}
|
|
782
|
-
const acceptedTopology = new Set(database.prepare("SELECT object_id AS objectId FROM snapshot_topology").all().map((row) => row.objectId));
|
|
783
|
-
const pendingJoins = new Map(segment.joins.map((join) => [join.objectId, join]));
|
|
784
|
-
const promoteReadyJoins = () => {
|
|
785
|
-
let promoted;
|
|
786
|
-
do {
|
|
787
|
-
promoted = false;
|
|
788
|
-
for (const join of pendingJoins.values()) {
|
|
789
|
-
if (!join.parents.every((parent) => acceptedTopology.has(parent)))
|
|
790
|
-
continue;
|
|
791
|
-
database.prepare("INSERT INTO snapshot_topology(object_id, node_json) VALUES (?, ?)").run(join.objectId, JSON.stringify({
|
|
792
|
-
objectId: join.objectId,
|
|
793
|
-
type: "join",
|
|
794
|
-
ledgerParents: join.parents,
|
|
795
|
-
}));
|
|
796
|
-
acceptedTopology.add(join.objectId);
|
|
797
|
-
pendingJoins.delete(join.objectId);
|
|
798
|
-
promoted = true;
|
|
799
|
-
}
|
|
800
|
-
} while (promoted);
|
|
801
|
-
};
|
|
802
|
-
for (const transaction of segment.transactions) {
|
|
803
|
-
promoteReadyJoins();
|
|
804
|
-
if ((transaction.predecessor !== null && !acceptedTopology.has(transaction.predecessor)) || transaction.entries.length === 0) {
|
|
805
|
-
database.exec("ROLLBACK");
|
|
806
|
-
return false;
|
|
807
|
-
}
|
|
808
|
-
if (transaction.entries.some((entry) => database.prepare("SELECT 1 FROM snapshot_history WHERE object_id = ?").get(entry.objectId) !== undefined)) {
|
|
809
|
-
database.exec("ROLLBACK");
|
|
810
|
-
return false;
|
|
811
|
-
}
|
|
812
|
-
const contract = transaction.entries[0].entry.contract;
|
|
813
|
-
const row = database.prepare("SELECT status, current_json AS currentJson, fold_json AS foldJson, diagnostics_json AS diagnosticsJson FROM snapshot_contracts WHERE contract = ?").get(contract);
|
|
814
|
-
let state = row?.foldJson ? parseJson(row.foldJson) : undefined;
|
|
815
|
-
const priorModel = row?.currentJson ? currentModel(database, contract, row.currentJson) : null;
|
|
816
|
-
const first = transaction.entries[0];
|
|
817
|
-
if (first.entry.kind === "bind" && (state || row)) {
|
|
818
|
-
const diagnostics = [...(row ? parseJson(row.diagnosticsJson) : []), {
|
|
819
|
-
objectId: first.objectId,
|
|
820
|
-
code: "same-contract-divergence",
|
|
821
|
-
contract,
|
|
822
|
-
detail: "incremental bind diverges from an already admitted contract",
|
|
823
|
-
}];
|
|
824
|
-
database.prepare("UPDATE snapshot_contracts SET status = 'isolated', current_json = NULL, fold_json = NULL, diagnostics_json = ? WHERE contract = ?").run(JSON.stringify(diagnostics), contract);
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
if (first.entry.kind !== "bind" && (!state || first.causalParent !== state.entryObjectIds.at(-1))) {
|
|
828
|
-
database.exec("ROLLBACK");
|
|
829
|
-
return false;
|
|
830
|
-
}
|
|
831
|
-
let staged = state === undefined ? undefined : structuredClone(state);
|
|
832
|
-
let disease;
|
|
833
|
-
const basePosition = staged?.entryObjectIds.length ?? 0;
|
|
834
|
-
for (let index = 0; index < transaction.entries.length; index += 1) {
|
|
835
|
-
const entry = transaction.entries[index];
|
|
836
|
-
const applied = applyFoldEntry(staged, { objectId: entry.objectId, entry: entry.entry, position: basePosition + index });
|
|
837
|
-
staged = applied.state;
|
|
838
|
-
if (applied.disease) {
|
|
839
|
-
disease = { ...applied.disease, transactionObjectId: transaction.objectId };
|
|
840
|
-
break;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
if (disease || !staged) {
|
|
844
|
-
const diagnostics = [...(row ? parseJson(row.diagnosticsJson) : []), ...(disease ? [disease] : [])];
|
|
845
|
-
if (row)
|
|
846
|
-
database.prepare("UPDATE snapshot_contracts SET status = 'diseased', diagnostics_json = ? WHERE contract = ?").run(JSON.stringify(diagnostics), contract);
|
|
847
|
-
else
|
|
848
|
-
database.prepare("INSERT INTO snapshot_contracts(contract, status, current_json, fold_json, diagnostics_json) VALUES (?, 'isolated', NULL, NULL, ?)").run(contract, JSON.stringify(diagnostics));
|
|
849
|
-
}
|
|
850
|
-
else {
|
|
851
|
-
if (first.entry.kind === "bind" && (first.entry.data.after?.length ?? 0) > 0) {
|
|
852
|
-
const unavailable = first.entry.data.after.filter((dependency) => {
|
|
853
|
-
const prerequisite = database.prepare("SELECT status, fold_json AS foldJson FROM snapshot_contracts WHERE contract = ?").get(dependency);
|
|
854
|
-
if (!prerequisite || prerequisite.status !== "healthy" || prerequisite.foldJson === null)
|
|
855
|
-
return true;
|
|
856
|
-
return false;
|
|
857
|
-
});
|
|
858
|
-
if (unavailable.length > 0) {
|
|
859
|
-
const diagnostic = {
|
|
860
|
-
objectId: first.objectId,
|
|
861
|
-
code: "causal-prerequisite",
|
|
862
|
-
contract,
|
|
863
|
-
detail: `after prerequisites are not admitted healthy contracts: ${unavailable.join(", ")}`,
|
|
864
|
-
};
|
|
865
|
-
database.prepare(`
|
|
866
|
-
INSERT INTO snapshot_contracts(contract, status, current_json, fold_json, diagnostics_json)
|
|
867
|
-
VALUES (?, 'isolated', NULL, NULL, ?)
|
|
868
|
-
ON CONFLICT(contract) DO UPDATE SET status = 'isolated', current_json = NULL, fold_json = NULL, diagnostics_json = excluded.diagnostics_json
|
|
869
|
-
`).run(contract, JSON.stringify([diagnostic]));
|
|
870
|
-
acceptedTopology.add(transaction.objectId);
|
|
871
|
-
database.prepare("INSERT INTO snapshot_topology(object_id, node_json) VALUES (?, ?)").run(transaction.objectId, JSON.stringify({
|
|
872
|
-
objectId: transaction.objectId,
|
|
873
|
-
type: "transaction",
|
|
874
|
-
ledgerParents: transaction.predecessor === null ? [] : [transaction.predecessor],
|
|
875
|
-
verb: transaction.transaction.verb,
|
|
876
|
-
contract,
|
|
877
|
-
entryObjectIds: transaction.entries.map((entry) => entry.objectId),
|
|
878
|
-
finalEntryObjectId: transaction.entries.at(-1).objectId,
|
|
879
|
-
}));
|
|
880
|
-
continue;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
const history = [...(priorModel?.history ?? []), ...transaction.entries.map((entry) => ({ objectId: entry.objectId, entry: entry.entry }))];
|
|
884
|
-
const model = await options.materialize(staged, history);
|
|
885
|
-
database.prepare(`
|
|
886
|
-
INSERT INTO snapshot_contracts(contract, status, current_json, fold_json, diagnostics_json)
|
|
887
|
-
VALUES (?, 'healthy', ?, ?, '[]')
|
|
888
|
-
ON CONFLICT(contract) DO UPDATE SET status = 'healthy', current_json = excluded.current_json, fold_json = excluded.fold_json, diagnostics_json = '[]'
|
|
889
|
-
`).run(contract, JSON.stringify(Object.fromEntries(Object.entries(model).filter(([key]) => key !== "history"))), JSON.stringify(staged));
|
|
890
|
-
database.prepare("DELETE FROM snapshot_history WHERE contract = ?").run(contract);
|
|
891
|
-
const insertHistory = database.prepare("INSERT INTO snapshot_history(contract, position, object_id, entry_json) VALUES (?, ?, ?, ?)");
|
|
892
|
-
model.history.forEach((historyRow, position) => insertHistory.run(contract, position, historyRow.objectId, JSON.stringify(historyRow.entry)));
|
|
893
|
-
database.prepare("INSERT INTO snapshot_summaries(contract, history_json) VALUES (?, ?) ON CONFLICT(contract) DO UPDATE SET history_json = excluded.history_json").run(contract, JSON.stringify(summaryHistory(model.history)));
|
|
894
|
-
database.prepare("DELETE FROM snapshot_resources WHERE contract = ?").run(contract);
|
|
895
|
-
const insertResource = database.prepare("INSERT INTO snapshot_resources(kind, resource_key, contract) VALUES (?, ?, ?)");
|
|
896
|
-
if (model.resources.worktree?.state === "held")
|
|
897
|
-
insertResource.run("worktree", model.resources.worktree.place, contract);
|
|
898
|
-
for (const task of model.resources.tasks)
|
|
899
|
-
if (task.state === "held")
|
|
900
|
-
insertResource.run("task", task.taskId, contract);
|
|
901
|
-
const collisions = [
|
|
902
|
-
...(model.resources.worktree?.state === "held" ? [{ kind: "worktree", key: model.resources.worktree.place }] : []),
|
|
903
|
-
...model.resources.tasks.filter((task) => task.state === "held").map((task) => ({ kind: "task", key: task.taskId })),
|
|
904
|
-
];
|
|
905
|
-
for (const resource of collisions) {
|
|
906
|
-
const owners = database.prepare("SELECT contract FROM snapshot_resources WHERE kind = ? AND resource_key = ?").all(resource.kind, resource.key);
|
|
907
|
-
if (owners.length < 2)
|
|
908
|
-
continue;
|
|
909
|
-
for (const owner of owners) {
|
|
910
|
-
const ownerRow = database.prepare("SELECT diagnostics_json AS diagnosticsJson FROM snapshot_contracts WHERE contract = ?").get(owner.contract);
|
|
911
|
-
const diagnostic = {
|
|
912
|
-
objectId: first.objectId,
|
|
913
|
-
code: resource.kind === "worktree" ? "worktree-place-collision" : "task-ownership-collision",
|
|
914
|
-
contract: owner.contract,
|
|
915
|
-
detail: `${resource.kind} ${resource.key} is held by ${owners.map((candidate) => candidate.contract).sort().join(", ")}`,
|
|
916
|
-
};
|
|
917
|
-
database.prepare("UPDATE snapshot_contracts SET status = 'isolated', current_json = NULL, fold_json = NULL, diagnostics_json = ? WHERE contract = ?").run(JSON.stringify([...parseJson(ownerRow.diagnosticsJson), diagnostic]), owner.contract);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
const insertLifecycleTopology = database.prepare("INSERT OR IGNORE INTO snapshot_topology(object_id, node_json) VALUES (?, ?)");
|
|
923
|
-
for (const entry of transaction.entries)
|
|
924
|
-
insertLifecycleTopology.run(entry.objectId, JSON.stringify({
|
|
925
|
-
objectId: entry.objectId,
|
|
926
|
-
type: "lifecycle",
|
|
927
|
-
ledgerParents: entry.causalParent === null ? [] : [entry.causalParent],
|
|
928
|
-
}));
|
|
929
|
-
database.prepare("INSERT INTO snapshot_topology(object_id, node_json) VALUES (?, ?)").run(transaction.objectId, JSON.stringify({
|
|
930
|
-
objectId: transaction.objectId,
|
|
931
|
-
type: "transaction",
|
|
932
|
-
ledgerParents: transaction.predecessor === null ? [] : [transaction.predecessor],
|
|
933
|
-
verb: transaction.transaction.verb,
|
|
934
|
-
contract,
|
|
935
|
-
entryObjectIds: transaction.entries.map((entry) => entry.objectId),
|
|
936
|
-
finalEntryObjectId: transaction.entries.at(-1).objectId,
|
|
937
|
-
}));
|
|
938
|
-
acceptedTopology.add(transaction.objectId);
|
|
939
|
-
}
|
|
940
|
-
promoteReadyJoins();
|
|
941
|
-
if (pendingJoins.size > 0 || !acceptedTopology.has(observedHead)) {
|
|
942
|
-
database.exec("ROLLBACK");
|
|
943
|
-
return false;
|
|
944
|
-
}
|
|
945
|
-
database.prepare("UPDATE snapshot_pin SET ledger_head = ? WHERE singleton = 1").run(observedHead);
|
|
946
|
-
writeIntegrityReceipts(database);
|
|
947
|
-
database.exec("COMMIT");
|
|
948
|
-
database.close();
|
|
949
|
-
await publishImmutableSnapshot(buildPath, databasePath);
|
|
950
|
-
await garbageCollectSnapshots(databasePath);
|
|
951
|
-
return true;
|
|
952
|
-
}
|
|
953
|
-
catch {
|
|
954
|
-
try {
|
|
955
|
-
database.exec("ROLLBACK");
|
|
956
|
-
}
|
|
957
|
-
catch { /* handled by recovery */ }
|
|
958
|
-
return false;
|
|
959
|
-
}
|
|
960
|
-
finally {
|
|
961
|
-
try {
|
|
962
|
-
database.close();
|
|
963
|
-
}
|
|
964
|
-
catch { /* database may not have opened */ }
|
|
965
|
-
await discardTemporarySnapshot(buildPath);
|
|
966
|
-
}
|
|
967
|
-
});
|
|
968
|
-
}
|