harnery 0.38.0 → 0.40.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/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +32 -8
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +97 -6
- package/dist/commands/backup.d.ts +16 -0
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +239 -33
- package/dist/commands/browse.d.ts +8 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +466 -231
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +6 -3
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +20 -4
- package/dist/commands/qa-run.d.ts.map +1 -1
- package/dist/commands/qa-run.js +26 -3
- package/dist/commands/qa-status.d.ts.map +1 -1
- package/dist/commands/qa-status.js +7 -2
- package/dist/commands/resources.d.ts.map +1 -1
- package/dist/commands/resources.js +23 -3
- package/dist/commands/review-pack.d.ts.map +1 -1
- package/dist/commands/review-pack.js +98 -23
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +2 -2
- package/dist/core/adapters/profiles.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts +3 -2
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -0
- package/dist/core/agents/cli.js +13 -6
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +5 -3
- package/dist/core/agents/live-lifecycle-v3.d.ts +2 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.d.ts +7 -5
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +63 -8
- package/dist/core/agents/session-finalizer-v3.d.ts +2 -2
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.d.ts +3 -3
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +2 -1
- package/dist/core/agents/state/names.d.ts +35 -2
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +80 -5
- package/dist/core/agents/state/stale-sweep.d.ts +2 -1
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +23 -6
- package/dist/core/agents/turn-elapsed.d.ts +51 -0
- package/dist/core/agents/turn-elapsed.d.ts.map +1 -0
- package/dist/core/agents/turn-elapsed.js +91 -0
- package/dist/core/artifacts/activity.d.ts +14 -0
- package/dist/core/artifacts/activity.d.ts.map +1 -0
- package/dist/core/artifacts/activity.js +52 -0
- package/dist/core/artifacts/delivery-card.d.ts +39 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -0
- package/dist/core/artifacts/delivery-card.js +282 -0
- package/dist/core/artifacts/index.d.ts +42 -7
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +288 -76
- package/dist/core/artifacts/mutation-lock.d.ts +2 -0
- package/dist/core/artifacts/mutation-lock.d.ts.map +1 -0
- package/dist/core/artifacts/mutation-lock.js +91 -0
- package/dist/core/backup/host-snapshot.d.ts +18 -0
- package/dist/core/backup/host-snapshot.d.ts.map +1 -0
- package/dist/core/backup/host-snapshot.js +64 -0
- package/dist/core/config.d.ts +20 -5
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +44 -10
- package/dist/core/diagnostics/advice.d.ts +38 -3
- package/dist/core/diagnostics/advice.d.ts.map +1 -1
- package/dist/core/diagnostics/advice.js +103 -99
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +111 -47
- package/dist/core/diagnostics/comparison.d.ts.map +1 -1
- package/dist/core/diagnostics/comparison.js +3 -2
- package/dist/core/diagnostics/contract.d.ts +15 -31
- package/dist/core/diagnostics/contract.d.ts.map +1 -1
- package/dist/core/diagnostics/contract.js +2 -5
- package/dist/core/diagnostics/index.d.ts +2 -0
- package/dist/core/diagnostics/index.d.ts.map +1 -1
- package/dist/core/diagnostics/index.js +2 -0
- package/dist/core/diagnostics/pressure-contract.d.ts +260 -0
- package/dist/core/diagnostics/pressure-contract.d.ts.map +1 -0
- package/dist/core/diagnostics/pressure-contract.js +70 -0
- package/dist/core/diagnostics/pressure.d.ts +25 -0
- package/dist/core/diagnostics/pressure.d.ts.map +1 -0
- package/dist/core/diagnostics/pressure.js +1138 -0
- package/dist/core/diagnostics/replay.d.ts.map +1 -1
- package/dist/core/diagnostics/replay.js +90 -10
- package/dist/core/events/v3/adapter-id.d.ts +6 -0
- package/dist/core/events/v3/adapter-id.d.ts.map +1 -0
- package/dist/core/events/v3/adapter-id.js +7 -0
- package/dist/core/events/v3/archive-retention.d.ts.map +1 -1
- package/dist/core/events/v3/archive-retention.js +3 -2
- package/dist/core/events/v3/bootstrap.d.ts +41 -4
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +120 -17
- package/dist/core/events/v3/capabilities.d.ts +16 -9
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +37 -2
- package/dist/core/events/v3/capability-drift.d.ts +2 -2
- package/dist/core/events/v3/capability-drift.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.d.ts +48 -5
- package/dist/core/events/v3/control-witness.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.js +91 -36
- package/dist/core/events/v3/control.d.ts.map +1 -1
- package/dist/core/events/v3/control.js +22 -2
- package/dist/core/events/v3/index.d.ts +2 -1
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +2 -1
- package/dist/core/events/v3/live-feed.d.ts +7 -1
- package/dist/core/events/v3/live-feed.d.ts.map +1 -1
- package/dist/core/events/v3/live-feed.js +21 -3
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +10 -2
- package/dist/core/events/v3/producers/command-recorder.d.ts +2 -2
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +2 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +2 -2
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +2 -1
- package/dist/core/events/v3/producers/hook-base.d.ts +2 -2
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +15 -5
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook.js +17 -4
- package/dist/core/events/v3/producers/intake.d.ts +4 -4
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +3 -2
- package/dist/core/events/v3/producers/producer-state-observer.d.ts.map +1 -1
- package/dist/core/events/v3/producers/producer-state-observer.js +2 -1
- package/dist/core/events/v3/producers/recorder.d.ts +16 -5
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +31 -9
- package/dist/core/events/v3/reader.d.ts.map +1 -1
- package/dist/core/events/v3/reader.js +10 -13
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +3 -2
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +2 -2
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/turn-telemetry.d.ts +2 -2
- package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.d.ts +16 -14
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +36 -38
- package/dist/core/hooks/backup-schedule.d.ts +34 -0
- package/dist/core/hooks/backup-schedule.d.ts.map +1 -0
- package/dist/core/hooks/backup-schedule.js +114 -0
- package/dist/core/hooks/cli.js +61 -18
- package/dist/core/hooks/codex-wsl-bridge.d.ts +2 -0
- package/dist/core/hooks/codex-wsl-bridge.d.ts.map +1 -1
- package/dist/core/hooks/codex-wsl-bridge.js +18 -4
- package/dist/core/hooks/index.d.ts +1 -0
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/prompt-context/contract.d.ts +61 -0
- package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -0
- package/dist/core/hooks/prompt-context/contract.js +102 -0
- package/dist/core/hooks/prompt-context/index.d.ts +5 -0
- package/dist/core/hooks/prompt-context/index.d.ts.map +1 -0
- package/dist/core/hooks/prompt-context/index.js +2 -0
- package/dist/core/hooks/prompt-context/runner.d.ts +24 -0
- package/dist/core/hooks/prompt-context/runner.d.ts.map +1 -0
- package/dist/core/hooks/prompt-context/runner.js +249 -0
- package/dist/core/qa-artifacts.d.ts +3 -0
- package/dist/core/qa-artifacts.d.ts.map +1 -1
- package/dist/core/qa-artifacts.js +25 -3
- package/dist/core/resources/contract.d.ts +69 -3
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/contract.js +1 -1
- package/dist/core/resources/index.d.ts +1 -0
- package/dist/core/resources/index.d.ts.map +1 -1
- package/dist/core/resources/index.js +1 -0
- package/dist/core/resources/native.d.ts +36 -0
- package/dist/core/resources/native.d.ts.map +1 -0
- package/dist/core/resources/native.js +418 -0
- package/dist/core/resources/sampler.d.ts +2 -0
- package/dist/core/resources/sampler.d.ts.map +1 -1
- package/dist/core/resources/sampler.js +31 -1
- package/dist/core/resources/status.d.ts +61 -0
- package/dist/core/resources/status.d.ts.map +1 -0
- package/dist/core/resources/status.js +536 -0
- package/dist/core/resources/system.d.ts +27 -0
- package/dist/core/resources/system.d.ts.map +1 -0
- package/dist/core/resources/system.js +439 -0
- package/dist/core/semantic/contract.d.ts +13 -13
- package/dist/core/semantic/service.d.ts +2 -0
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +27 -4
- package/dist/core/storage/builtins.js +41 -0
- package/dist/core/supervisor/contract.d.ts +41 -3
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/contract.js +3 -2
- package/dist/core/supervisor/findings.d.ts +3 -0
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +88 -0
- package/dist/core/supervisor/history.d.ts.map +1 -1
- package/dist/core/supervisor/history.js +14 -0
- package/dist/core/supervisor/ledger-state.d.ts +55 -0
- package/dist/core/supervisor/ledger-state.d.ts.map +1 -0
- package/dist/core/supervisor/ledger-state.js +0 -0
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +120 -7
- package/dist/core/supervisor/storage.d.ts +3 -1
- package/dist/core/supervisor/storage.d.ts.map +1 -1
- package/dist/core/supervisor/storage.js +4 -0
- package/dist/core/workflow/admission.d.ts +3 -0
- package/dist/core/workflow/admission.d.ts.map +1 -1
- package/dist/core/workflow/admission.js +28 -16
- package/dist/core/workflow/engine.js +2 -2
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +6 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +4 -3
- package/dist/lib/browser/capture-fidelity.d.ts +7 -0
- package/dist/lib/browser/capture-fidelity.d.ts.map +1 -1
- package/dist/lib/browser/capture-fidelity.js +17 -0
- package/dist/lib/browser/client.d.ts +9 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +85 -37
- package/dist/lib/browser/index.d.ts +3 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -1
- package/dist/lib/browser/page-review-budget.d.ts +13 -0
- package/dist/lib/browser/page-review-budget.d.ts.map +1 -0
- package/dist/lib/browser/page-review-budget.js +251 -0
- package/dist/lib/browser/page-review-contracts.d.ts +127 -0
- package/dist/lib/browser/page-review-contracts.d.ts.map +1 -0
- package/dist/lib/browser/page-review-contracts.js +5 -0
- package/dist/lib/browser/page-review-evidence.d.ts +48 -0
- package/dist/lib/browser/page-review-evidence.d.ts.map +1 -0
- package/dist/lib/browser/page-review-evidence.js +340 -0
- package/dist/lib/browser/page-review-judge.d.ts +1 -1
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +46 -38
- package/dist/lib/browser/page-review-pack.d.ts +44 -22
- package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
- package/dist/lib/browser/page-review-pack.js +116 -36
- package/dist/lib/browser/page-review-source.d.ts +12 -0
- package/dist/lib/browser/page-review-source.d.ts.map +1 -0
- package/dist/lib/browser/page-review-source.js +22 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +35 -9
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run-contracts.js +48 -14
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +215 -68
- package/dist/lib/browser/qa-snapshot.d.ts +6 -0
- package/dist/lib/browser/qa-snapshot.d.ts.map +1 -1
- package/dist/lib/browser/qa-snapshot.js +259 -48
- package/dist/lib/browser/review-capture-transaction.d.ts +13 -0
- package/dist/lib/browser/review-capture-transaction.d.ts.map +1 -0
- package/dist/lib/browser/review-capture-transaction.js +79 -0
- package/dist/lib/browser/standalone-html.d.ts.map +1 -1
- package/dist/lib/browser/standalone-html.js +4 -2
- package/dist/lib/browser/thumbnail-association.d.ts +34 -0
- package/dist/lib/browser/thumbnail-association.d.ts.map +1 -0
- package/dist/lib/browser/thumbnail-association.js +158 -0
- package/dist/lib/browser/tiling.d.ts +30 -0
- package/dist/lib/browser/tiling.d.ts.map +1 -1
- package/dist/lib/browser/tiling.js +38 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
- package/dist/lib/docs-metadata-managed.js +4 -1
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +7 -1
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +1 -0
- package/dist/lib/local-file-url.d.ts +12 -0
- package/dist/lib/local-file-url.d.ts.map +1 -1
- package/dist/lib/local-file-url.js +16 -2
- package/dist/lib/tunnel/state.d.ts +19 -4
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +53 -13
- package/package.json +7 -4
- package/schemas/config.schema.json +69 -2
- package/src/commands/agents.ts +42 -9
- package/src/commands/artifacts.ts +130 -6
- package/src/commands/backup.ts +287 -34
- package/src/commands/browse.ts +564 -267
- package/src/commands/diagnostics.ts +6 -3
- package/src/commands/ledger-v3.ts +20 -3
- package/src/commands/qa-run.ts +37 -3
- package/src/commands/qa-status.ts +11 -2
- package/src/commands/resources.ts +23 -3
- package/src/commands/review-pack.ts +165 -62
- package/src/commands/tunnel.ts +2 -1
- package/src/core/agents/canonical-emit.ts +4 -2
- package/src/core/agents/cli.ts +14 -6
- package/src/core/agents/live-authority-v3.ts +7 -3
- package/src/core/agents/live-lifecycle-v3.ts +2 -2
- package/src/core/agents/render/prompt-context.ts +68 -14
- package/src/core/agents/session-finalizer-v3.ts +2 -2
- package/src/core/agents/state/live-coordination-view.ts +4 -4
- package/src/core/agents/state/names.ts +92 -7
- package/src/core/agents/state/stale-sweep.ts +22 -8
- package/src/core/agents/turn-elapsed.ts +135 -0
- package/src/core/artifacts/activity.ts +71 -0
- package/src/core/artifacts/delivery-card.ts +375 -0
- package/src/core/artifacts/index.ts +385 -91
- package/src/core/artifacts/mutation-lock.ts +106 -0
- package/src/core/backup/host-snapshot.ts +76 -0
- package/src/core/config.ts +66 -13
- package/src/core/diagnostics/advice.ts +148 -120
- package/src/core/diagnostics/bundle.ts +138 -52
- package/src/core/diagnostics/comparison.ts +6 -2
- package/src/core/diagnostics/contract.ts +39 -42
- package/src/core/diagnostics/index.ts +2 -0
- package/src/core/diagnostics/pressure-contract.ts +294 -0
- package/src/core/diagnostics/pressure.ts +1361 -0
- package/src/core/diagnostics/replay.ts +116 -11
- package/src/core/events/v3/adapter-id.ts +12 -0
- package/src/core/events/v3/archive-retention.ts +3 -6
- package/src/core/events/v3/bootstrap.ts +163 -16
- package/src/core/events/v3/capabilities.ts +75 -25
- package/src/core/events/v3/capability-drift.ts +2 -2
- package/src/core/events/v3/control-witness.ts +172 -41
- package/src/core/events/v3/control.ts +34 -2
- package/src/core/events/v3/index.ts +3 -0
- package/src/core/events/v3/live-feed.ts +23 -5
- package/src/core/events/v3/live-routing.ts +14 -2
- package/src/core/events/v3/producers/command-recorder.ts +4 -4
- package/src/core/events/v3/producers/coordination-recorder.ts +4 -4
- package/src/core/events/v3/producers/hook-base.ts +21 -16
- package/src/core/events/v3/producers/hook.ts +21 -4
- package/src/core/events/v3/producers/intake.ts +6 -6
- package/src/core/events/v3/producers/producer-state-observer.ts +2 -1
- package/src/core/events/v3/producers/recorder.ts +50 -21
- package/src/core/events/v3/reader.ts +14 -13
- package/src/core/events/v3/runtime-build.ts +3 -2
- package/src/core/events/v3/runtime-telemetry-capabilities.ts +3 -3
- package/src/core/events/v3/turn-telemetry.ts +4 -4
- package/src/core/hooks/adapter/output.ts +50 -36
- package/src/core/hooks/backup-schedule.ts +147 -0
- package/src/core/hooks/cli.ts +65 -14
- package/src/core/hooks/codex-wsl-bridge.ts +21 -5
- package/src/core/hooks/index.ts +1 -0
- package/src/core/hooks/prompt-context/contract.ts +177 -0
- package/src/core/hooks/prompt-context/index.ts +22 -0
- package/src/core/hooks/prompt-context/runner.ts +303 -0
- package/src/core/qa-artifacts.ts +29 -3
- package/src/core/resources/contract.ts +76 -3
- package/src/core/resources/index.ts +1 -0
- package/src/core/resources/native.ts +481 -0
- package/src/core/resources/sampler.ts +46 -1
- package/src/core/resources/status.ts +717 -0
- package/src/core/resources/system.ts +530 -0
- package/src/core/semantic/service.ts +37 -3
- package/src/core/storage/builtins.ts +41 -0
- package/src/core/supervisor/contract.ts +45 -3
- package/src/core/supervisor/findings.ts +159 -0
- package/src/core/supervisor/history.ts +15 -0
- package/src/core/supervisor/ledger-state.ts +0 -0
- package/src/core/supervisor/service.ts +133 -5
- package/src/core/supervisor/storage.ts +7 -0
- package/src/core/workflow/admission.ts +33 -17
- package/src/core/workflow/engine.ts +2 -2
- package/src/core/workflow/live-session-v3.ts +6 -8
- package/src/core/workflow/proof.ts +4 -3
- package/src/lib/browser/capture-fidelity.ts +23 -0
- package/src/lib/browser/client.ts +96 -44
- package/src/lib/browser/index.ts +15 -1
- package/src/lib/browser/page-review-budget.ts +309 -0
- package/src/lib/browser/page-review-contracts.ts +129 -0
- package/src/lib/browser/page-review-evidence.ts +465 -0
- package/src/lib/browser/page-review-judge.ts +56 -40
- package/src/lib/browser/page-review-pack.ts +181 -43
- package/src/lib/browser/page-review-source.ts +30 -0
- package/src/lib/browser/qa-run-contracts.ts +62 -18
- package/src/lib/browser/qa-run.ts +259 -71
- package/src/lib/browser/qa-snapshot.ts +289 -48
- package/src/lib/browser/review-capture-transaction.ts +106 -0
- package/src/lib/browser/standalone-html.ts +4 -2
- package/src/lib/browser/thumbnail-association.ts +190 -0
- package/src/lib/browser/tiling.ts +63 -0
- package/src/lib/docs-metadata-managed.ts +4 -1
- package/src/lib/identities/assume.ts +12 -4
- package/src/lib/instructions/templates.ts +1 -0
- package/src/lib/local-file-url.ts +17 -2
- package/src/lib/tunnel/state.ts +70 -13
|
@@ -6,11 +6,13 @@ import type {
|
|
|
6
6
|
ObservedHookHealth,
|
|
7
7
|
ObservedServiceHealth,
|
|
8
8
|
SupervisorActivitySnapshot,
|
|
9
|
+
SupervisorCapability,
|
|
9
10
|
SupervisorFinding,
|
|
10
11
|
SupervisorFindingExplanation,
|
|
11
12
|
SupervisorFindings,
|
|
12
13
|
SupervisorHistory,
|
|
13
14
|
SupervisorLogFeed,
|
|
15
|
+
SupervisorPressureRecord,
|
|
14
16
|
SupervisorSnapshot,
|
|
15
17
|
SupervisorTimeline,
|
|
16
18
|
} from "../supervisor/contract.ts";
|
|
@@ -23,14 +25,24 @@ import { explainSupervisorFinding } from "../supervisor/explanations.ts";
|
|
|
23
25
|
import { updateSupervisorFindings } from "../supervisor/findings.ts";
|
|
24
26
|
import { projectHookHealth } from "../supervisor/hook-health.ts";
|
|
25
27
|
import { buildSupervisorTimeline } from "../supervisor/timeline.ts";
|
|
26
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
buildDiagnosticAdvice,
|
|
30
|
+
pressureHistoryFromSupervisor,
|
|
31
|
+
unknownPressureAssessment,
|
|
32
|
+
} from "./advice.ts";
|
|
27
33
|
import {
|
|
28
34
|
DIAGNOSTIC_EXPECTED_SCHEMA_VERSION,
|
|
35
|
+
type DiagnosticAdvice,
|
|
29
36
|
type DiagnosticExpected,
|
|
30
37
|
type DiagnosticObservations,
|
|
31
38
|
type DiagnosticSelection,
|
|
32
39
|
type DiagnosticThresholds,
|
|
33
40
|
} from "./contract.ts";
|
|
41
|
+
import { assessPressure } from "./pressure.ts";
|
|
42
|
+
import type { PressureFindingInput } from "./pressure-contract.ts";
|
|
43
|
+
|
|
44
|
+
const PRESSURE_SOURCE = "supervisor.pressure";
|
|
45
|
+
const FINDINGS_SOURCE = "supervisor.findings";
|
|
34
46
|
|
|
35
47
|
export function canonicalJson(value: unknown): string {
|
|
36
48
|
return JSON.stringify(sortValue(value));
|
|
@@ -60,7 +72,7 @@ export function deriveCapturedExpected(
|
|
|
60
72
|
findings,
|
|
61
73
|
findings.map((finding) => diagnosticTimeline(finding, relatedSources)),
|
|
62
74
|
findings.map(explainSupervisorFinding),
|
|
63
|
-
observations,
|
|
75
|
+
capturedAdvice(observations, findings),
|
|
64
76
|
);
|
|
65
77
|
}
|
|
66
78
|
|
|
@@ -75,8 +87,15 @@ export function replayDiagnosticInputs(
|
|
|
75
87
|
const supervisor =
|
|
76
88
|
optionalSource<SupervisorSnapshot>(observations, "supervisor.snapshot") ??
|
|
77
89
|
neutralSupervisor(observations.captured_at);
|
|
90
|
+
// A captured history is untrusted JSON from a bundle that may predate the
|
|
91
|
+
// current point shape. Reading it unchecked would let an older capture reach
|
|
92
|
+
// the trend projection with no pressure readings on its points. The live
|
|
93
|
+
// observer and the dashboard validate the same way.
|
|
94
|
+
const capturedHistory = optionalSource<SupervisorHistory>(observations, "supervisor.history");
|
|
78
95
|
const history =
|
|
79
|
-
|
|
96
|
+
capturedHistory?.schema_version === SUPERVISOR_HISTORY_SCHEMA_VERSION
|
|
97
|
+
? capturedHistory
|
|
98
|
+
: neutralHistory();
|
|
80
99
|
const logFeed =
|
|
81
100
|
optionalSource<SupervisorLogFeed>(observations, "supervisor.log-feed") ??
|
|
82
101
|
neutralLogFeed(observations.captured_at);
|
|
@@ -115,10 +134,100 @@ export function replayDiagnosticInputs(
|
|
|
115
134
|
findings,
|
|
116
135
|
findings.map((finding) => diagnosticTimeline(finding, relatedSources)),
|
|
117
136
|
findings.map(explainSupervisorFinding),
|
|
118
|
-
observations,
|
|
137
|
+
replayedAdvice(observations, resource, history, evaluated),
|
|
119
138
|
);
|
|
120
139
|
}
|
|
121
140
|
|
|
141
|
+
/**
|
|
142
|
+
* The live assessment, exactly as the observer published it. Nothing is
|
|
143
|
+
* recomputed here, because `expected.json` freezes what the machine actually
|
|
144
|
+
* reported at capture time.
|
|
145
|
+
*/
|
|
146
|
+
function capturedAdvice(
|
|
147
|
+
observations: DiagnosticObservations,
|
|
148
|
+
findings: readonly SupervisorFinding[],
|
|
149
|
+
): DiagnosticAdvice {
|
|
150
|
+
const record = optionalSource<SupervisorPressureRecord>(observations, PRESSURE_SOURCE);
|
|
151
|
+
const capability = capturedSourceCapability(observations, PRESSURE_SOURCE);
|
|
152
|
+
if (!record?.assessment) {
|
|
153
|
+
return unavailableAdvice(observations.captured_at, capability);
|
|
154
|
+
}
|
|
155
|
+
return buildDiagnosticAdvice({
|
|
156
|
+
assessment: record.assessment,
|
|
157
|
+
priorHysteresis: record.prior_hysteresis ?? null,
|
|
158
|
+
sourceCapability: capability,
|
|
159
|
+
activeFindingCount: openedCount(findings),
|
|
160
|
+
evaluatedAt: observations.captured_at,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Recompute the assessment from frozen inputs. `prior_hysteresis` comes from
|
|
166
|
+
* the captured record, so a transition that depended on the previous sample is
|
|
167
|
+
* reproduced instead of restarting from a cold state.
|
|
168
|
+
*/
|
|
169
|
+
function replayedAdvice(
|
|
170
|
+
observations: DiagnosticObservations,
|
|
171
|
+
resource: ResourceSnapshot,
|
|
172
|
+
history: SupervisorHistory,
|
|
173
|
+
evaluated: SupervisorFindings,
|
|
174
|
+
): DiagnosticAdvice {
|
|
175
|
+
const record = optionalSource<SupervisorPressureRecord>(observations, PRESSURE_SOURCE);
|
|
176
|
+
const capability = capturedSourceCapability(observations, PRESSURE_SOURCE);
|
|
177
|
+
if (!record?.assessment) {
|
|
178
|
+
return unavailableAdvice(observations.captured_at, capability);
|
|
179
|
+
}
|
|
180
|
+
const snapshotAvailable = optionalSource<ResourceSnapshot>(observations, "resources.snapshot");
|
|
181
|
+
const findingsCapability = capturedSourceCapability(observations, FINDINGS_SOURCE);
|
|
182
|
+
const active = [...evaluated.active, ...evaluated.transitions].filter(
|
|
183
|
+
(finding) => finding.state === "opened",
|
|
184
|
+
);
|
|
185
|
+
const assessment = assessPressure({
|
|
186
|
+
snapshot: snapshotAvailable ? resource : null,
|
|
187
|
+
snapshot_reason: snapshotAvailable ? null : "captured_source_unavailable",
|
|
188
|
+
history: pressureHistoryFromSupervisor(history.points),
|
|
189
|
+
findings: active as readonly PressureFindingInput[],
|
|
190
|
+
findings_capability: {
|
|
191
|
+
source_kind: findingsCapability.source_kind,
|
|
192
|
+
state: findingsCapability.state,
|
|
193
|
+
...(findingsCapability.reason_code ? { reason_code: findingsCapability.reason_code } : {}),
|
|
194
|
+
},
|
|
195
|
+
prior: record.prior_hysteresis ?? null,
|
|
196
|
+
observer_generation: record.observer_generation,
|
|
197
|
+
// Recover the observer's exact clock. `observed_at` is the sample instant
|
|
198
|
+
// and `sample_age_ms` is how far past it the observer read, so the sum is
|
|
199
|
+
// the original `now`, which staleness and the OOM hold both depend on.
|
|
200
|
+
now_ms: Date.parse(record.assessment.observed_at) + (record.assessment.sample_age_ms ?? 0),
|
|
201
|
+
});
|
|
202
|
+
return buildDiagnosticAdvice({
|
|
203
|
+
assessment,
|
|
204
|
+
priorHysteresis: record.prior_hysteresis ?? null,
|
|
205
|
+
sourceCapability: capability,
|
|
206
|
+
activeFindingCount: openedCount(
|
|
207
|
+
filterFindings(findingsProjection(evaluated), observations.selection),
|
|
208
|
+
),
|
|
209
|
+
evaluatedAt: observations.captured_at,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function unavailableAdvice(capturedAt: string, capability: SupervisorCapability): DiagnosticAdvice {
|
|
214
|
+
return buildDiagnosticAdvice({
|
|
215
|
+
assessment: unknownPressureAssessment({
|
|
216
|
+
observedAt: capturedAt,
|
|
217
|
+
reasonCode: "evidence_unavailable",
|
|
218
|
+
summary: `Local resource pressure cannot be determined because the published assessment is ${capability.state}${capability.reason_code ? ` (${capability.reason_code})` : ""}.`,
|
|
219
|
+
}),
|
|
220
|
+
priorHysteresis: null,
|
|
221
|
+
sourceCapability: capability,
|
|
222
|
+
activeFindingCount: 0,
|
|
223
|
+
evaluatedAt: capturedAt,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function openedCount(findings: readonly SupervisorFinding[]): number {
|
|
228
|
+
return findings.filter((finding) => finding.state === "opened").length;
|
|
229
|
+
}
|
|
230
|
+
|
|
122
231
|
function diagnosticTimeline(
|
|
123
232
|
finding: SupervisorFinding,
|
|
124
233
|
relatedSources: readonly CoordinationHealthSnapshot["recent_events"][number][],
|
|
@@ -138,7 +247,7 @@ function expected(
|
|
|
138
247
|
findings: readonly SupervisorFinding[],
|
|
139
248
|
timelines: readonly SupervisorTimeline[],
|
|
140
249
|
explanations: readonly SupervisorFindingExplanation[],
|
|
141
|
-
|
|
250
|
+
advice: DiagnosticAdvice,
|
|
142
251
|
): DiagnosticExpected {
|
|
143
252
|
return {
|
|
144
253
|
schema_version: DIAGNOSTIC_EXPECTED_SCHEMA_VERSION,
|
|
@@ -151,18 +260,14 @@ function expected(
|
|
|
151
260
|
explanations: [...explanations].sort((left, right) =>
|
|
152
261
|
left.finding_id.localeCompare(right.finding_id),
|
|
153
262
|
),
|
|
154
|
-
advice
|
|
155
|
-
findings,
|
|
156
|
-
sourceCapability: capturedSourceCapability(observations, "supervisor.findings"),
|
|
157
|
-
evaluatedAt: observations.captured_at,
|
|
158
|
-
}),
|
|
263
|
+
advice,
|
|
159
264
|
};
|
|
160
265
|
}
|
|
161
266
|
|
|
162
267
|
function capturedSourceCapability(
|
|
163
268
|
observations: DiagnosticObservations,
|
|
164
269
|
sourceKind: string,
|
|
165
|
-
):
|
|
270
|
+
): SupervisorCapability {
|
|
166
271
|
const source = observations.sources.find((candidate) => candidate.source_kind === sourceKind);
|
|
167
272
|
return source
|
|
168
273
|
? {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Adapter } from "../../adapter.ts";
|
|
2
|
+
|
|
3
|
+
/** Harness identities that may produce or describe Event Ledger V3 evidence. */
|
|
4
|
+
export type EventAdapterIdV3 = Adapter | "openclaw";
|
|
5
|
+
|
|
6
|
+
/** Canonical event-adapter census. Workflow launch adapters remain in core/adapter.ts. */
|
|
7
|
+
export const EVENT_ADAPTER_IDS_V3 = [
|
|
8
|
+
"claude-code",
|
|
9
|
+
"codex",
|
|
10
|
+
"cursor",
|
|
11
|
+
"openclaw",
|
|
12
|
+
] as const satisfies readonly EventAdapterIdV3[];
|
|
@@ -10,11 +10,7 @@ import {
|
|
|
10
10
|
writeFileSync,
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { basename, join, relative, resolve } from "node:path";
|
|
13
|
-
import {
|
|
14
|
-
artifactAutoCleanIntervalHours,
|
|
15
|
-
type EventLedgerArchivePolicy,
|
|
16
|
-
eventLedgerArchivePolicy,
|
|
17
|
-
} from "../../config.ts";
|
|
13
|
+
import { type EventLedgerArchivePolicy, eventLedgerArchivePolicy } from "../../config.ts";
|
|
18
14
|
|
|
19
15
|
export type EventV3ArchiveClassification =
|
|
20
16
|
| "retained"
|
|
@@ -151,7 +147,8 @@ export function autoCleanEventV3Archives(
|
|
|
151
147
|
const policy = eventLedgerArchivePolicy(repoRoot);
|
|
152
148
|
if (!policy.autoClean) return { ran: false, reason: "disabled", deleted: 0, bytes: 0 };
|
|
153
149
|
const stampPath = join(resolve(repoRoot), AUTO_CLEAN_STAMP);
|
|
154
|
-
|
|
150
|
+
// Archive cleanup remains daily, independent of short-lived working files.
|
|
151
|
+
const intervalMs = 24 * 60 * 60 * 1000;
|
|
155
152
|
try {
|
|
156
153
|
const stamp = JSON.parse(readFileSync(stampPath, "utf8")) as { last_run_at?: string };
|
|
157
154
|
const last = Date.parse(stamp.last_run_at ?? "");
|
|
@@ -10,15 +10,18 @@ import {
|
|
|
10
10
|
readFileSync,
|
|
11
11
|
renameSync,
|
|
12
12
|
statSync,
|
|
13
|
+
unlinkSync,
|
|
13
14
|
writeFileSync,
|
|
14
15
|
} from "node:fs";
|
|
15
16
|
import { hostname } from "node:os";
|
|
16
17
|
import { dirname, join, resolve } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
17
19
|
import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
|
|
18
20
|
import { acquireNoClobberLease } from "../../workflow/workspaces/leases.ts";
|
|
19
21
|
import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
|
|
20
22
|
import { ADAPTER_CAPABILITY_PROFILES_V3 } from "./capabilities.ts";
|
|
21
23
|
import {
|
|
24
|
+
type ActivationManifestV3,
|
|
22
25
|
buildActivationManifestV3,
|
|
23
26
|
buildCandidateGenesisManifestV3,
|
|
24
27
|
type CandidateGenesisManifestV3,
|
|
@@ -42,6 +45,16 @@ export interface InitializeEventLedgerV3Input {
|
|
|
42
45
|
configDigest: `sha256:${string}`;
|
|
43
46
|
approvalRecordId: string;
|
|
44
47
|
forceNewEpoch?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Activate a candidate epoch that was already created for this root.
|
|
50
|
+
*
|
|
51
|
+
* Unset, this activates a candidate that carries this initializer's producer
|
|
52
|
+
* identity and refuses every other one. Creation and activation are one
|
|
53
|
+
* locked step here, so such a candidate is the residue of a crash or a
|
|
54
|
+
* failed activation, never work in progress; a candidate from another
|
|
55
|
+
* producer is a deliberate cutover state. Set it explicitly to force either
|
|
56
|
+
* answer.
|
|
57
|
+
*/
|
|
45
58
|
resumeCandidate?: boolean;
|
|
46
59
|
now?: () => Date;
|
|
47
60
|
}
|
|
@@ -59,6 +72,17 @@ export interface RefreshIncompatibleEventLedgerV3Result {
|
|
|
59
72
|
control: Extract<EventV3ControlState, { state: "active" }>;
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
/**
|
|
76
|
+
* The control producer every epoch created by this initializer carries.
|
|
77
|
+
*
|
|
78
|
+
* It separates a stranded epoch from a deliberate one. A candidate minted here
|
|
79
|
+
* was always meant to be activated in the same locked step, so finishing it is
|
|
80
|
+
* a repair. A candidate published by any other producer (an operator cutover or
|
|
81
|
+
* rollback rehearsal) is a state someone chose, and automatic activation would
|
|
82
|
+
* overrule them.
|
|
83
|
+
*/
|
|
84
|
+
const BOOTSTRAP_CONTROL_PRODUCER_ID = "prd_harnery-init" as const;
|
|
85
|
+
|
|
62
86
|
const BOOTSTRAP_LEASE_RETRIES = 12;
|
|
63
87
|
const BOOTSTRAP_LEASE_RETRY_MS = 25;
|
|
64
88
|
const BOOTSTRAP_LEASE_STALE_MS = 10_000;
|
|
@@ -141,6 +165,74 @@ export function refreshIncompatibleEventLedgerV3(
|
|
|
141
165
|
});
|
|
142
166
|
}
|
|
143
167
|
|
|
168
|
+
export interface RepairStrandedEventLedgerV3CandidateResult {
|
|
169
|
+
state: "repaired" | "not_stranded" | "unavailable";
|
|
170
|
+
reason?: string;
|
|
171
|
+
control: EventV3ControlState;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The approval record every automatic stranded-candidate repair is bound to. */
|
|
175
|
+
export const EVENT_V3_STRANDED_CANDIDATE_APPROVAL_RECORD_ID =
|
|
176
|
+
"harnery-runtime-v3-stranded-candidate" as const;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Complete an epoch that was created but never activated.
|
|
180
|
+
*
|
|
181
|
+
* This initializer creates a candidate and activates it inside one lease, so a
|
|
182
|
+
* candidate carrying its producer identity is residue: some process published
|
|
183
|
+
* the genesis and died or failed before publishing the activation, and every
|
|
184
|
+
* hook afterwards served that half-built epoch with a candidate-only write
|
|
185
|
+
* gate. Finishing it is a repair, not a decision, and it is exactly as safe as
|
|
186
|
+
* the creation it completes: the same lease serializes it, the activation is
|
|
187
|
+
* derived from the immutable candidate packet, and no ledger row is edited or
|
|
188
|
+
* synthesized.
|
|
189
|
+
*
|
|
190
|
+
* A candidate from any other producer is a deliberate cutover state and is
|
|
191
|
+
* left untouched. A live bootstrap lease makes this a no-op rather than a
|
|
192
|
+
* failure, so the next boundary retries.
|
|
193
|
+
*/
|
|
194
|
+
export function repairStrandedEventLedgerV3Candidate(
|
|
195
|
+
coordRoot: string,
|
|
196
|
+
): RepairStrandedEventLedgerV3CandidateResult {
|
|
197
|
+
const root = resolve(coordRoot);
|
|
198
|
+
const observed = readEventV3ControlState(root);
|
|
199
|
+
const deliberateCandidate = observed.state === "candidate";
|
|
200
|
+
if (!strandedBootstrapCandidate(observed)) {
|
|
201
|
+
return {
|
|
202
|
+
state: "not_stranded",
|
|
203
|
+
control: observed,
|
|
204
|
+
...(deliberateCandidate ? { reason: "candidate_not_bootstrap_created" } : {}),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
return withBootstrapLease(root, () => {
|
|
209
|
+
const current = readEventV3ControlState(root);
|
|
210
|
+
if (!strandedBootstrapCandidate(current)) return { state: "not_stranded", control: current };
|
|
211
|
+
const activated = activateCandidateEpoch(
|
|
212
|
+
root,
|
|
213
|
+
current.genesis,
|
|
214
|
+
EVENT_V3_STRANDED_CANDIDATE_APPROVAL_RECORD_ID,
|
|
215
|
+
);
|
|
216
|
+
return { state: "repaired", control: activated.control };
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
return {
|
|
220
|
+
state: "unavailable",
|
|
221
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
222
|
+
control: observed,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function strandedBootstrapCandidate(
|
|
228
|
+
control: EventV3ControlState,
|
|
229
|
+
): control is Extract<EventV3ControlState, { state: "candidate" }> {
|
|
230
|
+
return (
|
|
231
|
+
control.state === "candidate" &&
|
|
232
|
+
control.genesis.event.producer.producer_id === BOOTSTRAP_CONTROL_PRODUCER_ID
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
144
236
|
export interface RotateOversizedEventLedgerV3Result {
|
|
145
237
|
state: "rotated" | "not_oversized" | "not_active" | "disabled";
|
|
146
238
|
active_bytes: number;
|
|
@@ -158,10 +250,13 @@ export interface RotateOversizedEventLedgerV3Result {
|
|
|
158
250
|
* (events, spool, producer states, manifests) moves whole into the V3
|
|
159
251
|
* archive, live sessions re-onboard into the new epoch on their next signal,
|
|
160
252
|
* and the writer's epoch fence keeps in-flight producers of the old epoch
|
|
161
|
-
* from ever committing into the new one.
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
253
|
+
* from ever committing into the new one. A candidate epoch rotates on the same
|
|
254
|
+
* terms as an active one: its control pair is valid, every reader still
|
|
255
|
+
* validates all of its history, and refusing to bound it is how a stranded
|
|
256
|
+
* epoch grew to 100 MB. Integrity failures stay closed for the explicit
|
|
257
|
+
* recovery command. The threshold comes from the isolated Event V3 config
|
|
258
|
+
* resolver unless the caller pins one; a non-positive threshold disables
|
|
259
|
+
* rotation.
|
|
165
260
|
*/
|
|
166
261
|
export function rotateOversizedEventLedgerV3(
|
|
167
262
|
coordRoot: string,
|
|
@@ -189,7 +284,7 @@ export function rotateOversizedEventLedgerV3(
|
|
|
189
284
|
return { state: "not_oversized", active_bytes: activeBytes, threshold_bytes: threshold };
|
|
190
285
|
}
|
|
191
286
|
const current = readEventV3ControlState(root);
|
|
192
|
-
if (current.state !== "active") {
|
|
287
|
+
if (current.state !== "active" && current.state !== "candidate") {
|
|
193
288
|
return { state: "not_active", active_bytes: activeBytes, threshold_bytes: threshold };
|
|
194
289
|
}
|
|
195
290
|
// Flush durable ready rows into the epoch they were produced for, so the
|
|
@@ -235,13 +330,14 @@ function initializeEventLedgerV3Locked(
|
|
|
235
330
|
): InitializeEventLedgerV3Result {
|
|
236
331
|
const root = resolve(input.coordRoot);
|
|
237
332
|
let current = readEventV3ControlState(root);
|
|
238
|
-
|
|
333
|
+
const resumeCandidate = input.resumeCandidate ?? strandedBootstrapCandidate(current);
|
|
334
|
+
if (resumeCandidate && current.state === "repairable") {
|
|
239
335
|
current = repairEventV3ControlPair(root);
|
|
240
336
|
}
|
|
241
337
|
if (!input.forceNewEpoch && current.state === "active") {
|
|
242
338
|
return { state: "active", initialized: false, control: current };
|
|
243
339
|
}
|
|
244
|
-
if (!input.forceNewEpoch &&
|
|
340
|
+
if (!input.forceNewEpoch && resumeCandidate && current.state === "candidate") {
|
|
245
341
|
return activateCandidateEpoch(root, current.genesis, input.approvalRecordId);
|
|
246
342
|
}
|
|
247
343
|
if (!input.forceNewEpoch && current.state === "candidate") {
|
|
@@ -250,11 +346,10 @@ function initializeEventLedgerV3Locked(
|
|
|
250
346
|
|
|
251
347
|
const now = input.now ?? (() => new Date());
|
|
252
348
|
const createdAt = now().toISOString();
|
|
253
|
-
const archivedEpoch = archiveCurrentEpoch(root, createdAt);
|
|
254
349
|
const keys = loadOrCreateFingerprintKeyStoreV3(root, now);
|
|
255
350
|
const harneryBuild = safeBuild(input.harneryBuild);
|
|
256
351
|
const hostBuild = safeBuild(input.hostBuild);
|
|
257
|
-
const producerId =
|
|
352
|
+
const producerId = BOOTSTRAP_CONTROL_PRODUCER_ID;
|
|
258
353
|
const bootId =
|
|
259
354
|
`boot_${createHash("sha256").update(`${root}\0${createdAt}`).digest("hex")}` as const;
|
|
260
355
|
const buildId = liveEventV3BuildId(harneryBuild);
|
|
@@ -288,18 +383,42 @@ function initializeEventLedgerV3Locked(
|
|
|
288
383
|
platform: livePlatformV3(),
|
|
289
384
|
},
|
|
290
385
|
});
|
|
386
|
+
// Mint the complete control pair before any epoch bytes move. Every approval
|
|
387
|
+
// and validation failure then happens while the current epoch is still
|
|
388
|
+
// whole, so it can never leave a replaced epoch stranded in candidate state.
|
|
389
|
+
const activation = buildActivationManifestV3(activationInput(candidate, input.approvalRecordId));
|
|
390
|
+
|
|
391
|
+
const archivedEpoch = archiveCurrentEpoch(root, createdAt);
|
|
291
392
|
publishControlFile(join(root, EVENT_V3_GENESIS_MANIFEST), candidate);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
393
|
+
let activated: InitializeEventLedgerV3Result;
|
|
394
|
+
try {
|
|
395
|
+
activated = completeCandidateActivation(root, activation);
|
|
396
|
+
} catch (error) {
|
|
397
|
+
// The epoch has already been replaced, so returning here would strand it.
|
|
398
|
+
// Retry the completion inside the same lease and, failing that, name the
|
|
399
|
+
// stranded epoch rather than reporting a successful initialization.
|
|
400
|
+
activated = completeStrandedCandidateActivation(root, activation, error);
|
|
295
401
|
}
|
|
296
|
-
const activated = activateCandidateEpoch(root, candidate, input.approvalRecordId);
|
|
297
402
|
return {
|
|
298
403
|
...activated,
|
|
299
404
|
...(archivedEpoch ? { archived_epoch: archivedEpoch } : {}),
|
|
300
405
|
};
|
|
301
406
|
}
|
|
302
407
|
|
|
408
|
+
function completeStrandedCandidateActivation(
|
|
409
|
+
root: string,
|
|
410
|
+
activation: ActivationManifestV3,
|
|
411
|
+
cause: unknown,
|
|
412
|
+
): InitializeEventLedgerV3Result {
|
|
413
|
+
try {
|
|
414
|
+
return completeCandidateActivation(root, activation);
|
|
415
|
+
} catch {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`event_v3_epoch_stranded_before_activation:${cause instanceof Error ? cause.message : String(cause)}`,
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
303
422
|
function runtimeCapabilityProfileCurrent(
|
|
304
423
|
control: Extract<EventV3ControlState, { state: "candidate" | "active" }>,
|
|
305
424
|
): boolean {
|
|
@@ -355,8 +474,16 @@ function activateCandidateEpoch(
|
|
|
355
474
|
candidate: CandidateGenesisManifestV3,
|
|
356
475
|
approvalRecordId: string,
|
|
357
476
|
): InitializeEventLedgerV3Result {
|
|
477
|
+
return completeCandidateActivation(
|
|
478
|
+
root,
|
|
479
|
+
buildActivationManifestV3(activationInput(candidate, approvalRecordId)),
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/** The activation packet one exact candidate deserves, derived and pure. */
|
|
484
|
+
function activationInput(candidate: CandidateGenesisManifestV3, approvalRecordId: string) {
|
|
358
485
|
const producer = candidate.event.producer;
|
|
359
|
-
|
|
486
|
+
return {
|
|
360
487
|
candidate,
|
|
361
488
|
approval_record_id: approvalRecordId,
|
|
362
489
|
activation_approved_at: candidate.profile.candidate_created_at,
|
|
@@ -368,7 +495,23 @@ function activateCandidateEpoch(
|
|
|
368
495
|
platform: producer.platform,
|
|
369
496
|
...(producer.bridge ? { bridge: producer.bridge } : {}),
|
|
370
497
|
},
|
|
371
|
-
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Bring a published candidate to active: append the pre-minted genesis event
|
|
503
|
+
* if a crash left it missing, publish the activation manifest, then append its
|
|
504
|
+
* pre-minted event. Every step is idempotent, so the caller may retry it, and
|
|
505
|
+
* anything short of `active` throws with the state that blocked it.
|
|
506
|
+
*/
|
|
507
|
+
function completeCandidateActivation(
|
|
508
|
+
root: string,
|
|
509
|
+
activation: ActivationManifestV3,
|
|
510
|
+
): InitializeEventLedgerV3Result {
|
|
511
|
+
const candidateState = repairEventV3ControlPair(root);
|
|
512
|
+
if (candidateState.state !== "candidate") {
|
|
513
|
+
throw new Error(`event_v3_candidate_initialization_failed:${candidateState.state}`);
|
|
514
|
+
}
|
|
372
515
|
publishControlFile(join(root, EVENT_V3_ACTIVATION_MANIFEST), activation);
|
|
373
516
|
const active = repairEventV3ControlPair(root);
|
|
374
517
|
if (active.state !== "active") {
|
|
@@ -394,6 +537,10 @@ function archiveCurrentEpoch(root: string, createdAt: string): string | undefine
|
|
|
394
537
|
function publishControlFile(path: string, value: unknown): void {
|
|
395
538
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
396
539
|
const temp = `${path}.tmp.${process.pid}`;
|
|
540
|
+
// A prior failed publish by this same pid would otherwise make every retry
|
|
541
|
+
// fail on the exclusive create, which is exactly how one transient error
|
|
542
|
+
// turns into a permanently stranded epoch.
|
|
543
|
+
if (existsSync(temp)) unlinkSync(temp);
|
|
397
544
|
const fd = openSync(temp, "wx", 0o600);
|
|
398
545
|
try {
|
|
399
546
|
writeFileSync(fd, `${canonicalJsonV3(value)}\n`, "utf8");
|
|
@@ -415,7 +562,7 @@ function safeBuild(value: string): string {
|
|
|
415
562
|
}
|
|
416
563
|
|
|
417
564
|
function rootOfHarnery(): string {
|
|
418
|
-
return resolve(import.meta.
|
|
565
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
419
566
|
}
|
|
420
567
|
|
|
421
568
|
function repositoryBuild(root: string): string {
|