@north-light/crouter 0.3.312 → 0.3.314
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 +757 -746
- 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 +13 -1
- 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/fresh-home-commands.test.js +49 -0
- package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
- package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
- 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 +51 -8
- 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 +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
- package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
- 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 +4 -1
- 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 +2 -1
- 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 +18 -4
- 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.js +11 -0
- package/dist/core/config.d.ts +17 -12
- package/dist/core/config.js +106 -155
- 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 +3 -2
- 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/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/placement.js +2 -2
- package/dist/core/runtime/recycle.js +8 -2
- package/dist/core/runtime/reset.js +22 -6
- package/dist/core/runtime/revive.js +226 -218
- package/dist/core/runtime/spawn.js +14 -3
- package/dist/core/scoped-state/db.d.ts +18 -0
- package/dist/core/scoped-state/db.js +135 -0
- package/dist/core/scoped-state/migrate.d.ts +8 -0
- package/dist/core/scoped-state/migrate.js +181 -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 +39 -15
- 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 +4 -1
- 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 +27 -41
- 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 -151
- 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.js +7 -13
- 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/index.d.ts +2 -1
- 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.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
- /package/dist/core/__tests__/{human-deliver.test.d.ts → integration/fresh-home-commands.test.d.ts} +0 -0
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
|
4
4
|
import { contextDir } from '../../canvas/paths.js';
|
|
5
5
|
import { getRow } from '../../canvas/canvas.js';
|
|
6
6
|
import { bashJobPaths, bashJobsDir } from '../../bash-jobs.js';
|
|
7
|
-
import {
|
|
7
|
+
import { acknowledgedNodeMailCount } from '../../canvas/deliveries.js';
|
|
8
8
|
import { transition } from '../../runtime/lifecycle.js';
|
|
9
9
|
import { ensureRootProfile, pauseProfile } from '../../profiles/manifest.js';
|
|
10
10
|
import { createHeadlessHarness } from '../helpers/harness.js';
|
|
@@ -22,7 +22,7 @@ function runningJob(nodeId) {
|
|
|
22
22
|
}
|
|
23
23
|
return undefined;
|
|
24
24
|
}
|
|
25
|
-
test('profile pause backgrounds real bash, retains its
|
|
25
|
+
test('profile pause backgrounds real bash, retains its queued SQL delivery, and recovers after a quick resume without failure', { timeout: 120_000 }, async () => {
|
|
26
26
|
const h = await createHeadlessHarness({ sessionPrefix: 'crtr-profile-pause' });
|
|
27
27
|
try {
|
|
28
28
|
const profile = ensureRootProfile();
|
|
@@ -39,7 +39,7 @@ test('profile pause backgrounds real bash, retains its inbox cursor, and recover
|
|
|
39
39
|
assert.equal(retained.code, 0, retained.stderr);
|
|
40
40
|
const retainedEntry = h.inbox(child).find((entry) => entry.data?.['body'] === 'mail retained while profile is paused');
|
|
41
41
|
assert.ok(retainedEntry, 'the message was durably appended');
|
|
42
|
-
assert.equal(
|
|
42
|
+
assert.equal(acknowledgedNodeMailCount(child), 0, 'pause leaves the queued SQL delivery unacknowledged');
|
|
43
43
|
const resumed = h.cli(null, ['profile', 'resume', profile.profileId]);
|
|
44
44
|
assert.equal(resumed.code, 0, resumed.stderr);
|
|
45
45
|
await h.awaitFleetExit(child);
|
|
@@ -47,7 +47,7 @@ test('profile pause backgrounds real bash, retains its inbox cursor, and recover
|
|
|
47
47
|
assert.equal(existsSync(job.jobRun), false, 'the foreground valve ownership is gone');
|
|
48
48
|
await h.waitFor(() => existsSync(job.jobExit), { label: 'background bash completion' });
|
|
49
49
|
assert.equal(readFileSync(job.jobExit, 'utf8').trim(), '0', 'the handoff command completed after its broker exited');
|
|
50
|
-
assert.equal(
|
|
50
|
+
assert.equal(acknowledgedNodeMailCount(child), 0, 'neither retained message nor background completion acknowledges the stopped broker delivery');
|
|
51
51
|
await h.tick();
|
|
52
52
|
assert.equal(h.bootCount(child), 1, 'the first resumed policy pass only consumes the pause interruption');
|
|
53
53
|
assert.equal(getRow(child)?.respawn_failures, 0, 'the intentional SIGTERM never counts as a crash');
|
|
@@ -21,13 +21,10 @@
|
|
|
21
21
|
import { test } from 'node:test';
|
|
22
22
|
import assert from 'node:assert/strict';
|
|
23
23
|
import { spawn } from 'node:child_process';
|
|
24
|
-
import { utimesSync } from 'node:fs';
|
|
25
|
-
import { join } from 'node:path';
|
|
26
24
|
import { createHeadlessHarness } from '../helpers/harness.js';
|
|
27
|
-
import { getRow
|
|
28
|
-
import {
|
|
25
|
+
import { getRow } from '../../canvas/canvas.js';
|
|
26
|
+
import { abortExecution, beginExecution, recordExecutionPid } from '../../canvas/executions.js';
|
|
29
27
|
import { isPidAlive } from '../../canvas/pid.js';
|
|
30
|
-
import { markBusy } from '../../runtime/busy.js';
|
|
31
28
|
import { BrokerSupervisionReconciler, yieldStallVerdict, YIELD_STALL_GRACE_MS } from '../../../daemon/reconcilers/broker-supervision.js';
|
|
32
29
|
// D4a — the pure refresh-stall truth table.
|
|
33
30
|
test('D4a yieldStallVerdict: kill ONLY a live, not-busy, refresh engine past the grace', () => {
|
|
@@ -61,7 +58,8 @@ test('D4b daemon force-recycles a stuck refresh: a live, not-busy refresh engine
|
|
|
61
58
|
// A live broker records its launch-time identity with its pid. The force
|
|
62
59
|
// signal re-verifies that identity, so this fixture must model the
|
|
63
60
|
// production record rather than a legacy identity-less row.
|
|
64
|
-
|
|
61
|
+
const executionId = beginExecution(id, { daemonEpoch: h.fleet.epoch() });
|
|
62
|
+
recordExecutionPid(executionId, id, enginePid);
|
|
65
63
|
// Post-cutover the live-fleet pass iterates fleet ENTRIES, not rows: a
|
|
66
64
|
// live broker IS a fleet entry (design D-1). Model the launch-time
|
|
67
65
|
// registration the production host performs, with a never-resolving
|
|
@@ -103,8 +101,8 @@ test('D4c yield escalation is scoped to the broker that received SIGTERM', { tim
|
|
|
103
101
|
pi_pid: original.pid,
|
|
104
102
|
pi_session_id: 'sess-d4c',
|
|
105
103
|
});
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
const originalExecution = beginExecution(id, { daemonEpoch: h.fleet.epoch() });
|
|
105
|
+
recordExecutionPid(originalExecution, id, original.pid);
|
|
108
106
|
h.fleet.register(id, { pid: original.pid, exited: new Promise(() => { }) });
|
|
109
107
|
const supervisor = new BrokerSupervisionReconciler();
|
|
110
108
|
const context = () => ({
|
|
@@ -119,15 +117,14 @@ test('D4c yield escalation is scoped to the broker that received SIGTERM', { tim
|
|
|
119
117
|
timeoutMs: 10_000,
|
|
120
118
|
});
|
|
121
119
|
h.fleet.forget(id);
|
|
122
|
-
|
|
123
|
-
utimesSync(join(jobDir(id), 'busy'), T_KILL / 1_000, T_KILL / 1_000);
|
|
120
|
+
abortExecution(originalExecution, { reason: 'test replacement' });
|
|
124
121
|
replacement = spawn(process.execPath, ['-e', 'process.on("SIGTERM", () => {}); console.log("ready"); setInterval(() => {}, 1000)'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
125
122
|
await new Promise((resolve, reject) => {
|
|
126
123
|
replacement.stdout.once('data', () => resolve());
|
|
127
124
|
replacement.once('error', reject);
|
|
128
125
|
});
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const replacementExecution = beginExecution(id, { daemonEpoch: h.fleet.epoch() });
|
|
127
|
+
recordExecutionPid(replacementExecution, id, replacement.pid);
|
|
131
128
|
h.fleet.register(id, { pid: replacement.pid, exited: new Promise(() => { }) });
|
|
132
129
|
const replacementStarted = T_KILL + KILL_ESCALATE_MS + 1;
|
|
133
130
|
await supervisor.run(replacementStarted, context());
|
|
@@ -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,8 +93,14 @@ 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
|
|
@@ -84,7 +124,7 @@ test('external child birth is deferred, persists its creator, and renders its ag
|
|
|
84
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');
|
|
@@ -107,9 +147,9 @@ test('external child birth is deferred, persists its creator, and renders its ag
|
|
|
107
147
|
}
|
|
108
148
|
}
|
|
109
149
|
await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
|
|
110
|
-
assert.equal(
|
|
150
|
+
assert.equal(mailFor('A').length, 1, 'a parent-created child stays silent');
|
|
111
151
|
await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: null });
|
|
112
|
-
assert.equal(
|
|
152
|
+
assert.equal(mailFor('A').length, 1, 'a root never announces');
|
|
113
153
|
});
|
|
114
154
|
test('independent root: provenance only, no parent, no subscription', async () => {
|
|
115
155
|
createNode(spawner('A'));
|
|
@@ -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/);
|