gentle-pi 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -18
- package/assets/agents/gentle-ai-explore.md +11 -4
- package/assets/agents/gentle-ai-verify.md +23 -4
- package/assets/agents/gentle-ai-worker.md +6 -6
- package/assets/install-wizard/index.html +47 -0
- package/assets/install-wizard/wizard.css +1279 -0
- package/assets/install-wizard/wizard.js +951 -0
- package/assets/orchestrator-delegation.md +70 -163
- package/assets/orchestrator-memory.md +11 -4
- package/assets/orchestrator-prompts.md +48 -0
- package/assets/orchestrator-tracking.md +53 -0
- package/assets/orchestrator-verification.md +28 -0
- package/assets/orchestrator-writer.md +38 -0
- package/assets/orchestrator.md +29 -25
- package/assets/sounds/LICENSE.md +52 -0
- package/assets/sounds/attention.wav +0 -0
- package/assets/sounds/error.wav +0 -0
- package/assets/sounds/success.wav +0 -0
- package/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +708 -22
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +399 -0
- package/docs/delegated-verification.md +3 -5
- package/docs/gentle-agents-activity.md +513 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +90 -33
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +1 -1
- package/docs/permission-modes.md +127 -0
- package/docs/prompt-history.md +1 -1
- package/docs/readme-reference.md +310 -47
- package/docs/render-cache-performance.md +28 -0
- package/docs/session-profile-format.md +39 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/yolo-mode.md +11 -10
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/child-context.ts +11 -2
- package/extensions/child-safety.ts +4 -2
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +718 -87
- package/extensions/gentle-ai.ts +991 -263
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +271 -80
- package/extensions/gentle-todo.ts +123 -18
- package/extensions/history/index.ts +11 -6
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +91 -21
- package/extensions/skill-registry.ts +175 -46
- package/extensions/startup-banner.ts +11 -2
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +40 -1
- package/lib/agents-runner.ts +90 -15
- package/lib/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +459 -0
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +484 -0
- package/lib/auto-permission-tool-context.ts +61 -0
- package/lib/background-jobs-tools.ts +202 -0
- package/lib/background-jobs.ts +253 -0
- package/lib/background-monitor.ts +154 -0
- package/lib/bounded-writer-admission.ts +83 -10
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +14 -4
- package/lib/command-palette-catalog.ts +3 -2
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +104 -28
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-shell-bundled.ts +535 -0
- package/lib/gentle-shell-launcher.ts +75 -24
- package/lib/gentle-shell-startup-flow.ts +54 -0
- package/lib/gentle-shell-startup-update.ts +92 -0
- package/lib/gentle-shell-uninstall.ts +840 -0
- package/lib/gentle-shell-update-panel.ts +231 -0
- package/lib/herdr-activity.ts +183 -0
- package/lib/inprocess-reviewer.ts +3 -0
- package/lib/jobs-sidebar-state.ts +20 -0
- package/lib/jobs-view.ts +161 -0
- package/lib/model-price-ratio.ts +83 -0
- package/lib/model-routing-authority.ts +6 -1
- package/lib/nan-provider.ts +33 -11
- package/lib/native-review-cli.ts +194 -73
- package/lib/notification-audio-native.ts +162 -0
- package/lib/notification-audio-windows.ts +399 -0
- package/lib/notification-audio.ts +332 -0
- package/lib/notification-customize.ts +241 -0
- package/lib/notification-events.ts +113 -0
- package/lib/notification-policy.ts +148 -0
- package/lib/notification-pulse-client.ts +367 -0
- package/lib/notification-pulse-protocol.ts +305 -0
- package/lib/notification-pulse-stream.ts +213 -0
- package/lib/notification-pulse-worker.ts +66 -0
- package/lib/notification-scheduler.ts +173 -0
- package/lib/notification-service.ts +202 -0
- package/lib/notification-ui.ts +87 -0
- package/lib/odd-phase.ts +1 -1
- package/lib/orchestrator-alias.ts +20 -0
- package/lib/orchestrator-catalog.ts +79 -0
- package/lib/orchestrator-consultation.ts +61 -0
- package/lib/orchestrator-discovery.ts +49 -0
- package/lib/orchestrator-helper-consent.ts +122 -0
- package/lib/orchestrator-helper.ts +167 -0
- package/lib/orchestrator-message-card.ts +105 -0
- package/lib/orchestrator-presence.ts +115 -14
- package/lib/orchestrator-scope.ts +80 -0
- package/lib/orchestrator-state.ts +107 -0
- package/lib/orchestrator-tool-card.ts +280 -0
- package/lib/orchestrator-work-search.ts +218 -0
- package/lib/orchestrator-work.ts +79 -0
- package/lib/permission-mode-policy.ts +59 -0
- package/lib/review-candidate-view-owner.ts +8 -6
- package/lib/review-candidate-view.ts +3 -2
- package/lib/review-compact-contract.ts +2 -2
- package/lib/review-host-relay.ts +7 -2
- package/lib/review-integration-v2.ts +100 -22
- package/lib/review-publication-gate.ts +1 -1
- package/lib/review-risk-assessment.ts +77 -51
- package/lib/review-sidebar-state.ts +2 -1
- package/lib/review-transaction.ts +72 -3
- package/lib/runtime-metrics-children.ts +1 -1
- package/lib/runtime-metrics-native.ts +3 -2
- package/lib/selection-engine.ts +22 -7
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +261 -0
- package/lib/session-profile-persistence.ts +187 -0
- package/lib/session-worktree-registry.ts +74 -8
- package/lib/shell-bar.ts +41 -11
- package/lib/shell-changes-view.ts +40 -13
- package/lib/shell-changes.ts +88 -71
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-sidebar-layout.ts +5 -1
- package/lib/shell-todo.ts +81 -26
- package/lib/shell-usage.ts +4 -3
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/vim-editor-adapter.ts +27 -5
- package/lib/visual-customization-policy.ts +20 -9
- package/lib/visual-customize-view.ts +171 -20
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +198 -121
- package/package.json +5 -5
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +536 -0
- package/runtime/gentle-shell-launcher.mjs +74 -23
- package/runtime/gentle-shell-startup-flow.mjs +55 -0
- package/runtime/gentle-shell-startup-update.mjs +93 -0
- package/runtime/gentle-shell-uninstall.mjs +841 -0
- package/runtime/gentle-shell-update-panel.mjs +232 -0
- package/runtime/native-review-cli.mjs +191 -70
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +77 -51
- package/runtime/status-timing-diagnostics.mjs +202 -0
- package/runtime/telemetry-trigger.mjs +3 -1
- package/scripts/bootstrap.cmd +255 -0
- package/scripts/bootstrap.sh +168 -0
- package/scripts/build-distribution.mjs +157 -0
- package/scripts/build-installer-bundles.mjs +136 -0
- package/scripts/build-runtime-modules.mjs +7 -0
- package/scripts/bundled-install.mjs +1308 -0
- package/scripts/bundled-wizard.mjs +399 -0
- package/scripts/check-types.mjs +4 -6
- package/scripts/gentle-ai-installer.mjs +79 -32
- package/scripts/install-gentle-ai.mjs +48 -1
- package/scripts/install-tui-mode-setting.mjs +10 -1
- package/scripts/install-wizard-preview.mjs +154 -0
- package/scripts/installer-downloads.mjs +648 -0
- package/scripts/installer-preflight.mjs +387 -0
- package/scripts/installer-probes.mjs +564 -0
- package/scripts/installer-runner.mjs +1102 -0
- package/scripts/installer-server.mjs +881 -0
- package/scripts/installer-windows-artifacts.json +15 -0
- package/scripts/installer-windows.mjs +752 -0
- package/scripts/main-channel.mjs +452 -0
- package/scripts/npm-pack-result.mjs +31 -0
- package/scripts/pi-update.mjs +223 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +22 -8
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +23 -5
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +82 -18
- package/skills/gentle-ai/SKILL.md +12 -11
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +12 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-protocol.test.ts +57 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +89 -4
- package/tests/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +276 -0
- package/tests/auto-mode-sdk.test.ts +84 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +634 -0
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +454 -0
- package/tests/autonomous-guard.test.ts +86 -16
- package/tests/background-cache-warming.test.ts +2 -1
- package/tests/background-jobs.test.ts +227 -0
- package/tests/background-monitor.test.ts +195 -0
- package/tests/background-subagents-default-mode.test.ts +28 -5
- package/tests/bounded-stop-contract.test.ts +85 -0
- package/tests/bounded-writer-admission.test.ts +52 -1
- package/tests/bridge-wake-lifecycle.test.ts +129 -0
- package/tests/bridge-wake-rendering.test.ts +56 -0
- package/tests/build-distribution.test.ts +269 -0
- package/tests/builtin-codemode-optout.test.ts +242 -0
- package/tests/bundled-install.test.ts +1319 -0
- package/tests/bundled-wizard.test.ts +505 -0
- package/tests/card-content-policy.test.ts +68 -0
- package/tests/card-style-policy.test.ts +34 -2
- package/tests/child-codegraph-bootstrap.test.ts +74 -0
- package/tests/child-package-entrypoints.test.ts +65 -0
- package/tests/child-package-injection.test.ts +74 -0
- package/tests/child-safety.test.ts +12 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +107 -2
- package/tests/command-palette.test.ts +5 -5
- package/tests/context-threshold.test.ts +31 -0
- package/tests/delegate-for-reason-contract.test.ts +327 -0
- package/tests/delegated-key-learnings-contract.test.ts +3 -2
- package/tests/devbinary/native-review-parity.devtest.ts +1 -1
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +316 -0
- package/tests/generic-agent-tools.test.ts +16 -1
- package/tests/gentle-agents.test.ts +1895 -76
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +19 -2
- package/tests/gentle-ai-child-guards.test.ts +202 -0
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
- package/tests/gentle-ai-installer.test.ts +548 -58
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +6 -6
- package/tests/gentle-ai.test.ts +999 -31
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +263 -19
- package/tests/gentle-shell-bundled.test.ts +1036 -0
- package/tests/gentle-shell-launcher.test.ts +251 -25
- package/tests/gentle-shell-startup-flow.test.ts +75 -0
- package/tests/gentle-shell-startup-update.test.ts +85 -0
- package/tests/gentle-shell-uninstall.test.ts +978 -0
- package/tests/gentle-shell-update-panel.test.ts +158 -0
- package/tests/gentle-shell.test.ts +611 -100
- package/tests/gentle-todo.test.ts +317 -14
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/inprocess-reviewer.test.ts +20 -0
- package/tests/install-gentle-ai-failure.test.ts +69 -0
- package/tests/install-tui-mode-setting.test.ts +15 -0
- package/tests/install-wizard.test.ts +1223 -0
- package/tests/installer-bundles.test.ts +122 -0
- package/tests/installer-posix-bootstrap.test.ts +1201 -0
- package/tests/installer-preflight.test.ts +750 -0
- package/tests/installer-probes.test.ts +1106 -0
- package/tests/installer-runner.test.ts +2159 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +395 -0
- package/tests/main-channel.test.ts +344 -0
- package/tests/maintainer/provider-relay.maintest.ts +13 -12
- package/tests/model-price-ratio.test.ts +116 -0
- package/tests/model-profiles-preservation.test.ts +153 -0
- package/tests/model-routing-package.test.ts +10 -0
- package/tests/nan-provider-runtime.test.ts +156 -0
- package/tests/nan-provider.test.ts +28 -18
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-cli.test.ts +236 -1
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +1 -1
- package/tests/native-review-parity.test.ts +12 -48
- package/tests/native-risk-evidence-parity.test.ts +21 -0
- package/tests/no-self-pause-contract.test.ts +87 -0
- package/tests/notification-audio-native.test.ts +281 -0
- package/tests/notification-audio-windows.test.ts +581 -0
- package/tests/notification-audio.test.ts +548 -0
- package/tests/notification-customize.test.ts +655 -0
- package/tests/notification-events.test.ts +115 -0
- package/tests/notification-policy.test.ts +201 -0
- package/tests/notification-pulse-client.test.ts +356 -0
- package/tests/notification-pulse-protocol.test.ts +213 -0
- package/tests/notification-pulse-stream.test.ts +320 -0
- package/tests/notification-scheduler.test.ts +258 -0
- package/tests/notification-ui.test.ts +157 -0
- package/tests/npm-pack-result.test.ts +41 -0
- package/tests/odd-integration.test.ts +14 -5
- package/tests/odd-routing-canonical-ratchet.test.ts +53 -12
- package/tests/odd-routing-contract.test.ts +80 -23
- package/tests/odd-runtime-delegation-gate.test.ts +4 -1
- package/tests/orchestrator-budget.test.ts +29 -6
- package/tests/orchestrator-catalog.test.ts +146 -0
- package/tests/orchestrator-consultation-sdk.test.ts +614 -0
- package/tests/orchestrator-consultation.test.ts +111 -0
- package/tests/orchestrator-discovery.test.ts +241 -0
- package/tests/orchestrator-helper-consent.test.ts +171 -0
- package/tests/orchestrator-helper.test.ts +297 -0
- package/tests/orchestrator-message-card.test.ts +190 -0
- package/tests/orchestrator-presence.test.ts +23 -0
- package/tests/orchestrator-rdd-ownership.test.ts +6 -5
- package/tests/orchestrator-scope.test.ts +107 -0
- package/tests/orchestrator-state.test.ts +114 -0
- package/tests/orchestrator-status-publication-contract.test.ts +34 -0
- package/tests/orchestrator-tool-card.test.ts +398 -0
- package/tests/orchestrator-work-search.test.ts +318 -0
- package/tests/orchestrator-work.test.ts +147 -0
- package/tests/package-manifest.test.ts +105 -21
- package/tests/packed-runner-owned-path.test.ts +35 -1
- package/tests/parallel-reason-contract.test.ts +21 -0
- package/tests/parallel-writer-admission.test.ts +210 -0
- package/tests/permission-mode-policy.test.ts +194 -0
- package/tests/persona-single-channel.test.ts +4 -12
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/pi-update-launcher.test.ts +87 -0
- package/tests/pi-update.test.ts +186 -0
- package/tests/provider-defect-handoff.test.ts +4 -3
- package/tests/quiet-tool-rendering.test.ts +160 -3
- package/tests/rdd-aware-verification-contract.test.ts +29 -41
- package/tests/rdd-status-line.test.ts +52 -0
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/review-agent-end-preflight.test.ts +62 -10
- package/tests/review-candidate-view.test.ts +101 -12
- package/tests/review-contract-prompt.test.ts +33 -4
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +255 -1
- package/tests/review-controller-workspace-root.test.ts +43 -2
- package/tests/review-controller.test.ts +2 -2
- package/tests/review-correction-lifecycle.test.ts +2 -1
- package/tests/review-gate.test.ts +7 -4
- package/tests/review-graph-schema.test.ts +2 -2
- package/tests/review-host-relay-routing.test.ts +5 -3
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +21 -1
- package/tests/review-json-arguments.test.ts +167 -0
- package/tests/review-last-event-closure.test.ts +157 -8
- package/tests/review-ledger-contract.test.ts +5 -4
- package/tests/review-policy-judgment-day.test.ts +1 -1
- package/tests/review-policy-ordinary.test.ts +2 -2
- package/tests/review-relay-transport-agent.test.ts +66 -1
- package/tests/review-risk-assessment.test.ts +109 -33
- package/tests/review-session-standing-permission-controller.test.ts +16 -17
- package/tests/review-session-standing-permission-ipc.test.ts +6 -5
- package/tests/review-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +83 -5
- package/tests/runtime-harness.mjs +42 -15
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +65 -5
- package/tests/session-change-capture.test.ts +10 -10
- package/tests/session-changes-shell.test.ts +242 -48
- package/tests/session-changes.test.ts +17 -24
- package/tests/session-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +394 -0
- package/tests/session-profile-persistence.test.ts +334 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +57 -2
- package/tests/shell-bar.test.ts +40 -0
- package/tests/shell-changes-view.test.ts +85 -34
- package/tests/shell-changes.test.ts +79 -62
- package/tests/shell-render-cache-runtime.test.ts +69 -0
- package/tests/shell-render-cache.test.ts +129 -0
- package/tests/shell-sidebar-layout.test.ts +27 -2
- package/tests/shell-todo.test.ts +132 -11
- package/tests/shell-usage-view.test.ts +1 -1
- package/tests/shell-usage.test.ts +35 -0
- package/tests/short-check-routing-contract.test.ts +49 -0
- package/tests/skill-registry-watcher.test.ts +119 -0
- package/tests/skill-registry.test.ts +311 -5
- package/tests/startup-banner.test.ts +117 -3
- package/tests/startup-home-repo.test.ts +170 -0
- package/tests/status-timing-diagnostics.test.ts +283 -0
- package/tests/support/orchestrator-modules.ts +19 -0
- package/tests/task-size-routing-contract.test.ts +241 -0
- package/tests/telemetry-trigger.test.ts +27 -1
- package/tests/test-strength-contract.test.ts +124 -0
- package/tests/verify-agent-contract.test.ts +90 -0
- package/tests/verify-first-launch-contract.test.ts +139 -0
- package/tests/verify-package-files.test.ts +56 -0
- package/tests/vim-editor-adapter.test.ts +47 -21
- package/tests/visual-customization-policy.test.ts +32 -3
- package/tests/visual-customize-view.test.ts +288 -10
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +18 -3
- package/tests/yolo-mode-runtime.test.ts +37 -10
- package/tests/yolo-mode.test.ts +38 -5
- package/tests/yolo-session-policy.test.ts +1 -1
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
|
@@ -67,21 +67,38 @@ test("native reclaim and recover preserve their exact authority bindings", async
|
|
|
67
67
|
]);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
test("native
|
|
70
|
+
test("native ABANDON rejects free-text reasons before launching native", async () => {
|
|
71
|
+
const queue = queuedAdapter([]);
|
|
71
72
|
const request = {
|
|
72
|
-
cwd: "/repo",
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
snapshotIdentity: SHA,
|
|
76
|
-
capturedLensResults: ["00-risk.json"],
|
|
77
|
-
findingsPresent: true,
|
|
78
|
-
actor: "maintainer",
|
|
79
|
-
reason: "discard candidate",
|
|
73
|
+
cwd: "/repo", lineage: "abandoned", expectedRevision: "revision-1", snapshotIdentity: SHA,
|
|
74
|
+
capturedLensResults: ["00-risk.json"], findingsPresent: true, actor: "maintainer",
|
|
75
|
+
reason: "discard candidate" as unknown as import("../lib/native-review-cli.ts").NativeReviewAbandonRequest["reason"],
|
|
80
76
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
await assert.rejects(
|
|
78
|
+
() => client(queue.adapter).abandon({ ...request, maintainerAuthorization: nativeReviewAbandonAuthorization(request) }),
|
|
79
|
+
/Native ABANDON reason must be "operator_disposition" or "retired_schema"/,
|
|
80
|
+
);
|
|
81
|
+
assert.equal(queue.calls.length, 0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("native abandon carries the exact discarded-work authorization and audit record", async () => {
|
|
85
|
+
for (const reason of ["operator_disposition", "retired_schema"] as const) {
|
|
86
|
+
const request = {
|
|
87
|
+
cwd: "/repo",
|
|
88
|
+
lineage: "abandoned",
|
|
89
|
+
expectedRevision: "revision-1",
|
|
90
|
+
snapshotIdentity: SHA,
|
|
91
|
+
capturedLensResults: ["00-risk.json"],
|
|
92
|
+
findingsPresent: true,
|
|
93
|
+
actor: "maintainer",
|
|
94
|
+
reason,
|
|
95
|
+
};
|
|
96
|
+
const queue = queuedAdapter([{ stdout: JSON.stringify({ operation: "review/abandon", record: { schema: "gentle-ai.review-reclaim-audit/v1", lineage_id: "abandoned", status: "committed" } }) }]);
|
|
97
|
+
const result = await client(queue.adapter).abandon({ ...request, maintainerAuthorization: nativeReviewAbandonAuthorization(request) });
|
|
98
|
+
assert.equal(result.record.status, "committed");
|
|
99
|
+
assert.deepEqual(queue.calls[0]?.arguments.slice(0, 10), ["review", "abandon", "--cwd", "/repo", "--lineage", "abandoned", "--expected-revision", "revision-1", "--actor", "maintainer"]);
|
|
100
|
+
assert.deepEqual(queue.calls[0]?.arguments.slice(10, 12), ["--reason", reason]);
|
|
101
|
+
}
|
|
85
102
|
});
|
|
86
103
|
|
|
87
104
|
// gentle-ai 0ed9225f ("close on the last causal event") removed evidence records
|
|
@@ -238,10 +255,26 @@ test("native recovery operations reject malformed canonical inputs before a proc
|
|
|
238
255
|
assert.equal(queue.calls.length, 0);
|
|
239
256
|
});
|
|
240
257
|
|
|
258
|
+
test("native RECOVER rejects malformed or unacknowledged committed selectors before launch", async () => {
|
|
259
|
+
const queue = queuedAdapter([]);
|
|
260
|
+
const request = { cwd: "/repo", predecessorLineage: "predecessor", expectedPredecessorRevision: "revision-1", successorLineage: "successor", disposition: "invalidated" as const, actor: "maintainer", reason: "recover authority" };
|
|
261
|
+
for (const selectors of [
|
|
262
|
+
{ baseRef: " base", committedOnly: true },
|
|
263
|
+
{ baseRef: "base\u0000", committedOnly: true },
|
|
264
|
+
{ baseRef: "base" },
|
|
265
|
+
{ baseRef: "base", committedOnly: false },
|
|
266
|
+
{ committedOnly: true },
|
|
267
|
+
{ committedOnly: false },
|
|
268
|
+
]) {
|
|
269
|
+
await assert.rejects(() => client(queue.adapter).recover({ ...request, ...selectors }), /Native RECOVER (baseRef|committedOnly)/);
|
|
270
|
+
}
|
|
271
|
+
assert.equal(queue.calls.length, 0);
|
|
272
|
+
});
|
|
273
|
+
|
|
241
274
|
test("partial maintenance failures preserve the provider audit record and unknown mutation outcome", async () => {
|
|
242
275
|
const request = {
|
|
243
276
|
cwd: "/repo", lineage: "abandoned", expectedRevision: "revision-1", snapshotIdentity: SHA,
|
|
244
|
-
capturedLensResults: ["00-risk.json"], findingsPresent: true, actor: "maintainer", reason: "
|
|
277
|
+
capturedLensResults: ["00-risk.json"], findingsPresent: true, actor: "maintainer", reason: "operator_disposition" as const,
|
|
245
278
|
};
|
|
246
279
|
const queue = queuedAdapter([{ exitCode: 1, stdout: JSON.stringify({ operation: "review/abandon", record: { schema: "gentle-ai.review-reclaim-audit/v1", lineage_id: request.lineage, status: "partial" } }) }]);
|
|
247
280
|
await assert.rejects(
|
|
@@ -290,7 +323,7 @@ test("native abandon rejects a legacy authorization before process launch", asyn
|
|
|
290
323
|
capturedLensResults: ["00-risk.json"],
|
|
291
324
|
findingsPresent: true,
|
|
292
325
|
actor: "maintainer",
|
|
293
|
-
reason: "
|
|
326
|
+
reason: "operator_disposition" as const,
|
|
294
327
|
};
|
|
295
328
|
const queue = queuedAdapter([]);
|
|
296
329
|
await assert.rejects(
|
|
@@ -332,12 +365,145 @@ function interactiveContext(confirm: boolean): ExtensionContext {
|
|
|
332
365
|
return { cwd: process.cwd(), hasUI: true, ui: { confirm: async () => confirm } } as unknown as ExtensionContext;
|
|
333
366
|
}
|
|
334
367
|
|
|
368
|
+
const SHA2 = `sha256:${"b".repeat(64)}`;
|
|
369
|
+
|
|
370
|
+
function abandonedInventoryNative(calls: Array<Record<string, unknown>>): import("../lib/native-review-cli.ts").NativeReviewCli {
|
|
371
|
+
return {
|
|
372
|
+
reviewStatus: async () => ({
|
|
373
|
+
repository: "/canonical/repository",
|
|
374
|
+
complete: true,
|
|
375
|
+
authoritative: true,
|
|
376
|
+
entries: [
|
|
377
|
+
{ version: "legacy-v1", status: "invalid", path: "/authority/legacy", problems: [] },
|
|
378
|
+
{ version: "compact-v2", status: "active", path: "/authority/compact", lineageId: "stranded", revision: SHA, snapshotIdentity: SHA2, state: "correction_required", discardedWork: { capturedLensResults: ["00-review-risk", "01-review-resilience"], findingsPresent: true } },
|
|
379
|
+
],
|
|
380
|
+
}),
|
|
381
|
+
abandon: async (request: Record<string, unknown>) => { calls.push(request); return { record: { schema: "gentle-ai.review-reclaim-audit/v1", lineage_id: "stranded", status: "committed" } }; },
|
|
382
|
+
} as unknown as import("../lib/native-review-cli.ts").NativeReviewCli;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
test("ABANDON rejects free-text reasons before inventory, confirmation, or mutation", async () => {
|
|
386
|
+
let statusCalls = 0;
|
|
387
|
+
let confirmCalls = 0;
|
|
388
|
+
let abandonCalls = 0;
|
|
389
|
+
const native = {
|
|
390
|
+
reviewStatus: async () => { statusCalls += 1; throw new Error("must not read inventory"); },
|
|
391
|
+
abandon: async () => { abandonCalls += 1; throw new Error("must not abandon"); },
|
|
392
|
+
} as unknown as import("../lib/native-review-cli.ts").NativeReviewCli;
|
|
393
|
+
const context = { hasUI: true, ui: { confirm: async () => { confirmCalls += 1; return true; } } } as unknown as ExtensionContext;
|
|
394
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "discard candidate" }) }, process.cwd(), native, undefined, undefined, context);
|
|
395
|
+
assert.equal(result.outcome, "native-input-invalid");
|
|
396
|
+
assert.deepEqual(result.allowed_reasons, ["operator_disposition", "retired_schema"]);
|
|
397
|
+
assert.equal(result.mutation_performed, false);
|
|
398
|
+
assert.equal(result.mutation_outcome, "none");
|
|
399
|
+
assert.equal(statusCalls, 0);
|
|
400
|
+
assert.equal(confirmCalls, 0);
|
|
401
|
+
assert.equal(abandonCalls, 0);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test("ABANDON derives its discarded-work inputs from fresh native inventory", async () => {
|
|
405
|
+
const calls: Array<Record<string, unknown>> = [];
|
|
406
|
+
const native = abandonedInventoryNative(calls);
|
|
407
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
408
|
+
assert.equal(result.mutation_outcome, "committed");
|
|
409
|
+
assert.equal(calls.length, 1);
|
|
410
|
+
assert.equal(calls[0]?.lineage, "stranded");
|
|
411
|
+
assert.equal(calls[0]?.expectedRevision, SHA);
|
|
412
|
+
assert.equal(calls[0]?.snapshotIdentity, SHA2);
|
|
413
|
+
assert.deepEqual(calls[0]?.capturedLensResults, ["00-review-risk", "01-review-resilience"]);
|
|
414
|
+
assert.equal(calls[0]?.findingsPresent, true);
|
|
415
|
+
assert.equal(calls[0]?.maintainerAuthorization, `gentle-ai.review-abandon-authorization/v2\nlineage=stranded\nrevision=${SHA}\nsnapshot_identity=${SHA2}\nreason=operator_disposition\ncaptured_lens_results=00-review-risk,01-review-resilience\nfindings_present=true\nactor=maintainer`);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("ABANDON accepts the top-level lineageId and requests only actor and reason", async () => {
|
|
419
|
+
const calls: Array<Record<string, unknown>> = [];
|
|
420
|
+
const native = abandonedInventoryNative(calls);
|
|
421
|
+
const missing = await __testing.executeReviewControllerOperation({ operation: "abandon", lineageId: "stranded", input: JSON.stringify({}) }, process.cwd(), native);
|
|
422
|
+
assert.equal(missing.outcome, "native-input-required");
|
|
423
|
+
assert.deepEqual(missing.missing_input, ["actor", "reason"]);
|
|
424
|
+
const committed = await __testing.executeReviewControllerOperation({ operation: "abandon", lineageId: "stranded", input: JSON.stringify({ actor: "maintainer", reason: "retired_schema" }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
425
|
+
assert.equal(committed.mutation_outcome, "committed");
|
|
426
|
+
assert.equal(calls[0]?.lineage, "stranded");
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
test("ABANDON rejects caller-supplied inventory-derived fields without mutating", async () => {
|
|
430
|
+
const calls: Array<Record<string, unknown>> = [];
|
|
431
|
+
const native = abandonedInventoryNative(calls);
|
|
432
|
+
const injected = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition", expectedRevision: SHA, snapshotIdentity: SHA2, capturedLensResults: ["00-review-risk"], findingsPresent: true }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
433
|
+
assert.equal(injected.outcome, "native-input-invalid");
|
|
434
|
+
const authorization = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition", maintainerAuthorization: "forged" }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
435
|
+
assert.equal(authorization.outcome, "native-input-invalid");
|
|
436
|
+
assert.equal(calls.length, 0);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test("ABANDON blocks when no eligible compact-v2 entry carries discarded work", async () => {
|
|
440
|
+
const native = {
|
|
441
|
+
reviewStatus: async () => ({
|
|
442
|
+
repository: "/canonical/repository",
|
|
443
|
+
complete: true,
|
|
444
|
+
authoritative: true,
|
|
445
|
+
entries: [{ version: "compact-v2", status: "active", path: "/authority/compact", lineageId: "stranded", revision: SHA, snapshotIdentity: SHA2, state: "terminal", problems: [] }],
|
|
446
|
+
}),
|
|
447
|
+
abandon: async () => { throw new Error("abandon must not run for an ineligible lineage"); },
|
|
448
|
+
} as unknown as import("../lib/native-review-cli.ts").NativeReviewCli;
|
|
449
|
+
const ineligible = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
450
|
+
assert.equal(ineligible.outcome, "native-abandon-ineligible");
|
|
451
|
+
assert.equal(ineligible.mutation_performed, false);
|
|
452
|
+
const incomplete = {
|
|
453
|
+
reviewStatus: async () => ({ repository: "/canonical/repository", complete: false, authoritative: false, entries: [] }),
|
|
454
|
+
abandon: async () => { throw new Error("abandon must not run for an incomplete inventory"); },
|
|
455
|
+
} as unknown as import("../lib/native-review-cli.ts").NativeReviewCli;
|
|
456
|
+
const blocked = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) }, process.cwd(), incomplete, undefined, undefined, interactiveContext(true));
|
|
457
|
+
assert.equal(blocked.outcome, "native-abandon-ineligible");
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test("ABANDON rechecks the derived authority after approval and fails closed on drift", async () => {
|
|
461
|
+
let reads = 0;
|
|
462
|
+
const native = {
|
|
463
|
+
reviewStatus: async () => {
|
|
464
|
+
reads += 1;
|
|
465
|
+
return reads === 1
|
|
466
|
+
? {
|
|
467
|
+
repository: "/canonical/repository",
|
|
468
|
+
complete: true,
|
|
469
|
+
authoritative: true,
|
|
470
|
+
entries: [{ version: "compact-v2", status: "active", path: "/authority/compact", lineageId: "stranded", revision: SHA, snapshotIdentity: SHA2, state: "correction_required", discardedWork: { capturedLensResults: ["00-review-risk", "01-review-resilience"], findingsPresent: true } }],
|
|
471
|
+
}
|
|
472
|
+
: {
|
|
473
|
+
repository: "/canonical/repository",
|
|
474
|
+
complete: true,
|
|
475
|
+
authoritative: true,
|
|
476
|
+
entries: [{ version: "compact-v2", status: "active", path: "/authority/compact", lineageId: "stranded", revision: `sha256:${"c".repeat(64)}`, snapshotIdentity: SHA2, state: "correction_required", discardedWork: { capturedLensResults: ["00-review-risk", "01-review-resilience"], findingsPresent: true } }],
|
|
477
|
+
};
|
|
478
|
+
},
|
|
479
|
+
abandon: async () => { throw new Error("must not abandon a drifted authority"); },
|
|
480
|
+
} as unknown as import("../lib/native-review-cli.ts").NativeReviewCli;
|
|
481
|
+
const drifted = await __testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
482
|
+
assert.equal(drifted.outcome, "native-abandon-authority-changed");
|
|
483
|
+
assert.equal(drifted.mutation_performed, false);
|
|
484
|
+
assert.equal(reads, 2);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("ABANDON fails closed headlessly and on declined approval", async () => {
|
|
488
|
+
const calls: Array<Record<string, unknown>> = [];
|
|
489
|
+
const native = abandonedInventoryNative(calls);
|
|
490
|
+
await assert.rejects(
|
|
491
|
+
__testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) }, process.cwd(), native),
|
|
492
|
+
/interactive Pi UI.*fails closed/i,
|
|
493
|
+
);
|
|
494
|
+
await assert.rejects(
|
|
495
|
+
__testing.executeReviewControllerOperation({ operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) }, process.cwd(), native, undefined, undefined, interactiveContext(false)),
|
|
496
|
+
/not explicitly authorized/,
|
|
497
|
+
);
|
|
498
|
+
assert.equal(calls.length, 0);
|
|
499
|
+
});
|
|
500
|
+
|
|
335
501
|
test("maintenance cancellation preserves the exact signal and unknown mutation outcome", async () => {
|
|
336
502
|
const controller = new AbortController();
|
|
337
503
|
const request = {
|
|
338
504
|
cwd: "/repo", lineage: "abandoned", expectedRevision: "revision-1", snapshotIdentity: SHA,
|
|
339
505
|
capturedLensResults: ["00-risk.json"], findingsPresent: true,
|
|
340
|
-
actor: "maintainer", reason: "
|
|
506
|
+
actor: "maintainer", reason: "operator_disposition" as const,
|
|
341
507
|
};
|
|
342
508
|
const adapter: ExecFileAdapter = async (call) => {
|
|
343
509
|
assert.equal(call.signal, controller.signal);
|
|
@@ -397,6 +563,35 @@ test("RECOVER derives a provider-bound authorization, rechecks it, and fails clo
|
|
|
397
563
|
assert.equal(changed.mutation_outcome, "none");
|
|
398
564
|
});
|
|
399
565
|
|
|
566
|
+
test("approved RECOVER preserves the frozen committed-range selector from both STATUS reads", async () => {
|
|
567
|
+
const baseCommit = "1".repeat(40);
|
|
568
|
+
const providerBaseTree = "2".repeat(40);
|
|
569
|
+
const statuses: Array<Record<string, unknown>> = [];
|
|
570
|
+
const queue = queuedAdapter([{ stdout: JSON.stringify({ schema: "gentle-ai.review-recovery/v1" }) }]);
|
|
571
|
+
const review = client(queue.adapter);
|
|
572
|
+
const native = {
|
|
573
|
+
targetStatus: async (request: Record<string, unknown>) => { statuses.push(request); return recoveryStatus(); },
|
|
574
|
+
recover: review.recover.bind(review),
|
|
575
|
+
} as unknown as import("../lib/native-review-cli.ts").NativeReviewCli;
|
|
576
|
+
const views = {
|
|
577
|
+
hasProjection: (lineage: string, cwd: string) => lineage === "predecessor" && cwd === process.cwd(),
|
|
578
|
+
resolveProjection: () => ({ committedOnly: true, baseCommit, providerBaseTree }),
|
|
579
|
+
} as unknown as NonNullable<Parameters<typeof __testing.executeReviewControllerOperation>[4]>;
|
|
580
|
+
const input = { predecessorLineage: "predecessor", expectedPredecessorRevision: "revision-1", successorLineage: "successor", disposition: "invalidated", actor: "maintainer", reason: "recover authority" };
|
|
581
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "recover", input: JSON.stringify(input) }, process.cwd(), native, undefined, views, interactiveContext(true));
|
|
582
|
+
assert.equal(result.mutation_outcome, "committed");
|
|
583
|
+
assert.equal(statuses.length, 2);
|
|
584
|
+
for (const status of statuses) {
|
|
585
|
+
assert.equal(status.baseRef, providerBaseTree);
|
|
586
|
+
assert.equal(status.committedOnly, true);
|
|
587
|
+
}
|
|
588
|
+
const args = queue.calls[0]!.arguments;
|
|
589
|
+
assert.ok(args.includes("--base-ref"), "RECOVER must carry the STATUS base selector");
|
|
590
|
+
assert.equal(args[args.indexOf("--base-ref") + 1], providerBaseTree);
|
|
591
|
+
assert.ok(args.includes("--committed-only"));
|
|
592
|
+
assert.ok(args.includes("--maintainer-authorization"));
|
|
593
|
+
});
|
|
594
|
+
|
|
400
595
|
test("RECOVER, RESET, and RECONCILE keep provider inputs and failures authority-scoped", async () => {
|
|
401
596
|
const recoverMissing = await __testing.executeReviewControllerOperation({ operation: "recover", input: JSON.stringify({ predecessorLineage: "predecessor", disposition: "not-a-disposition" }) }, process.cwd(), {
|
|
402
597
|
recover: async () => { throw new Error("must not recover"); },
|
|
@@ -415,7 +610,7 @@ test("RECOVER, RESET, and RECONCILE keep provider inputs and failures authority-
|
|
|
415
610
|
|
|
416
611
|
for (const error of [
|
|
417
612
|
new NativeReviewCliError("cancelled", "review/reconcile-authority", true, true, "cancelled"),
|
|
418
|
-
new NativeReviewCliError("
|
|
613
|
+
new NativeReviewCliError("non-zero", "review/reconcile-authority", true, true, "partial", undefined, { schema: "gentle-ai.review-reconcile-audit/v1", status: "partial" }),
|
|
419
614
|
]) {
|
|
420
615
|
const failed = await __testing.executeReviewControllerOperation({ operation: "reconcile-authority", input: JSON.stringify(reconciliation) }, process.cwd(), {
|
|
421
616
|
reconcileAuthority: async () => { throw error; },
|
|
@@ -493,3 +688,118 @@ test("REPAIR_LEGACY_ALIAS derives its immutable target from fresh native invento
|
|
|
493
688
|
const injected = await __testing.executeReviewControllerOperation({ operation: "repair-legacy-alias", input: JSON.stringify({ lineage: "legacy-alias", actor: "maintainer", reason: "repair alias", repository: "/injected" }) }, process.cwd(), native, undefined, undefined, interactiveContext(true));
|
|
494
689
|
assert.equal(injected.outcome, "native-input-invalid");
|
|
495
690
|
});
|
|
691
|
+
|
|
692
|
+
// Adapter-level ABANDON battery (reviewer input on #1668): the registered
|
|
693
|
+
// gentle_review tool driven through the real NativeReviewCliV216 adapter, so
|
|
694
|
+
// the native status decoder and the exact abandon argv are exercised, not a
|
|
695
|
+
// faked NativeReviewCli surface.
|
|
696
|
+
const ELIGIBLE_ENTRY = { version: "compact-v2", path: ".git/gentle-ai/reviews/current.json", status: "active", lineage_id: "stranded", state: "correction_required", revision: SHA, snapshot_identity: SHA2, problems: [], discarded_work: { captured_lens_results: ["00-review-risk", "01-review-resilience"], findings_present: true } };
|
|
697
|
+
const INCOMPLETE_DUPLICATE = { version: "compact-v2", path: ".git/gentle-ai/reviews/duplicate.json", status: "incomplete-store-entry", lineage_id: "stranded", problems: [] };
|
|
698
|
+
const ABANDON_RECORD = { operation: "review/abandon", record: { schema: "gentle-ai.review-reclaim-audit/v1", lineage_id: "stranded", status: "committed" } };
|
|
699
|
+
|
|
700
|
+
function statusStdout(entries: object[], repository: string = process.cwd(), authoritative: boolean = true): string {
|
|
701
|
+
return JSON.stringify({ schema: "gentle-ai.review-authority-status/v1", operation: "review/status", repository, complete: authoritative, authoritative, status: "active", entries, locks: [], diagnostics: [] });
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const ABANDON_INPUT = { operation: "abandon", input: JSON.stringify({ lineage: "stranded", actor: "maintainer", reason: "operator_disposition" }) } as const;
|
|
705
|
+
|
|
706
|
+
test("registered ABANDON decodes authoritative inventory and dispatches the exact binding once", async () => {
|
|
707
|
+
const queue = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY]) }, { stdout: statusStdout([ELIGIBLE_ENTRY]) }, { stdout: JSON.stringify(ABANDON_RECORD) }]);
|
|
708
|
+
const controller = registeredController(client(queue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli);
|
|
709
|
+
const result = await controller.execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
710
|
+
assert.equal((result.details as { mutation_outcome?: string }).mutation_outcome, "committed");
|
|
711
|
+
assert.equal(queue.calls.length, 3);
|
|
712
|
+
assert.deepEqual(queue.calls[0]?.arguments, ["review", "status", "--cwd", process.cwd()]);
|
|
713
|
+
assert.deepEqual(queue.calls[2]?.arguments, ["review", "abandon", "--cwd", process.cwd(), "--lineage", "stranded", "--expected-revision", SHA, "--actor", "maintainer", "--reason", "operator_disposition", "--maintainer-authorization", `gentle-ai.review-abandon-authorization/v2\nlineage=stranded\nrevision=${SHA}\nsnapshot_identity=${SHA2}\nreason=operator_disposition\ncaptured_lens_results=00-review-risk,01-review-resilience\nfindings_present=true\nactor=maintainer`]);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
test("registered ABANDON rejects duplicate and non-authoritative inventories before approval", async () => {
|
|
717
|
+
const duplicateQueue = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY, INCOMPLETE_DUPLICATE]) }]);
|
|
718
|
+
const duplicate = await (registeredController(client(duplicateQueue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
719
|
+
assert.equal((duplicate.details as { outcome?: string }).outcome, "native-abandon-ineligible");
|
|
720
|
+
assert.equal(duplicateQueue.calls.length, 1);
|
|
721
|
+
const unauthoritativeQueue = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY], process.cwd(), false) }]);
|
|
722
|
+
const unauthoritative = await (registeredController(client(unauthoritativeQueue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
723
|
+
assert.equal((unauthoritative.details as { outcome?: string }).outcome, "native-abandon-ineligible");
|
|
724
|
+
assert.equal(unauthoritativeQueue.calls.length, 1);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
test("registered ABANDON blocks foreign repositories and inventory failures without dispatch", async () => {
|
|
728
|
+
const foreignQueue = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY], "/foreign/repository") }]);
|
|
729
|
+
const foreign = await (registeredController(client(foreignQueue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
730
|
+
assert.equal((foreign.details as { status?: string }).status, "blocked");
|
|
731
|
+
assert.equal(foreignQueue.calls.length, 1);
|
|
732
|
+
const brokenQueue = queuedAdapter([{ stdout: "{not json" }]);
|
|
733
|
+
const broken = await (registeredController(client(brokenQueue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
734
|
+
assert.equal((broken.details as { status?: string }).status, "blocked");
|
|
735
|
+
assert.equal(brokenQueue.calls.length, 1);
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
test("native status repository identity tolerates Windows casing and rejects foreign roots", async () => {
|
|
739
|
+
const cased = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY], "c:\\REPO") }]);
|
|
740
|
+
const inventory = await client(cased.adapter).reviewStatus({ cwd: "C:\\Repo" });
|
|
741
|
+
assert.equal(inventory.repository, "c:\\REPO");
|
|
742
|
+
const foreign = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY], "D:\\Other") }]);
|
|
743
|
+
await assert.rejects(() => client(foreign.adapter).reviewStatus({ cwd: "C:\\Repo" }), /repository mismatch/);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
test("registered ABANDON fails closed on cancellation at the inventory and approval boundaries", async () => {
|
|
747
|
+
const cancelling = new AbortController();
|
|
748
|
+
const cancellingAdapter: ExecFileAdapter = async (request) => {
|
|
749
|
+
cancelling.abort();
|
|
750
|
+
return { stdout: statusStdout([ELIGIBLE_ENTRY]), stderr: "", exitCode: 0, signal: null, timedOut: false, outputLimitExceeded: false };
|
|
751
|
+
};
|
|
752
|
+
await assert.rejects(
|
|
753
|
+
(registeredController(client(cancellingAdapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, cancelling.signal, undefined, interactiveContext(true)),
|
|
754
|
+
/cancelled/,
|
|
755
|
+
);
|
|
756
|
+
const afterApproval = new AbortController();
|
|
757
|
+
const queue = queuedAdapter([{ stdout: statusStdout([ELIGIBLE_ENTRY]) }]);
|
|
758
|
+
const context = { cwd: process.cwd(), hasUI: true, ui: { confirm: async () => { afterApproval.abort(); return true; } } } as unknown as ExtensionContext;
|
|
759
|
+
await assert.rejects(
|
|
760
|
+
(registeredController(client(queue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, afterApproval.signal, undefined, context),
|
|
761
|
+
/cancelled/,
|
|
762
|
+
);
|
|
763
|
+
assert.equal(queue.calls.length, 1);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
test("registered ABANDON cancels during the second inventory without dispatching abandon argv", async () => {
|
|
767
|
+
const cancelling = new AbortController();
|
|
768
|
+
const calls: Array<Parameters<ExecFileAdapter>[0]> = [];
|
|
769
|
+
const adapter: ExecFileAdapter = async (request) => {
|
|
770
|
+
calls.push(request);
|
|
771
|
+
assert.equal(request.signal, cancelling.signal);
|
|
772
|
+
if (calls.length === 2) cancelling.abort();
|
|
773
|
+
return { stdout: request.arguments[1] === "abandon" ? JSON.stringify(ABANDON_RECORD) : statusStdout([ELIGIBLE_ENTRY]), stderr: "", exitCode: 0, signal: null, timedOut: false, outputLimitExceeded: false };
|
|
774
|
+
};
|
|
775
|
+
let approvals = 0;
|
|
776
|
+
const context = { cwd: process.cwd(), hasUI: true, ui: { confirm: async (_title: string, message: string) => {
|
|
777
|
+
approvals++;
|
|
778
|
+
assert.equal(cancelling.signal.aborted, false);
|
|
779
|
+
assert.ok(message.includes(`gentle-ai.review-abandon-authorization/v2\nlineage=stranded\nrevision=${SHA}\nsnapshot_identity=${SHA2}\nreason=operator_disposition\ncaptured_lens_results=00-review-risk,01-review-resilience\nfindings_present=true\nactor=maintainer`));
|
|
780
|
+
return true;
|
|
781
|
+
} } } as unknown as ExtensionContext;
|
|
782
|
+
const controller = registeredController(client(adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli);
|
|
783
|
+
let cancellation: unknown;
|
|
784
|
+
try {
|
|
785
|
+
await controller.execute("second-inventory-cancel", ABANDON_INPUT, cancelling.signal, undefined, context);
|
|
786
|
+
} catch (error) {
|
|
787
|
+
cancellation = error;
|
|
788
|
+
}
|
|
789
|
+
assert.equal(approvals, 1);
|
|
790
|
+
assert.ok(calls.every((request) => request.arguments[1] === "status"), "cancellation during the second inventory must not dispatch abandon argv");
|
|
791
|
+
assert.equal(calls.length, 2);
|
|
792
|
+
assert.match(String(cancellation), /cancelled/);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
test("registered ABANDON rejects terminal status and terminal state entries before approval", async () => {
|
|
796
|
+
const terminalStatusEntry = { ...ELIGIBLE_ENTRY, status: "approved" };
|
|
797
|
+
const terminalStatusQueue = queuedAdapter([{ stdout: statusStdout([terminalStatusEntry]) }]);
|
|
798
|
+
const terminalStatus = await (registeredController(client(terminalStatusQueue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
799
|
+
assert.equal((terminalStatus.details as { outcome?: string }).outcome, "native-abandon-ineligible");
|
|
800
|
+
assert.equal(terminalStatusQueue.calls.length, 1);
|
|
801
|
+
const terminalStateQueue = queuedAdapter([{ stdout: statusStdout([{ ...ELIGIBLE_ENTRY, state: "closed" }]) }]);
|
|
802
|
+
const terminalState = await (registeredController(client(terminalStateQueue.adapter) as unknown as import("../lib/native-review-cli.ts").NativeReviewCli)).execute("t", ABANDON_INPUT, undefined, undefined, interactiveContext(true));
|
|
803
|
+
assert.equal((terminalState.details as { outcome?: string }).outcome, "native-abandon-ineligible");
|
|
804
|
+
assert.equal(terminalStateQueue.calls.length, 1);
|
|
805
|
+
});
|