@north-light/crouter 0.3.311 → 0.3.313
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/api/client.d.ts +27 -30
- package/dist/api/client.js +80 -67
- package/dist/api/dto/broker-ops.d.ts +10 -20
- package/dist/api/dto/canvas.d.ts +1 -10
- package/dist/api/dto/health.d.ts +113 -0
- package/dist/api/dto/human-requests.d.ts +14 -3
- package/dist/api/dto/human.d.ts +2 -48
- package/dist/api/dto/human.js +3 -2
- package/dist/api/dto/inbox.d.ts +14 -0
- package/dist/api/dto/mail.d.ts +43 -0
- package/dist/api/dto/nodes.d.ts +11 -8
- package/dist/api/dto/recovery.d.ts +96 -0
- package/dist/api/dto/reviews.d.ts +2 -2
- package/dist/api/routes.d.ts +9 -6
- package/dist/api/routes.js +11 -11
- package/dist/build-root.d.ts +4 -0
- package/dist/build-root.js +11 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
- package/dist/cli.js +10 -1
- package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
- package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
- package/dist/clients/attach/chrome/canvas-panels.js +1 -1
- package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
- package/dist/clients/attach/chrome/inbox-strip.js +17 -9
- package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
- package/dist/clients/attach/chrome/ticket-panel.js +77 -46
- package/dist/clients/attach/overlays/graph.js +6 -9
- package/dist/clients/attach/render/chat-view.d.ts +5 -4
- package/dist/clients/attach/render/chat-view.js +22 -13
- package/dist/clients/attach/render/page-block.d.ts +11 -7
- package/dist/clients/attach/render/page-block.js +64 -55
- package/dist/clients/attach/viewer.js +746 -735
- package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
- package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
- package/dist/clients/inbox/controller.d.ts +42 -14
- package/dist/clients/inbox/controller.js +240 -124
- package/dist/clients/inbox/dto-adapt.d.ts +28 -0
- package/dist/clients/inbox/dto-adapt.js +93 -0
- package/dist/clients/inbox/page-adapter.d.ts +5 -4
- package/dist/clients/inbox/page-adapter.js +12 -12
- package/dist/clients/inbox/resolve.d.ts +10 -5
- package/dist/clients/inbox/resolve.js +13 -12
- package/dist/clients/inbox/review/review-client.d.ts +0 -1
- package/dist/clients/inbox/review/review-client.js +0 -4
- package/dist/clients/inbox/review-adapter.d.ts +2 -1
- package/dist/clients/inbox/review-adapter.js +3 -5
- package/dist/clients/inbox/surface.d.ts +2 -2
- package/dist/clients/inbox/surface.js +19 -8
- package/dist/clients/inbox/tui.d.ts +3 -3
- package/dist/commands/__tests__/human.test.js +31 -15
- package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
- package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
- package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
- package/dist/commands/api-client.d.ts +5 -4
- package/dist/commands/api-client.js +13 -7
- package/dist/commands/canvas-history/shared.js +1 -1
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-roster.js +1 -1
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/human/feedback.js +20 -17
- package/dist/commands/human/prompts.js +45 -36
- package/dist/commands/human/queue.js +73 -123
- package/dist/commands/human/review.js +6 -16
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
- package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
- package/dist/commands/node-inspect-artifacts.js +1 -1
- package/dist/commands/node-lifecycle-revive.d.ts +2 -2
- package/dist/commands/node-lifecycle-revive.js +9 -19
- package/dist/commands/pkg/plugin-manage.js +0 -1
- package/dist/commands/profile/show.js +4 -12
- package/dist/commands/surface-tmux-spread.js +1 -5
- package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
- package/dist/commands/sys/branch.d.ts +2 -0
- package/dist/commands/sys/branch.js +19 -0
- package/dist/commands/sys/config.js +106 -33
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/sys/daemon.js +130 -7
- package/dist/commands/sys/doctor.js +5 -15
- package/dist/commands/sys/migrate.js +21 -92
- package/dist/commands/sys/setup-settings-runner.js +4 -2
- package/dist/commands/sys.js +2 -15
- package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
- package/dist/core/__tests__/boot.test.js +32 -25
- package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
- package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
- package/dist/core/__tests__/canvas.test.js +18 -47
- package/dist/core/__tests__/child-death-wake.test.js +29 -7
- package/dist/core/__tests__/cli-help-contract.test.js +2 -0
- package/dist/core/__tests__/close.test.js +29 -10
- package/dist/core/__tests__/collectible.test.js +2 -1
- package/dist/core/__tests__/config-change-delta.test.js +2 -0
- package/dist/core/__tests__/context-intro.test.js +17 -5
- package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
- package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
- package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
- package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
- package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
- package/dist/core/__tests__/daemon-boot.test.js +51 -14
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
- package/dist/core/__tests__/focuses.test.js +10 -5
- package/dist/core/__tests__/helpers/harness.js +50 -18
- package/dist/core/__tests__/history-inbox.test.js +12 -16
- package/dist/core/__tests__/human-action-delivery.test.js +48 -28
- package/dist/core/__tests__/human-actions.test.js +7 -3
- package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
- package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
- package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
- package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
- package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
- package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
- package/dist/core/__tests__/integration/lifecycle-hooks.test.js +312 -1
- package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
- package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
- package/dist/core/__tests__/integration/revive.test.js +67 -21
- package/dist/core/__tests__/integration/spawn-root.test.js +62 -19
- package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
- package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
- package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
- package/dist/core/__tests__/kickoff.test.js +36 -39
- package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
- package/dist/core/__tests__/lifecycle.test.js +2 -1
- package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
- package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
- package/dist/core/__tests__/migration.test.js +145 -31
- package/dist/core/__tests__/model-pin-durability.test.js +4 -1
- package/dist/core/__tests__/model-routes-config.test.js +13 -5
- package/dist/core/__tests__/node-outcome.test.js +93 -26
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +4 -3
- package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
- package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
- package/dist/core/__tests__/passive-subscription.test.js +50 -157
- package/dist/core/__tests__/plugin-kinds.test.js +6 -4
- package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
- package/dist/core/__tests__/plugin-page-components.test.js +6 -4
- package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +35 -32
- package/dist/core/__tests__/prune.test.js +11 -5
- package/dist/core/__tests__/push-final-guard.test.js +14 -7
- package/dist/core/__tests__/relaunch-root.test.js +30 -2
- package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
- package/dist/core/__tests__/repository-root-identity.test.js +4 -0
- package/dist/core/__tests__/review-model-floor.test.js +6 -3
- package/dist/core/__tests__/revive-all.test.js +13 -3
- package/dist/core/__tests__/revive-capacity.test.js +26 -4
- package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
- package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
- package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
- package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
- package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +5 -4
- package/dist/core/__tests__/stop-guard.test.js +2 -1
- package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +19 -5
- package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
- package/dist/core/bootstrap.js +2 -0
- package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
- package/dist/core/broker-client/transport-socket.d.ts +1 -0
- package/dist/core/broker-client/transport-socket.js +13 -5
- package/dist/core/canvas/__tests__/attention.test.js +29 -35
- package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
- package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
- package/dist/core/canvas/attention.d.ts +2 -5
- package/dist/core/canvas/attention.js +15 -64
- package/dist/core/canvas/boot.d.ts +6 -7
- package/dist/core/canvas/boot.js +5 -9
- package/dist/core/canvas/canvas.d.ts +13 -109
- package/dist/core/canvas/canvas.js +140 -401
- package/dist/core/canvas/crons.d.ts +2 -0
- package/dist/core/canvas/crons.js +16 -15
- package/dist/core/canvas/daemon-owner.js +10 -0
- package/dist/core/canvas/db.d.ts +2 -2
- package/dist/core/canvas/db.js +2 -18
- package/dist/core/canvas/deliveries.d.ts +104 -0
- package/dist/core/canvas/deliveries.js +354 -0
- package/dist/core/canvas/executions.d.ts +29 -0
- package/dist/core/canvas/executions.js +68 -0
- package/dist/core/canvas/history.js +35 -35
- package/dist/core/canvas/index.d.ts +3 -1
- package/dist/core/canvas/index.js +3 -1
- package/dist/core/canvas/migrations.js +20 -12
- package/dist/core/canvas/nav-model.js +1 -3
- package/dist/core/canvas/nav-render.d.ts +4 -4
- package/dist/core/canvas/nav-render.js +11 -21
- package/dist/core/canvas/node-sql-migration.d.ts +3 -0
- package/dist/core/canvas/node-sql-migration.js +261 -0
- package/dist/core/canvas/paths.d.ts +2 -16
- package/dist/core/canvas/paths.js +2 -31
- package/dist/core/canvas/recovery.d.ts +76 -0
- package/dist/core/canvas/recovery.js +588 -0
- package/dist/core/canvas/remote-canvas-source.js +4 -0
- package/dist/core/canvas/render-source.d.ts +16 -27
- package/dist/core/canvas/render-source.js +16 -45
- package/dist/core/canvas/sessions.d.ts +33 -0
- package/dist/core/canvas/sessions.js +78 -0
- package/dist/core/canvas/types.d.ts +33 -33
- package/dist/core/canvas/types.js +2 -5
- package/dist/core/canvas/waits.d.ts +18 -0
- package/dist/core/canvas/waits.js +70 -0
- package/dist/core/canvas/worktrees.d.ts +40 -0
- package/dist/core/canvas/worktrees.js +93 -0
- package/dist/core/command-hooks/artifact.d.ts +6 -0
- package/dist/core/command-hooks/artifact.js +22 -6
- package/dist/core/command-hooks/discovery.d.ts +2 -0
- package/dist/core/command-hooks/discovery.js +2 -0
- package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
- package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
- package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
- package/dist/core/command-hooks/lifecycle-create.js +51 -0
- package/dist/core/command-hooks/schema.d.ts +2 -2
- package/dist/core/command-hooks/schema.js +5 -4
- package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
- package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
- package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
- package/dist/core/config.d.ts +17 -12
- package/dist/core/config.js +106 -155
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/errors.js +4 -0
- package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
- package/dist/core/feed/feed.d.ts +0 -6
- package/dist/core/feed/feed.js +68 -56
- package/dist/core/feed/inbox.d.ts +7 -81
- package/dist/core/feed/inbox.js +46 -378
- package/dist/core/feed/messages.d.ts +58 -0
- package/dist/core/feed/messages.js +217 -0
- package/dist/core/fs-utils.d.ts +4 -0
- package/dist/core/fs-utils.js +21 -1
- package/dist/core/human/answer.d.ts +1 -1
- package/dist/core/human/answer.js +1 -1
- package/dist/core/human/claim.d.ts +1 -1
- package/dist/core/human/claim.js +2 -4
- package/dist/core/human/component-docs.js +1 -1
- package/dist/core/human/feedback-companion.js +4 -3
- package/dist/core/human/feedback.d.ts +5 -8
- package/dist/core/human/feedback.js +57 -85
- package/dist/core/human/page-authoring.d.ts +30 -0
- package/dist/core/human/page-authoring.js +19 -0
- package/dist/core/human/requests.d.ts +136 -0
- package/dist/core/human/requests.js +515 -0
- package/dist/core/human/root.d.ts +0 -7
- package/dist/core/human/root.js +0 -17
- package/dist/core/human/scan.d.ts +2 -7
- package/dist/core/human/scan.js +48 -132
- package/dist/core/human/summary.d.ts +1 -1
- package/dist/core/human/summary.js +1 -1
- package/dist/core/human/tickets.d.ts +23 -83
- package/dist/core/human/tickets.js +92 -243
- package/dist/core/human/types.d.ts +5 -1
- package/dist/core/installed-plugins.js +8 -3
- package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
- package/dist/core/locked-file.d.ts +6 -3
- package/dist/core/locked-file.js +36 -9
- package/dist/core/memory/extensions.js +2 -0
- package/dist/core/preview-registry.js +0 -1
- package/dist/core/profiles/__tests__/env-store.test.js +2 -0
- package/dist/core/profiles/default-binding.d.ts +0 -4
- package/dist/core/profiles/default-binding.js +12 -58
- package/dist/core/profiles/env-store.js +8 -3
- package/dist/core/profiles/manifest.d.ts +2 -37
- package/dist/core/profiles/manifest.js +35 -161
- package/dist/core/profiles/select.js +3 -0
- package/dist/core/review/__tests__/capture-origin.test.js +13 -2
- package/dist/core/review/__tests__/comments.test.js +28 -10
- package/dist/core/review/__tests__/remap.test.js +28 -10
- package/dist/core/review/__tests__/stage-identity.test.js +17 -7
- package/dist/core/review/birth.d.ts +1 -1
- package/dist/core/review/birth.js +34 -5
- package/dist/core/review/comments.js +1 -1
- package/dist/core/review/companion.d.ts +1 -1
- package/dist/core/review/companion.js +2 -2
- package/dist/core/review/document.js +1 -1
- package/dist/core/review/project.d.ts +5 -1
- package/dist/core/review/project.js +14 -36
- package/dist/core/review/realize.d.ts +3 -6
- package/dist/core/review/realize.js +51 -168
- package/dist/core/review/remap.js +1 -1
- package/dist/core/review/stage.d.ts +3 -2
- package/dist/core/review/stage.js +2 -3
- package/dist/core/review/store.d.ts +3 -21
- package/dist/core/review/store.js +140 -186
- package/dist/core/review/types.d.ts +0 -1
- package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
- package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
- package/dist/core/runtime/broker/auth-reload.js +9 -10
- package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
- package/dist/core/runtime/broker/daemon-ops.js +42 -9
- package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
- package/dist/core/runtime/broker/extension-abort.js +11 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
- package/dist/core/runtime/broker/fault-retry.js +142 -95
- package/dist/core/runtime/broker/inbox.d.ts +4 -32
- package/dist/core/runtime/broker/inbox.js +4 -92
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +21 -12
- package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
- package/dist/core/runtime/broker-persona-guidance.js +2 -13
- package/dist/core/runtime/broker.js +28 -22
- package/dist/core/runtime/close.d.ts +3 -9
- package/dist/core/runtime/close.js +16 -27
- package/dist/core/runtime/fault.d.ts +3 -93
- package/dist/core/runtime/fault.js +9 -419
- package/dist/core/runtime/kickoff.d.ts +3 -6
- package/dist/core/runtime/kickoff.js +12 -18
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +40 -7
- package/dist/core/runtime/nodes.d.ts +5 -1
- package/dist/core/runtime/nodes.js +26 -11
- package/dist/core/runtime/placement.js +2 -2
- package/dist/core/runtime/recycle.js +24 -14
- package/dist/core/runtime/reset.js +26 -7
- package/dist/core/runtime/revive.js +226 -217
- package/dist/core/runtime/spawn.d.ts +4 -1
- package/dist/core/runtime/spawn.js +43 -5
- package/dist/core/runtime/warm-pool.d.ts +2 -2
- package/dist/core/runtime/warm-pool.js +23 -3
- package/dist/core/scoped-state/db.d.ts +7 -0
- package/dist/core/scoped-state/db.js +92 -0
- package/dist/core/scoped-state/migrate.d.ts +11 -0
- package/dist/core/scoped-state/migrate.js +198 -0
- package/dist/core/scoped-state/paths.d.ts +9 -0
- package/dist/core/scoped-state/paths.js +27 -0
- package/dist/core/scoped-state/profiles.d.ts +12 -0
- package/dist/core/scoped-state/profiles.js +62 -0
- package/dist/core/scoped-state/providers.d.ts +16 -0
- package/dist/core/scoped-state/providers.js +8 -0
- package/dist/core/scoped-state/schema.d.ts +3 -0
- package/dist/core/scoped-state/schema.js +26 -0
- package/dist/core/scoped-state/settings.d.ts +28 -0
- package/dist/core/scoped-state/settings.js +82 -0
- package/dist/core/subscription-state.d.ts +29 -48
- package/dist/core/subscription-state.js +176 -712
- package/dist/core/worktree-close.js +4 -4
- package/dist/core/worktree-mutation-async.js +2 -2
- package/dist/core/worktree-sweep.js +11 -16
- package/dist/core/worktree.js +6 -12
- package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
- package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
- package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
- package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
- package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +41 -17
- package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
- package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
- package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
- package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
- package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
- package/dist/daemon/api/__tests__/model-config.test.js +8 -8
- package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
- package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
- package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +6 -3
- package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
- package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
- package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
- package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +29 -43
- package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
- package/dist/daemon/api/bridge.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +6 -7
- package/dist/daemon/api/handlers/broker-ops.js +47 -84
- package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
- package/dist/daemon/api/handlers/broker-recovery.js +251 -0
- package/dist/daemon/api/handlers/canvas.js +2 -44
- package/dist/daemon/api/handlers/daemon.js +41 -0
- package/dist/daemon/api/handlers/feedback-comments.js +2 -2
- package/dist/daemon/api/handlers/health.js +10 -5
- package/dist/daemon/api/handlers/human-requests.js +168 -188
- package/dist/daemon/api/handlers/human.js +3 -148
- package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
- package/dist/daemon/api/handlers/inbox-page.js +89 -0
- package/dist/daemon/api/handlers/inbox.js +21 -120
- package/dist/daemon/api/handlers/messages.d.ts +1 -0
- package/dist/daemon/api/handlers/messages.js +73 -15
- package/dist/daemon/api/handlers/node-outcomes.js +58 -13
- package/dist/daemon/api/handlers/nodes.d.ts +1 -0
- package/dist/daemon/api/handlers/nodes.js +41 -42
- package/dist/daemon/api/handlers/reviews.js +4 -29
- package/dist/daemon/api/map.d.ts +25 -3
- package/dist/daemon/api/map.js +84 -14
- package/dist/daemon/api/server.js +68 -9
- package/dist/daemon/control.d.ts +18 -0
- package/dist/daemon/cron/disposition.d.ts +2 -0
- package/dist/daemon/cron/disposition.js +8 -5
- package/dist/daemon/cron/settle.js +37 -13
- package/dist/daemon/cron/sinks.d.ts +18 -1
- package/dist/daemon/cron/sinks.js +47 -93
- package/dist/daemon/crtrd-cli.js +4 -19
- package/dist/daemon/crtrd.d.ts +2 -0
- package/dist/daemon/crtrd.js +206 -81
- package/dist/daemon/fleet.js +5 -6
- package/dist/daemon/human/finish.d.ts +12 -24
- package/dist/daemon/human/finish.js +88 -285
- package/dist/daemon/human/settle.d.ts +40 -0
- package/dist/daemon/human/settle.js +89 -0
- package/dist/daemon/human/sweep.d.ts +20 -5
- package/dist/daemon/human/sweep.js +56 -60
- package/dist/daemon/manage.d.ts +21 -16
- package/dist/daemon/manage.js +85 -51
- package/dist/daemon/messaging/node-message.d.ts +1 -1
- package/dist/daemon/messaging/node-message.js +9 -12
- package/dist/daemon/park-activity.js +3 -3
- package/dist/daemon/reconcilers/bash-deadline.js +6 -7
- package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
- package/dist/daemon/reconcilers/broker-supervision.js +41 -19
- package/dist/daemon/reconcilers/controller-death.js +6 -18
- package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/delivery-lane.js +130 -0
- package/dist/daemon/reconcilers/live-obligation.js +3 -3
- package/dist/daemon/reconcilers/node-deadline.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
- package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
- package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
- package/dist/daemon/review/companion.js +5 -11
- package/dist/daemon/review/deliver.d.ts +1 -5
- package/dist/daemon/review/deliver.js +3 -52
- package/dist/daemon/review/finish.d.ts +2 -27
- package/dist/daemon/review/finish.js +79 -263
- package/dist/daemon/review/sweep.d.ts +3 -10
- package/dist/daemon/review/sweep.js +13 -131
- package/dist/daemon/startup-policy.d.ts +0 -5
- package/dist/daemon/startup-policy.js +0 -5
- package/dist/hook-authoring.d.ts +28 -2
- package/dist/hook-authoring.js +53 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
- package/dist/migrations/__tests__/activation.test.js +9 -6
- package/dist/migrations/activation.d.ts +3 -2
- package/dist/migrations/activation.js +2 -4
- package/dist/migrations/corpus.d.ts +2 -5
- package/dist/migrations/corpus.js +9 -20
- package/dist/migrations/runner.d.ts +3 -1
- package/dist/migrations/runner.js +6 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
- package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
- package/dist/pi-extensions/canvas-passive-context.js +85 -37
- package/dist/pi-extensions/canvas-stophook.js +47 -31
- package/dist/shared/env.d.ts +1 -0
- package/dist/shared/env.js +3 -0
- package/package.json +1 -1
- package/runtime.lock.json +5 -5
- package/dist/clients/attach/__tests__/page-block.test.js +0 -54
- package/dist/clients/inbox/review-visibility.d.ts +0 -5
- package/dist/clients/inbox/review-visibility.js +0 -19
- package/dist/commands/canvas-rebuild-index.d.ts +0 -2
- package/dist/commands/canvas-rebuild-index.js +0 -46
- package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
- package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
- package/dist/core/__tests__/human-deliver.test.js +0 -256
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
- package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
- package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
- package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
- package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
- package/dist/core/__tests__/phase4-review-store.test.js +0 -144
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
- package/dist/core/canvas/human-deliveries.d.ts +0 -39
- package/dist/core/canvas/human-deliveries.js +0 -75
- package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
- package/dist/core/canvas/node-outcome-deliveries.js +0 -159
- package/dist/core/feed/passive.d.ts +0 -3
- package/dist/core/feed/passive.js +0 -53
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
- package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
- package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
- package/dist/core/human/__tests__/page-tickets.test.js +0 -161
- package/dist/core/review/ticket-filter.d.ts +0 -17
- package/dist/core/review/ticket-filter.js +0 -21
- package/dist/core/runtime/broker/passive.d.ts +0 -17
- package/dist/core/runtime/broker/passive.js +0 -44
- package/dist/core/runtime/busy.d.ts +0 -46
- package/dist/core/runtime/busy.js +0 -150
- package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
- package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
- package/dist/daemon/human/deliver-action.d.ts +0 -3
- package/dist/daemon/human/deliver-action.js +0 -9
- package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
- package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
- package/dist/daemon/startup-block-marker.d.ts +0 -25
- package/dist/daemon/startup-block-marker.js +0 -99
- /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
- /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
- /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
|
@@ -34,8 +34,12 @@ import { join } from 'node:path';
|
|
|
34
34
|
import { spawn, spawnSync } from 'node:child_process';
|
|
35
35
|
import { createNode, deleteNode, getNode, subscribe, updateNode } from '../../canvas/canvas.js';
|
|
36
36
|
import { armCron, getCron } from '../../canvas/crons.js';
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
37
|
+
import { closeDb, migrate, openDb } from '../../canvas/db.js';
|
|
38
|
+
import { closeScopedStores } from '../../scoped-state/db.js';
|
|
39
|
+
import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
|
|
40
|
+
import { userScopeRoot } from '../../scope.js';
|
|
41
|
+
import { resetSession } from '../../canvas/sessions.js';
|
|
42
|
+
import { liveExecutionForNode } from '../../canvas/executions.js';
|
|
39
43
|
import { apiSocketPath } from '../../canvas/paths.js';
|
|
40
44
|
import { buildPiArgv } from '../../runtime/launch.js';
|
|
41
45
|
import { headlessBrokerHost } from '../../runtime/host.js';
|
|
@@ -45,12 +49,15 @@ import { parseCard } from '../../../shared/generated-context.js';
|
|
|
45
49
|
import { seedRoadmap } from '../../runtime/roadmap.js';
|
|
46
50
|
import { InputError } from '../../io.js';
|
|
47
51
|
import { CrtrClient } from '../../../api/client.js';
|
|
48
|
-
import { readFault } from '../../
|
|
52
|
+
import { readFault } from '../../canvas/recovery.js';
|
|
49
53
|
import { createHarness } from '../helpers/harness.js';
|
|
50
54
|
import { bindTestFleet } from '../helpers/fleet.js';
|
|
51
55
|
import { resetFleetForTests } from '../../runtime/fleet.js';
|
|
52
56
|
import { emptyContextExposureState, exposureTarget, initializeForkContextExposure, loadContextExposureState, registerExposure, saveContextExposureState, } from '../../substrate/injected-store.js';
|
|
53
57
|
let home;
|
|
58
|
+
let scopeCwd;
|
|
59
|
+
let originalCwd;
|
|
60
|
+
let originalHome;
|
|
54
61
|
function node(id, over = {}) {
|
|
55
62
|
return {
|
|
56
63
|
node_id: id,
|
|
@@ -64,6 +71,26 @@ function node(id, over = {}) {
|
|
|
64
71
|
...over,
|
|
65
72
|
};
|
|
66
73
|
}
|
|
74
|
+
function fabricateInScope(h, options = {}) {
|
|
75
|
+
const id = h.fabricateBrokerNode(options);
|
|
76
|
+
updateNode(id, { cwd: scopeCwd });
|
|
77
|
+
return id;
|
|
78
|
+
}
|
|
79
|
+
function createNodeWithSession(meta) {
|
|
80
|
+
createNode(meta);
|
|
81
|
+
if (meta.pi_session_id !== undefined || meta.pi_session_file !== undefined) {
|
|
82
|
+
resetSession(meta.node_id, { sessionId: meta.pi_session_id ?? '', transcriptPath: meta.pi_session_file ?? null });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function wakeMail(nodeId) {
|
|
86
|
+
return openDb().prepare(`SELECT m.sender_node_id AS "from", m.body_json AS data
|
|
87
|
+
FROM deliveries d JOIN messages m ON m.message_id = d.message_id
|
|
88
|
+
WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = 1
|
|
89
|
+
ORDER BY m.seq`).all(nodeId).map((row) => {
|
|
90
|
+
const mail = row;
|
|
91
|
+
return { from: mail.from, data: mail.data === null ? null : JSON.parse(mail.data) };
|
|
92
|
+
});
|
|
93
|
+
}
|
|
67
94
|
/** A launch prompt is the fresh-revive kickoff when it carries that card kind. */
|
|
68
95
|
function isReviveKickoff(arg) {
|
|
69
96
|
return parseCard({ role: 'user', content: arg })?.kind === 'revive';
|
|
@@ -88,16 +115,33 @@ before(() => {
|
|
|
88
115
|
// to /private/var, and the runtime resolves a fork's durable source file, so a
|
|
89
116
|
// raw temp path would compare unequal to the very path the runtime replays.
|
|
90
117
|
home = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-revive-')));
|
|
118
|
+
scopeCwd = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-revive-scope-')));
|
|
119
|
+
originalCwd = process.cwd();
|
|
120
|
+
originalHome = process.env['HOME'];
|
|
121
|
+
process.chdir(scopeCwd);
|
|
122
|
+
process.env['HOME'] = scopeCwd;
|
|
91
123
|
process.env['CRTR_HOME'] = home;
|
|
92
124
|
});
|
|
93
125
|
beforeEach(() => {
|
|
94
126
|
closeDb();
|
|
127
|
+
closeScopedStores();
|
|
95
128
|
rmSync(home, { recursive: true, force: true });
|
|
129
|
+
rmSync(userScopeRoot(), { recursive: true, force: true });
|
|
130
|
+
migrate(openDb());
|
|
131
|
+
initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
|
|
96
132
|
});
|
|
97
133
|
after(() => {
|
|
98
134
|
closeDb();
|
|
135
|
+
closeScopedStores();
|
|
99
136
|
rmSync(home, { recursive: true, force: true });
|
|
137
|
+
rmSync(userScopeRoot(), { recursive: true, force: true });
|
|
100
138
|
delete process.env['CRTR_HOME'];
|
|
139
|
+
process.chdir(originalCwd);
|
|
140
|
+
rmSync(scopeCwd, { recursive: true, force: true });
|
|
141
|
+
if (originalHome === undefined)
|
|
142
|
+
delete process.env['HOME'];
|
|
143
|
+
else
|
|
144
|
+
process.env['HOME'] = originalHome;
|
|
101
145
|
for (const pid of livePids) {
|
|
102
146
|
try {
|
|
103
147
|
process.kill(pid, 'SIGKILL');
|
|
@@ -188,7 +232,7 @@ test('a resumed revive replays the persisted LaunchSpec (model, tools, extension
|
|
|
188
232
|
env: { FOO: 'bar' },
|
|
189
233
|
},
|
|
190
234
|
});
|
|
191
|
-
|
|
235
|
+
createNodeWithSession(m); // scaffolds the node dir so the system prompt persists to a file
|
|
192
236
|
const { argv, env } = buildPiArgv(m, resumeArgs(m, true));
|
|
193
237
|
// Persona extensions, in order.
|
|
194
238
|
const ea = argv.indexOf('/ext/a.js');
|
|
@@ -223,7 +267,7 @@ test('reviveNode no-ops when the node has a live fleet entry — double-revive g
|
|
|
223
267
|
// synchronously, so a live entry means a broker is already on this .jsonl
|
|
224
268
|
// and re-launching would double-spawn onto it. No pid probe is involved
|
|
225
269
|
// anywhere — the entry alone is the authority.
|
|
226
|
-
const M = h
|
|
270
|
+
const M = fabricateInScope(h, {
|
|
227
271
|
status: 'active',
|
|
228
272
|
intent: null,
|
|
229
273
|
pi_pid: deadPid(),
|
|
@@ -251,7 +295,7 @@ test('reviveNode PROCEEDS when the node has NO fleet entry — cycle counter bum
|
|
|
251
295
|
const stalePid = deadPid();
|
|
252
296
|
mkdirSync(join(home, 'sessions'), { recursive: true });
|
|
253
297
|
writeFileSync(sessionFile, '{}\n', 'utf8');
|
|
254
|
-
const M = h
|
|
298
|
+
const M = fabricateInScope(h, {
|
|
255
299
|
status: 'active',
|
|
256
300
|
intent: null,
|
|
257
301
|
pi_pid: stalePid,
|
|
@@ -301,11 +345,11 @@ function armDeadline(anchor, cronId) {
|
|
|
301
345
|
test('a recovery relaunch keeps the armed deadline; a wake revive consumes it', async () => {
|
|
302
346
|
const h = await createHarness({ headless: true, sessionPrefix: 'crtr-revive-deadline' });
|
|
303
347
|
try {
|
|
304
|
-
const recovered = h
|
|
348
|
+
const recovered = fabricateInScope(h, { status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'uuid-1' });
|
|
305
349
|
armDeadline(recovered, 'cron-recovery');
|
|
306
350
|
await reviveNode(recovered, { resume: true, recovery: true, capacity: 'freeze' });
|
|
307
351
|
assert.equal(getCron('cron-recovery') !== null, true, 'the daemon replacing a dead broker is not a wake — the deadline survives so the wait can still settle');
|
|
308
|
-
const woken = h
|
|
352
|
+
const woken = fabricateInScope(h, { status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'uuid-2' });
|
|
309
353
|
armDeadline(woken, 'cron-wake');
|
|
310
354
|
await reviveNode(woken, { resume: true, capacity: 'freeze' });
|
|
311
355
|
assert.equal(getCron('cron-wake'), null, 'a wake revive won the race and cancels the deadline');
|
|
@@ -320,15 +364,17 @@ test('a second broker session-bound request is shut down instead of adopting a l
|
|
|
320
364
|
const sessionFile = join(home, 'sessions', 'owned.jsonl');
|
|
321
365
|
mkdirSync(join(home, 'sessions'), { recursive: true });
|
|
322
366
|
writeFileSync(sessionFile, '{}\n', 'utf8');
|
|
323
|
-
const owner = h
|
|
367
|
+
const owner = fabricateInScope(h, {
|
|
324
368
|
id: 'live-session-owner',
|
|
325
369
|
pi_pid: process.pid,
|
|
326
370
|
pi_session_id: 'owner-session',
|
|
327
371
|
pi_session_file: sessionFile,
|
|
328
372
|
});
|
|
329
|
-
const claimant = h
|
|
373
|
+
const claimant = fabricateInScope(h, { id: 'session-claimant', pi_pid: process.pid });
|
|
330
374
|
const client = new CrtrClient({ socketPath: apiSocketPath(), autostart: false });
|
|
375
|
+
assert.equal(getNode(owner)?.pi_pid, process.pid, 'the owner has a live SQL execution');
|
|
331
376
|
const result = await client.bindBrokerSession(claimant, {
|
|
377
|
+
executionId: liveExecutionForNode(claimant).executionId,
|
|
332
378
|
piSessionId: 'claimant-session', sessionFile, pid: process.pid, reason: 'startup', reviewBoundaryIds: [],
|
|
333
379
|
});
|
|
334
380
|
assert.deepEqual(result, { action: 'shutdown' }, 'the second broker is refused before it can bind the live transcript');
|
|
@@ -351,7 +397,7 @@ test('reviveNode fresh-fallback clears the stale session identity (Major-3: no p
|
|
|
351
397
|
// retried forever instead of surfaced as a boot failure.
|
|
352
398
|
const missingFile = join(home, 'sessions', 'never-written.jsonl');
|
|
353
399
|
const stalePid = deadPid();
|
|
354
|
-
const M = h
|
|
400
|
+
const M = fabricateInScope(h, {
|
|
355
401
|
status: 'active',
|
|
356
402
|
intent: null,
|
|
357
403
|
pi_pid: stalePid,
|
|
@@ -378,7 +424,7 @@ test('unbound forks replay one durable source file for node-id, path, and UUID o
|
|
|
378
424
|
try {
|
|
379
425
|
mkdirSync(home, { recursive: true });
|
|
380
426
|
writeFileSync(forkFile, '{}\n', 'utf8');
|
|
381
|
-
|
|
427
|
+
createNodeWithSession(node('ordinary-fork-source', { cwd: home, pi_session_file: forkFile }));
|
|
382
428
|
const sourceExposure = emptyContextExposureState();
|
|
383
429
|
registerExposure(exposureTarget(sourceExposure, 'system'), 'source-system-only', 'content');
|
|
384
430
|
registerExposure(exposureTarget(sourceExposure, 'transcript'), 'source-transcript', 'content');
|
|
@@ -392,7 +438,7 @@ test('unbound forks replay one durable source file for node-id, path, and UUID o
|
|
|
392
438
|
const operands = ['ordinary-fork-source', forkFile, '019e8ce3-322e-7000-8000-000000000001'];
|
|
393
439
|
for (const [index, operand] of operands.entries()) {
|
|
394
440
|
const destination = `ordinary-fork-${index}`;
|
|
395
|
-
|
|
441
|
+
createNodeWithSession(node(destination, {
|
|
396
442
|
cwd: home,
|
|
397
443
|
fork_from: operand,
|
|
398
444
|
fork_source_file: forkFile,
|
|
@@ -431,7 +477,7 @@ test('reviveNode throws a typed cwd_missing error and does NOT crash the node wh
|
|
|
431
477
|
const h = await createHarness({ headless: true, sessionPrefix: 'crtr-revive-cwdmissing' });
|
|
432
478
|
try {
|
|
433
479
|
const missingCwd = join(home, 'never-created-cwd');
|
|
434
|
-
const M = h
|
|
480
|
+
const M = fabricateInScope(h, {
|
|
435
481
|
status: 'active',
|
|
436
482
|
intent: null,
|
|
437
483
|
pi_pid: deadPid(),
|
|
@@ -452,7 +498,7 @@ test('reviveNode relaunches fresh when the session header cwd was removed (NL-14
|
|
|
452
498
|
const sessionFile = join(home, 'sessions', 'stale-cwd.jsonl');
|
|
453
499
|
mkdirSync(join(home, 'sessions'), { recursive: true });
|
|
454
500
|
writeFileSync(sessionFile, `${JSON.stringify({ type: 'session', version: 3, id: 'stale-session', cwd: removedCwd })}\n`, 'utf8');
|
|
455
|
-
const M = h
|
|
501
|
+
const M = fabricateInScope(h, {
|
|
456
502
|
status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'stale-session', pi_session_file: sessionFile,
|
|
457
503
|
});
|
|
458
504
|
const result = await reviveNode(M, { resume: true, capacity: 'freeze', recovery: true });
|
|
@@ -471,7 +517,7 @@ test('reviveNode crashes and clears pid when launch/preflight throws', async ()
|
|
|
471
517
|
const sessionFile = join(home, 'sessions', 'crtr-revive-fail.jsonl');
|
|
472
518
|
mkdirSync(join(home, 'sessions'), { recursive: true });
|
|
473
519
|
writeFileSync(sessionFile, '{}\n', 'utf8');
|
|
474
|
-
const M = h
|
|
520
|
+
const M = fabricateInScope(h, {
|
|
475
521
|
status: 'active',
|
|
476
522
|
intent: null,
|
|
477
523
|
pi_pid: deadPid(),
|
|
@@ -500,7 +546,7 @@ test('reviveNode fans a doctrine wake to subscribers when launch/preflight throw
|
|
|
500
546
|
const sessionFile = join(home, 'sessions', 'crtr-revive-crashwake.jsonl');
|
|
501
547
|
mkdirSync(join(home, 'sessions'), { recursive: true });
|
|
502
548
|
writeFileSync(sessionFile, '{}\n', 'utf8');
|
|
503
|
-
const M = h
|
|
549
|
+
const M = fabricateInScope(h, {
|
|
504
550
|
parent: root,
|
|
505
551
|
status: 'active',
|
|
506
552
|
intent: null,
|
|
@@ -514,7 +560,7 @@ test('reviveNode fans a doctrine wake to subscribers when launch/preflight throw
|
|
|
514
560
|
headlessBrokerHost.launch = (_nodeId, _inv, _opts) => { throw new Error('boom'); };
|
|
515
561
|
await assert.rejects(reviveNode(M, { resume: true, capacity: 'freeze' }), /refusing to revive/i);
|
|
516
562
|
assert.equal(h.node(M).status, 'dead', 'failed revive marks the node dead');
|
|
517
|
-
const entries =
|
|
563
|
+
const entries = wakeMail(root);
|
|
518
564
|
const notice = entries.find((e) => e.data?.['reason'] === 'child-crashed' && e.data?.['child'] === M);
|
|
519
565
|
assert.ok(notice !== undefined, 'the surviving parent got a doctrine wake for the dead child');
|
|
520
566
|
assert.equal(notice.from, M, 'the notice is attributed to the crashed child');
|
|
@@ -546,18 +592,18 @@ test('an unstarted node relaunches as a birth, not a "revived fresh" kickoff', a
|
|
|
546
592
|
};
|
|
547
593
|
const missing = join(home, 'sessions', 'never-written.jsonl');
|
|
548
594
|
// (1) Bare interactive node — nothing on disk anywhere: launch promptless.
|
|
549
|
-
|
|
595
|
+
createNodeWithSession(node('bare-unstarted', { cwd: home, pi_session_id: 'uuid-a', pi_session_file: missing }));
|
|
550
596
|
await reviveNode('bare-unstarted', { resume: true, capacity: 'freeze' });
|
|
551
597
|
const bare = launches[0];
|
|
552
598
|
assert.equal(bare.some((a) => isReviveKickoff(a)), false, 'an unstarted node is never told it lost context it never had');
|
|
553
599
|
assert.deepEqual(bare, buildPiArgv(getNode('bare-unstarted'), {}).argv, 'a promptless boot — the argv carries no trailing prompt positional at all');
|
|
554
600
|
// (2) Spawned with a mandate whose first turn never persisted: replay it.
|
|
555
|
-
|
|
601
|
+
createNodeWithSession(node('mandate-unstarted', { cwd: home, pi_session_id: 'uuid-b', pi_session_file: missing }));
|
|
556
602
|
writeGoal('mandate-unstarted', 'do the thing');
|
|
557
603
|
await reviveNode('mandate-unstarted', { resume: true, capacity: 'freeze' });
|
|
558
604
|
assert.equal(launches[1].at(-1), 'do the thing', 'a birth replays the spawn mandate');
|
|
559
605
|
// (3) Real durable progress on disk → still a genuine fresh revive.
|
|
560
|
-
|
|
606
|
+
createNodeWithSession(node('progressed', { cwd: home, pi_session_id: 'uuid-c', pi_session_file: missing }));
|
|
561
607
|
seedRoadmap('progressed', { goal: 'ship it' });
|
|
562
608
|
await reviveNode('progressed', { resume: true, capacity: 'freeze' });
|
|
563
609
|
assert.equal(isReviveKickoff(launches[2].at(-1)), true, 'a node with a roadmap still rebuilds from the kickoff');
|
|
@@ -9,9 +9,13 @@ import { join } from 'node:path';
|
|
|
9
9
|
import { createServer } from 'node:net';
|
|
10
10
|
import { spawn as spawnProcess } from 'node:child_process';
|
|
11
11
|
import { createNode, getNode, getRow, subscribersOf } from '../../canvas/canvas.js';
|
|
12
|
-
import { coalesce
|
|
12
|
+
import { coalesce } from '../../feed/inbox.js';
|
|
13
13
|
import { coalesceBrokerInbox } from '../../runtime/broker/inbox.js';
|
|
14
|
-
import { closeDb } from '../../canvas/db.js';
|
|
14
|
+
import { closeDb, migrate, openDb } from '../../canvas/db.js';
|
|
15
|
+
import { resetSession } from '../../canvas/sessions.js';
|
|
16
|
+
import { closeScopedStores } from '../../scoped-state/db.js';
|
|
17
|
+
import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
|
|
18
|
+
import { userScopeRoot } from '../../scope.js';
|
|
15
19
|
import { nodeDir } from '../../canvas/paths.js';
|
|
16
20
|
import { spawnNode } from '../../runtime/nodes.js';
|
|
17
21
|
import { readGoal } from '../../runtime/kickoff.js';
|
|
@@ -19,12 +23,35 @@ import { spawnChild, resolveSpawner } from '../../runtime/spawn.js';
|
|
|
19
23
|
import { headlessBrokerHost } from '../../runtime/host.js';
|
|
20
24
|
import { resetFleetForTests } from '../../runtime/fleet.js';
|
|
21
25
|
import { bindTestFleet } from '../helpers/fleet.js';
|
|
22
|
-
import { recordFault } from '../../
|
|
26
|
+
import { recordFault } from '../../canvas/recovery.js';
|
|
23
27
|
import { piSessionsRoot } from '../../runtime/pi-vendored.js';
|
|
24
28
|
import { emptyContextExposureState, exposureTarget, loadContextExposureState, registerExposure, saveContextExposureState, } from '../../substrate/injected-store.js';
|
|
25
29
|
import { ROOT_PROFILE_ID } from '../../profiles/manifest.js';
|
|
26
30
|
let home;
|
|
31
|
+
let scopedHome;
|
|
32
|
+
let previousHome;
|
|
27
33
|
let previousPidfile;
|
|
34
|
+
function initializeTestUserScope() {
|
|
35
|
+
initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
|
|
36
|
+
}
|
|
37
|
+
function mailFor(nodeId) {
|
|
38
|
+
return openDb().prepare(`SELECT m.message_id, m.operation_id, m.created_at, m.sender_node_id, m.sender_name, m.kind, m.label, m.body_ref, m.body_json, m.disposition, d.urgency
|
|
39
|
+
FROM deliveries d JOIN messages m ON m.message_id = d.message_id
|
|
40
|
+
WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.canceled_at IS NULL
|
|
41
|
+
ORDER BY m.seq`).all(nodeId).map((row) => ({
|
|
42
|
+
entry_id: row['message_id'],
|
|
43
|
+
operation_id: row['operation_id'],
|
|
44
|
+
ts: row['created_at'],
|
|
45
|
+
from: row['sender_node_id'],
|
|
46
|
+
...(row['sender_name'] === null ? {} : { from_name: row['sender_name'] }),
|
|
47
|
+
tier: row['urgency'],
|
|
48
|
+
kind: row['kind'],
|
|
49
|
+
...(row['body_ref'] === null ? {} : { ref: row['body_ref'] }),
|
|
50
|
+
label: row['label'],
|
|
51
|
+
...(row['body_json'] === null ? {} : { data: JSON.parse(row['body_json']) }),
|
|
52
|
+
...(row['disposition'] === null ? {} : { disposition: row['disposition'] }),
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
28
55
|
function spawner(id) {
|
|
29
56
|
return {
|
|
30
57
|
node_id: id,
|
|
@@ -41,6 +68,9 @@ before(() => {
|
|
|
41
68
|
// Unix-domain socket paths are capped at ~104 bytes on macOS; keep the
|
|
42
69
|
// canvas home short because root readiness now opens a real view.sock.
|
|
43
70
|
home = mkdtempSync('/tmp/crtr-spawn-root-');
|
|
71
|
+
scopedHome = mkdtempSync('/tmp/crtr-spawn-root-scope-');
|
|
72
|
+
previousHome = process.env['HOME'];
|
|
73
|
+
process.env['HOME'] = scopedHome;
|
|
44
74
|
process.env['CRTR_HOME'] = home;
|
|
45
75
|
previousPidfile = process.env['CRTR_PIDFILE'];
|
|
46
76
|
process.env['CRTR_PIDFILE'] = join(home, 'crtrd.pid');
|
|
@@ -50,8 +80,12 @@ before(() => {
|
|
|
50
80
|
});
|
|
51
81
|
beforeEach(() => {
|
|
52
82
|
closeDb();
|
|
83
|
+
closeScopedStores();
|
|
53
84
|
rmSync(home, { recursive: true, force: true });
|
|
85
|
+
rmSync(userScopeRoot(), { recursive: true, force: true });
|
|
54
86
|
mkdirSync(home, { recursive: true });
|
|
87
|
+
migrate(openDb());
|
|
88
|
+
initializeTestUserScope();
|
|
55
89
|
// These tests exercise direct spawn behavior, not daemon autostart. Keep
|
|
56
90
|
// ensureDaemon() from launching a background daemon whose startup verifier
|
|
57
91
|
// would outlive the test's next CRTR_HOME reset.
|
|
@@ -59,16 +93,22 @@ beforeEach(() => {
|
|
|
59
93
|
});
|
|
60
94
|
after(() => {
|
|
61
95
|
closeDb();
|
|
96
|
+
closeScopedStores();
|
|
62
97
|
resetFleetForTests();
|
|
63
98
|
rmSync(home, { recursive: true, force: true });
|
|
99
|
+
rmSync(scopedHome, { recursive: true, force: true });
|
|
100
|
+
if (previousHome === undefined)
|
|
101
|
+
delete process.env['HOME'];
|
|
102
|
+
else
|
|
103
|
+
process.env['HOME'] = previousHome;
|
|
64
104
|
if (previousPidfile === undefined)
|
|
65
105
|
delete process.env['CRTR_PIDFILE'];
|
|
66
106
|
else
|
|
67
107
|
process.env['CRTR_PIDFILE'] = previousPidfile;
|
|
68
108
|
});
|
|
69
|
-
test('managed child: parent on the spine + active subscription + provenance', () => {
|
|
109
|
+
test('managed child: parent on the spine + active subscription + provenance', async () => {
|
|
70
110
|
createNode(spawner('A'));
|
|
71
|
-
const child = spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
|
|
111
|
+
const child = await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
|
|
72
112
|
const meta = getNode(child.node_id);
|
|
73
113
|
assert.equal(meta.parent, 'A', 'child keeps its spine parent');
|
|
74
114
|
assert.equal(meta.spawned_by, 'A', 'child provenance defaults to its parent');
|
|
@@ -78,13 +118,13 @@ test('managed child: parent on the spine + active subscription + provenance', ()
|
|
|
78
118
|
assert.equal(subs[0].node_id, 'A');
|
|
79
119
|
assert.equal(subs[0].active, true, 'parent subscription is active');
|
|
80
120
|
});
|
|
81
|
-
test('external child birth is deferred, persists its creator, and renders its age at delivery in both inbox mirrors', () => {
|
|
121
|
+
test('external child birth is deferred, persists its creator, and renders its age at delivery in both inbox mirrors', async () => {
|
|
82
122
|
createNode(spawner('A'));
|
|
83
|
-
const creator = spawnNode({ kind: 'general', cwd: '/tmp/work', parent: null, name: 'Core' });
|
|
84
|
-
const child = spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: creator.node_id, name: 'worker' });
|
|
123
|
+
const creator = await spawnNode({ kind: 'general', cwd: '/tmp/work', parent: null, name: 'Core' });
|
|
124
|
+
const child = await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: creator.node_id, name: 'worker' });
|
|
85
125
|
const saved = getNode(child.node_id);
|
|
86
126
|
assert.equal(saved.creator, creator.node_id, 'creator persists through meta.json and getNode');
|
|
87
|
-
const [entry] =
|
|
127
|
+
const [entry] = mailFor('A');
|
|
88
128
|
assert.ok(entry, 'the parent receives exactly one birth entry');
|
|
89
129
|
assert.equal(entry.from, child.node_id, 'birth groups with the child’s future reports');
|
|
90
130
|
assert.equal(entry.tier, 'deferred', 'birth never starts a parent cycle');
|
|
@@ -106,14 +146,14 @@ test('external child birth is deferred, persists its creator, and renders its ag
|
|
|
106
146
|
assert.doesNotMatch(card, /\[\[CRTR_BIRTH_ANNOUNCEMENT_AGE\]\]/, 'the marker is not visible to the recipient');
|
|
107
147
|
}
|
|
108
148
|
}
|
|
109
|
-
spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
|
|
110
|
-
assert.equal(
|
|
111
|
-
spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: null });
|
|
112
|
-
assert.equal(
|
|
149
|
+
await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
|
|
150
|
+
assert.equal(mailFor('A').length, 1, 'a parent-created child stays silent');
|
|
151
|
+
await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: null });
|
|
152
|
+
assert.equal(mailFor('A').length, 1, 'a root never announces');
|
|
113
153
|
});
|
|
114
|
-
test('independent root: provenance only, no parent, no subscription', () => {
|
|
154
|
+
test('independent root: provenance only, no parent, no subscription', async () => {
|
|
115
155
|
createNode(spawner('A'));
|
|
116
|
-
const root = spawnNode({
|
|
156
|
+
const root = await spawnNode({
|
|
117
157
|
kind: 'developer',
|
|
118
158
|
cwd: '/tmp/work',
|
|
119
159
|
parent: null,
|
|
@@ -135,10 +175,10 @@ test('resolveSpawner: a --root spawn needs no caller; a managed child does (regr
|
|
|
135
175
|
assert.equal(resolveSpawner('A', null, false), 'A', 'explicit parent satisfies a child spawn');
|
|
136
176
|
assert.throws(() => resolveSpawner(undefined, null, false), /requires a calling node/, 'a managed child with no caller still throws');
|
|
137
177
|
});
|
|
138
|
-
test('unknown parent: spawnNode throws and mints NO node dir / row (validate before create)', () => {
|
|
178
|
+
test('unknown parent: spawnNode throws and mints NO node dir / row (validate before create)', async () => {
|
|
139
179
|
// Pre-allocate the would-be id so we can prove nothing was scaffolded for it.
|
|
140
180
|
const orphanId = 'orphan-under-ghost';
|
|
141
|
-
assert.
|
|
181
|
+
await assert.rejects(() => spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'ghost', nodeId: orphanId }), /cannot spawn under unknown parent node: ghost/);
|
|
142
182
|
// No half-born orphan: no meta (getNode null), no db row, no node dir on disk.
|
|
143
183
|
assert.equal(getNode(orphanId), null, 'no meta.json written for the orphan');
|
|
144
184
|
assert.equal(getRow(orphanId), null, 'no index row written for the orphan');
|
|
@@ -178,6 +218,7 @@ test('fork spawn resolves path and UUID provenance to the same transcript-only e
|
|
|
178
218
|
const launched = [];
|
|
179
219
|
try {
|
|
180
220
|
process.env['HOME'] = localHome;
|
|
221
|
+
initializeTestUserScope();
|
|
181
222
|
process.env['PI_CODING_AGENT_DIR'] = join(localHome, 'pi-agent');
|
|
182
223
|
process.env['CRTR_NODE_ID'] = 'fork-source';
|
|
183
224
|
delete process.env['TMUX'];
|
|
@@ -195,9 +236,8 @@ test('fork spawn resolves path and UUID provenance to the same transcript-only e
|
|
|
195
236
|
createNode({
|
|
196
237
|
...spawner('fork-source'),
|
|
197
238
|
cwd: localCwd,
|
|
198
|
-
pi_session_id: sessionId,
|
|
199
|
-
pi_session_file: sourceFile,
|
|
200
239
|
});
|
|
240
|
+
resetSession('fork-source', { sessionId, transcriptPath: sourceFile });
|
|
201
241
|
const canonicalSourceFile = getNode('fork-source').pi_session_file;
|
|
202
242
|
const sourceExposure = emptyContextExposureState();
|
|
203
243
|
registerExposure(exposureTarget(sourceExposure, 'system'), 'source-system', 'content');
|
|
@@ -243,6 +283,7 @@ test('fork spawn resolves path and UUID provenance to the same transcript-only e
|
|
|
243
283
|
}
|
|
244
284
|
finally {
|
|
245
285
|
headlessBrokerHost.launch = originalLaunch;
|
|
286
|
+
closeScopedStores();
|
|
246
287
|
if (previousHome === undefined)
|
|
247
288
|
delete process.env['HOME'];
|
|
248
289
|
else
|
|
@@ -281,6 +322,7 @@ test('spawnChild: a root from a shell gets the stable root profile id', async ()
|
|
|
281
322
|
const previousStdoutTty = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY');
|
|
282
323
|
try {
|
|
283
324
|
process.env.HOME = localHome;
|
|
325
|
+
initializeTestUserScope();
|
|
284
326
|
Object.defineProperty(process.stdin, 'isTTY', { value: false, configurable: true, writable: true });
|
|
285
327
|
Object.defineProperty(process.stdout, 'isTTY', { value: false, configurable: true, writable: true });
|
|
286
328
|
Reflect.deleteProperty(env, 'CRTR_NODE_ID');
|
|
@@ -303,6 +345,7 @@ test('spawnChild: a root from a shell gets the stable root profile id', async ()
|
|
|
303
345
|
}
|
|
304
346
|
finally {
|
|
305
347
|
headlessBrokerHost.launch = originalLaunch;
|
|
348
|
+
closeScopedStores();
|
|
306
349
|
if (previousHome === undefined)
|
|
307
350
|
delete process.env.HOME;
|
|
308
351
|
else
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Run with: npm run test:integration
|
|
2
2
|
//
|
|
3
3
|
// THE DAEMON WAKE HALF of delivery-vs-wake, on a graph >=2 levels deep. Routing
|
|
4
|
-
// itself (active →
|
|
4
|
+
// itself (active → wake delivery, passive → non-waking delivery) is owned by
|
|
5
5
|
// passive-subscription.test.ts; what this file owns is the decision ONE real
|
|
6
6
|
// `superviseTick` makes on that routing: the ACTIVE subscriber of a finished
|
|
7
7
|
// target is revived, the PASSIVE subscriber on the same target is not.
|
|
@@ -30,27 +30,40 @@
|
|
|
30
30
|
import { test } from 'node:test';
|
|
31
31
|
import assert from 'node:assert/strict';
|
|
32
32
|
import { spawnSync } from 'node:child_process';
|
|
33
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
34
33
|
import { createHarness } from '../helpers/harness.js';
|
|
35
|
-
import {
|
|
34
|
+
import { openDb } from '../../canvas/db.js';
|
|
36
35
|
import { subscribe } from '../../canvas/canvas.js';
|
|
37
36
|
import { pushFinal } from '../../feed/feed.js';
|
|
38
|
-
import { passivePath } from '../../canvas/paths.js';
|
|
39
37
|
// A pid reaped (dead) by the time spawnSync returns — a released broker's exited
|
|
40
38
|
// process (the headless analog of a closed pane).
|
|
41
39
|
function deadPid() {
|
|
42
40
|
const r = spawnSync('true', [], { stdio: 'ignore' });
|
|
43
41
|
return r.pid ?? 0x7ffffffe;
|
|
44
42
|
}
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// exposes inbox() but NOT passive() — this fills that gap.
|
|
43
|
+
// Read the canonical non-waking delivery rows, mirroring the harness's SQL
|
|
44
|
+
// wake-delivery reader.
|
|
48
45
|
function passive(nodeId) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
return openDb().prepare(`SELECT m.message_id AS entry_id, m.operation_id, m.created_at AS ts,
|
|
47
|
+
m.sender_node_id AS "from", m.sender_name AS from_name, d.urgency AS tier,
|
|
48
|
+
m.kind, m.body_ref AS ref, m.label, m.body_json AS data, m.disposition
|
|
49
|
+
FROM deliveries d JOIN messages m ON m.message_id = d.message_id
|
|
50
|
+
WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = 0
|
|
51
|
+
ORDER BY m.seq`).all(nodeId).map((row) => {
|
|
52
|
+
const value = row;
|
|
53
|
+
return {
|
|
54
|
+
entry_id: value['entry_id'],
|
|
55
|
+
operation_id: value['operation_id'],
|
|
56
|
+
ts: value['ts'],
|
|
57
|
+
from: value['from'],
|
|
58
|
+
...(value['from_name'] === null ? {} : { from_name: value['from_name'] }),
|
|
59
|
+
tier: value['tier'],
|
|
60
|
+
kind: value['kind'],
|
|
61
|
+
...(value['ref'] === null ? {} : { ref: value['ref'] }),
|
|
62
|
+
label: value['label'],
|
|
63
|
+
...(value['data'] === null ? {} : { data: JSON.parse(value['data']) }),
|
|
64
|
+
...(value['disposition'] === null ? {} : { disposition: value['disposition'] }),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
54
67
|
}
|
|
55
68
|
// `{node_id, active}` arrays → a stable comparable set of `id:active|passive`.
|
|
56
69
|
function edgeSet(arr) {
|
|
@@ -87,15 +100,14 @@ test('one daemon tick revives the active subscriber of a finished target and lea
|
|
|
87
100
|
assert.equal(h.node(B).cycles ?? 0, 0, 'B not yet revived');
|
|
88
101
|
assert.equal(h.node(P).cycles ?? 0, 0, 'P not yet revived');
|
|
89
102
|
// T FINISHES via the model layer — the precondition the daemon decides on:
|
|
90
|
-
// B
|
|
103
|
+
// B gets a wake delivery, P gets a non-waking delivery.
|
|
91
104
|
await pushFinal(T, 'TARGET-FINAL-BODY: the worker completed');
|
|
92
|
-
|
|
93
|
-
assert.
|
|
94
|
-
assert.equal(
|
|
95
|
-
assert.equal(passive(P).length, 1, 'PASSIVE subscriber P: exactly one passive entry');
|
|
105
|
+
assert.ok(h.inbox(B).find((e) => e.from === T && e.kind === 'final'), 'ACTIVE subscriber B: T-final wake delivery is durable');
|
|
106
|
+
assert.equal(h.inbox(P).length, 0, 'PASSIVE subscriber P: wake-delivery set stays empty');
|
|
107
|
+
assert.equal(passive(P).length, 1, 'PASSIVE subscriber P: exactly one non-waking delivery');
|
|
96
108
|
// The DAEMON decision pass. Its 2nd pass revives an idle-released node
|
|
97
|
-
// ONLY on an unseen
|
|
98
|
-
// (its entry
|
|
109
|
+
// ONLY on an unseen wake delivery. B has one (active) → REVIVED. P has none
|
|
110
|
+
// (its entry is non-waking) → stays idle. Decided in ONE tick.
|
|
99
111
|
await h.tick();
|
|
100
112
|
// ACTIVE subscriber: WOKEN — reviveNode bumped cycles + transition(revive).
|
|
101
113
|
{
|
|
@@ -111,8 +123,8 @@ test('one daemon tick revives the active subscriber of a finished target and lea
|
|
|
111
123
|
assert.equal(p.status, 'idle', 'P STILL idle — daemon did NOT wake it');
|
|
112
124
|
assert.equal(p.intent, 'idle-release', 'P still intent=idle-release (untouched)');
|
|
113
125
|
}
|
|
114
|
-
assert.equal(passive(P).length, 1, "P's
|
|
115
|
-
assert.equal(h.inbox(P).length, 0, "P's
|
|
126
|
+
assert.equal(passive(P).length, 1, "P's non-waking delivery is still pending (drained only on its next message)");
|
|
127
|
+
assert.equal(h.inbox(P).length, 0, "P's wake-delivery set is still empty");
|
|
116
128
|
}
|
|
117
129
|
finally {
|
|
118
130
|
await h.dispose();
|
|
@@ -13,7 +13,8 @@ import { createManagedWorktree, closeManagedWorktree, managedWorktreePath, rollb
|
|
|
13
13
|
import { closeManagedWorktreeAsync } from '../../worktree-close.js';
|
|
14
14
|
import { reconcileManagedWorktree } from '../../worktree-sweep.js';
|
|
15
15
|
import { createNode, getNode } from '../../canvas/canvas.js';
|
|
16
|
-
import { closeDb } from '../../canvas/db.js';
|
|
16
|
+
import { closeDb, migrate, openDb } from '../../canvas/db.js';
|
|
17
|
+
import { resetSession } from '../../canvas/sessions.js';
|
|
17
18
|
let home;
|
|
18
19
|
let remoteDir;
|
|
19
20
|
let sourceDir;
|
|
@@ -61,6 +62,7 @@ beforeEach(() => {
|
|
|
61
62
|
closeDb();
|
|
62
63
|
rmSync(home, { recursive: true, force: true });
|
|
63
64
|
process.env['CRTR_HOME'] = home;
|
|
65
|
+
migrate(openDb());
|
|
64
66
|
});
|
|
65
67
|
after(() => {
|
|
66
68
|
closeDb();
|
|
@@ -81,7 +83,8 @@ test('closeManagedWorktree leaves the active caller checkout intact with named d
|
|
|
81
83
|
mkdirSync(sessionDir, { recursive: true });
|
|
82
84
|
const sessionFile = join(sessionDir, 'session.jsonl');
|
|
83
85
|
writeFileSync(sessionFile, `${JSON.stringify({ type: 'session', version: 3, id: 'wt-session', cwd: wt.path })}\n`);
|
|
84
|
-
createNode(node(nodeId, { cwd: wt.path, managed_worktree: wt
|
|
86
|
+
createNode(node(nodeId, { cwd: wt.path, managed_worktree: wt }));
|
|
87
|
+
resetSession(nodeId, { sessionId: 'wt-session', transcriptPath: sessionFile });
|
|
85
88
|
const result = closeManagedWorktree(nodeId);
|
|
86
89
|
assert.equal(result.worktree_removed, false);
|
|
87
90
|
assert.match(result.worktree_remove_error ?? '', /cleanup pending/);
|
|
@@ -22,10 +22,14 @@ import { headlessBrokerHost } from '../../runtime/host.js';
|
|
|
22
22
|
import { reviveNode } from '../../runtime/revive.js';
|
|
23
23
|
import { bindTestFleet } from '../helpers/fleet.js';
|
|
24
24
|
import { resetFleetForTests } from '../../runtime/fleet.js';
|
|
25
|
-
import { closeDb } from '../../canvas/db.js';
|
|
25
|
+
import { closeDb, migrate, openDb } from '../../canvas/db.js';
|
|
26
|
+
import { closeScopedStores } from '../../scoped-state/db.js';
|
|
27
|
+
import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
|
|
28
|
+
import { userScopeRoot } from '../../scope.js';
|
|
26
29
|
let home;
|
|
27
30
|
let remoteDir;
|
|
28
31
|
let sourceDir;
|
|
32
|
+
let previousHome;
|
|
29
33
|
const scratchRoot = join(homedir(), '.crouter', 'canvas', 'scratch');
|
|
30
34
|
function run(args, cwd) {
|
|
31
35
|
const res = gitSync(args, cwd);
|
|
@@ -53,6 +57,8 @@ before(() => {
|
|
|
53
57
|
// directory. Every path-ownership check must judge identity, not spelling.
|
|
54
58
|
mkdirSync(scratchRoot, { recursive: true });
|
|
55
59
|
home = mkdtempSync(join(scratchRoot, 'crtr-worktree-home-'));
|
|
60
|
+
previousHome = process.env['HOME'];
|
|
61
|
+
process.env['HOME'] = home;
|
|
56
62
|
process.env['CRTR_HOME'] = home;
|
|
57
63
|
// Bare "remote" wired up as origin so the test can prove landing NEVER pushes
|
|
58
64
|
// to it (managed worktrees are based on and land onto local `main` only).
|
|
@@ -70,15 +76,23 @@ before(() => {
|
|
|
70
76
|
});
|
|
71
77
|
beforeEach(() => {
|
|
72
78
|
closeDb();
|
|
79
|
+
closeScopedStores();
|
|
73
80
|
rmSync(home, { recursive: true, force: true });
|
|
74
81
|
process.env['CRTR_HOME'] = home;
|
|
82
|
+
migrate(openDb());
|
|
83
|
+
initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
|
|
75
84
|
});
|
|
76
85
|
after(() => {
|
|
77
86
|
closeDb();
|
|
87
|
+
closeScopedStores();
|
|
78
88
|
rmSync(home, { recursive: true, force: true });
|
|
79
89
|
rmSync(remoteDir, { recursive: true, force: true });
|
|
80
90
|
rmSync(sourceDir, { recursive: true, force: true });
|
|
81
91
|
delete process.env['CRTR_HOME'];
|
|
92
|
+
if (previousHome === undefined)
|
|
93
|
+
delete process.env['HOME'];
|
|
94
|
+
else
|
|
95
|
+
process.env['HOME'] = previousHome;
|
|
82
96
|
});
|
|
83
97
|
// Fix B (#327/#333): a managed worktree with zero commits ahead of its base
|
|
84
98
|
// and nothing uncommitted has nothing to land — push-final's auto-drop path
|