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
|
@@ -11,7 +11,7 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
|
|
|
11
11
|
import { __testing, createGentleAiExtension, PendingReviewConsentRegistry } from "../extensions/gentle-ai.ts";
|
|
12
12
|
import { CandidateViewError, CandidateViewRegistry } from "../lib/review-candidate-view.ts";
|
|
13
13
|
import { NATIVE_REVIEW_ERROR_CODE, NativeReviewCliError, NativeReviewConsentRequiredError, type NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
14
|
-
import { decodeReviewConsentV3, decodeReviewStatusV3, type ReviewCollectInputV3, type ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
14
|
+
import { decodeReviewLastEventClosureV1, decodeReviewConsentV3, decodeReviewStatusV3, type ReviewCollectInputV3, type ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
15
15
|
|
|
16
16
|
const SHA = `sha256:${"a".repeat(64)}`;
|
|
17
17
|
const TREE = "b".repeat(40);
|
|
@@ -129,6 +129,149 @@ test("STATUS renders the managed_assets_outdated continuation command as the act
|
|
|
129
129
|
assert.equal(degraded.hint, undefined);
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
+
test("caller-relative STATUS approved guidance preserves selected root against a different session", async () => {
|
|
133
|
+
const lineageId = "caller-relative-status";
|
|
134
|
+
const root = realpathSync(process.cwd());
|
|
135
|
+
const native = { targetStatus: async () => approvedAcknowledgementStatus(lineageId, root) } as unknown as NativeReviewCli;
|
|
136
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "status", lineageId, workspaceRoot: root }, dirname(root), native);
|
|
137
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}","workspaceRoot":${JSON.stringify(root)}}`);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("caller-relative approved closure guidance preserves selected root and canonical omission", async () => {
|
|
141
|
+
const lineageId = "caller-relative-closure";
|
|
142
|
+
const root = realpathSync(process.cwd());
|
|
143
|
+
const { submission: _submission, artifactSubject: _artifactSubject, ...base } = collectInput(lineageId);
|
|
144
|
+
const input = { ...base, name: "provider_refuter", schema: "https://gentle-ai.dev/schema/review/refuter/v1", captureOperation: "review.capture-refuter", arguments: [
|
|
145
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
146
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
147
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
148
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
149
|
+
] } as unknown as ReviewCollectInputV3;
|
|
150
|
+
const transition = approvedAcknowledgementStatus(lineageId, root).nextTransition;
|
|
151
|
+
assert.equal(transition?.kind, "execute");
|
|
152
|
+
if (transition?.kind !== "execute") throw new Error("expected acknowledgement transition");
|
|
153
|
+
const acknowledgement = transition.execute;
|
|
154
|
+
const native = {
|
|
155
|
+
targetStatus: async () => ({ ...status(lineageId), nextTransition: { kind: "collect", reasonCode: "provider_role_required", collect: { inputs: [input] } } }),
|
|
156
|
+
captureProviderRole: async () => ({ schema: "gentle-ai.review-last-event-closure/v1", operation: "review.capture-refuter", lineageId, state: "approved", storeRevision: SHA, acknowledgement }),
|
|
157
|
+
} as unknown as NativeReviewCli;
|
|
158
|
+
const result = await __testing.executeReviewCaptureOperation({ lineageId, workspaceRoot: root, collectBinding: JSON.stringify(input) }, dirname(root), native);
|
|
159
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}","workspaceRoot":${JSON.stringify(root)}}`);
|
|
160
|
+
const canonical = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(input) }, root, native);
|
|
161
|
+
assert.equal(canonical.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}"}`);
|
|
162
|
+
assert.deepEqual((result.closure as Record<string, unknown>).acknowledgement, (canonical.closure as Record<string, unknown>).acknowledgement);
|
|
163
|
+
// This root-only registry double models a remembered lineage without a
|
|
164
|
+
// frozen projection, and enforces the same root identity at admission.
|
|
165
|
+
const boundViews = {
|
|
166
|
+
resolveWorkspaceRoot: (requestedLineage: string) => {
|
|
167
|
+
assert.equal(requestedLineage, lineageId);
|
|
168
|
+
return root;
|
|
169
|
+
},
|
|
170
|
+
assertWorkspaceRoot: (requestedLineage: string, requestedRoot: string) => {
|
|
171
|
+
assert.equal(requestedLineage, lineageId);
|
|
172
|
+
assert.equal(realpathSync(requestedRoot), root);
|
|
173
|
+
},
|
|
174
|
+
hasProjection: () => false,
|
|
175
|
+
} as unknown as CandidateViewRegistry;
|
|
176
|
+
const bound = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(input) }, dirname(root), native, undefined, boundViews);
|
|
177
|
+
assert.equal(bound.next_action, canonical.next_action);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("caller-relative grouped capture closure preserves selected workspace root", async (t) => {
|
|
181
|
+
t.after(() => __testing.setReviewHostRelayGroupRunnersForTesting());
|
|
182
|
+
const lineageId = "caller-relative-grouped";
|
|
183
|
+
const root = realpathSync(process.cwd());
|
|
184
|
+
const inputs = (["review-risk", "review-resilience", "review-readability", "review-reliability"] as const).map((lens, order) => {
|
|
185
|
+
const input = collectInput(lineageId);
|
|
186
|
+
input.arguments = input.arguments.map((argument) => {
|
|
187
|
+
const value = argument.name === "lens" ? lens : argument.name === "order" ? String(order) : argument.name === "subject-hash" ? `sha256:${String(order).repeat(64)}` : argument.value;
|
|
188
|
+
return { ...argument, value, token: `--${argument.name}=${value}` };
|
|
189
|
+
});
|
|
190
|
+
input.submission!.argumentTokens = [...input.arguments.map((argument) => argument.token!), "--input={{value}}"];
|
|
191
|
+
input.arguments = [...input.arguments, { name: "agent", value: "pi", token: "--agent=pi" }, { name: "materialize", value: "true", token: "--materialize=true" }];
|
|
192
|
+
input.artifactSubject = { ...input.artifactSubject!, lens, selectedOrder: order, subjectHash: `sha256:${String(order).repeat(64)}` };
|
|
193
|
+
return input;
|
|
194
|
+
});
|
|
195
|
+
const transition = approvedAcknowledgementStatus(lineageId, root).nextTransition;
|
|
196
|
+
if (transition?.kind !== "execute") throw new Error("expected acknowledgement transition");
|
|
197
|
+
const acknowledgement = transition.execute;
|
|
198
|
+
const native = { targetStatus: async (request: { cwd: string }) => {
|
|
199
|
+
assert.equal(request.cwd, root);
|
|
200
|
+
return { ...status(lineageId, inputs), repositoryContext: { capability: "review.opaque_repository_context", handle: `rctx1_${"c".repeat(64)}`, revision: SHA, targetIdentity: SHA } };
|
|
201
|
+
} } as unknown as NativeReviewCli;
|
|
202
|
+
const wireAcknowledgement = { ...acknowledgement, binding: { lineage_id: lineageId, revision: SHA, target_identity: SHA } };
|
|
203
|
+
const wireClosure = { schema: "gentle-ai.review-last-event-closure/v1", operation: "review/capture-result", lineage_id: lineageId, state: "approved", store_revision: SHA, action: "approved by reviewers", acknowledgement: wireAcknowledgement };
|
|
204
|
+
const decodedClosure = decodeReviewLastEventClosureV1(wireClosure);
|
|
205
|
+
assert.equal(decodedClosure.lineageId, lineageId);
|
|
206
|
+
assert.deepEqual(decodedClosure.acknowledgement?.raw, wireAcknowledgement);
|
|
207
|
+
let submissions = 0;
|
|
208
|
+
__testing.setReviewHostRelayGroupRunnersForTesting(async (requests) => requests.map((request) => ({ request, promptByteLength: 64, resultByteLength: 32 })), async () => {
|
|
209
|
+
submissions += 1;
|
|
210
|
+
return { promptByteLength: 64, resultByteLength: 32, submission: JSON.stringify(wireClosure) };
|
|
211
|
+
});
|
|
212
|
+
const result = await __testing.executeReviewCaptureGroupOperation({ lineageId, workspaceRoot: root, collectBindings: inputs.map((input) => JSON.stringify(input)), reviewerRunAcknowledged: true }, dirname(root), native);
|
|
213
|
+
assert.equal(submissions, 1, JSON.stringify(result));
|
|
214
|
+
assert.equal(result.outcome, "native-last-event-closure", JSON.stringify(result));
|
|
215
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}","workspaceRoot":${JSON.stringify(root)}}`);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Issue #1371: approved status mappings and closure envelopes provide additive next_action
|
|
219
|
+
test("STATUS on approved target renders additive next_action naming exact gentle_review acknowledge-approved invocation", async () => {
|
|
220
|
+
const lineageId = "status-approved-ack";
|
|
221
|
+
const native = { targetStatus: async () => approvedAcknowledgementStatus(lineageId) } as unknown as NativeReviewCli;
|
|
222
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "status", lineageId }, process.cwd(), native);
|
|
223
|
+
assert.equal(result.status, "blocked");
|
|
224
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}"}`);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("STATUS on approved target preserves workspaceRoot in next_action when distinct from process cwd", async (t) => {
|
|
228
|
+
const lineageId = "status-approved-ack-worktree";
|
|
229
|
+
const worktreeRoot = repository(t);
|
|
230
|
+
const native = { targetStatus: async () => approvedAcknowledgementStatus(lineageId, worktreeRoot) } as unknown as NativeReviewCli;
|
|
231
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "status", lineageId, workspaceRoot: worktreeRoot }, worktreeRoot, native);
|
|
232
|
+
assert.equal(result.status, "blocked");
|
|
233
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}","workspaceRoot":${JSON.stringify(worktreeRoot)}}`);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("invalid acknowledgement input returns self-healing next_action for canonical lineage and fallback slug otherwise", async (t) => {
|
|
237
|
+
const lineageId = "canonical-lineage-ack";
|
|
238
|
+
const native = { targetStatus: async () => approvedAcknowledgementStatus(lineageId) } as unknown as NativeReviewCli;
|
|
239
|
+
|
|
240
|
+
// 1. Controller-only input with canonical lineage (no workspaceRoot)
|
|
241
|
+
const rejectedWithInput = await __testing.executeReviewControllerOperation(
|
|
242
|
+
{ operation: "acknowledge-approved", lineageId, input: "{}" },
|
|
243
|
+
process.cwd(),
|
|
244
|
+
native,
|
|
245
|
+
);
|
|
246
|
+
assert.equal(rejectedWithInput.outcome, "native-approved-acknowledgement-input-invalid");
|
|
247
|
+
assert.equal(rejectedWithInput.reason, "controller-only-input");
|
|
248
|
+
assert.equal(rejectedWithInput.field, "input");
|
|
249
|
+
assert.equal(rejectedWithInput.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}"}`);
|
|
250
|
+
|
|
251
|
+
// 2. Controller-only input with canonical lineage and distinct workspaceRoot
|
|
252
|
+
const worktreeRoot = repository(t);
|
|
253
|
+
const rejectedWithWorktree = await __testing.executeReviewControllerOperation(
|
|
254
|
+
{ operation: "acknowledge-approved", lineageId, input: "{}", workspaceRoot: worktreeRoot },
|
|
255
|
+
process.cwd(),
|
|
256
|
+
native,
|
|
257
|
+
);
|
|
258
|
+
assert.equal(rejectedWithWorktree.outcome, "native-approved-acknowledgement-input-invalid");
|
|
259
|
+
assert.equal(
|
|
260
|
+
rejectedWithWorktree.next_action,
|
|
261
|
+
`gentle_review {"operation":"acknowledge-approved","lineageId":"${lineageId}","workspaceRoot":${JSON.stringify(worktreeRoot)}}`,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// 3. Non-canonical / invalid lineage returns fallback slug
|
|
265
|
+
const rejectedNonCanonical = await __testing.executeReviewControllerOperation(
|
|
266
|
+
{ operation: "acknowledge-approved", lineageId: "invalid lineage with newline\n", input: "{}" },
|
|
267
|
+
process.cwd(),
|
|
268
|
+
native,
|
|
269
|
+
);
|
|
270
|
+
assert.equal(rejectedNonCanonical.outcome, "native-approved-acknowledgement-input-invalid");
|
|
271
|
+
assert.equal(rejectedNonCanonical.reason, "controller-only-input");
|
|
272
|
+
assert.equal(rejectedNonCanonical.next_action, "resubmit-the-exact-lineage-without-controller-only-input");
|
|
273
|
+
});
|
|
274
|
+
|
|
132
275
|
test("public acknowledgement relays one current provider vector and never replays after authority burn", async () => {
|
|
133
276
|
const lineageId = "acknowledge-approved";
|
|
134
277
|
const requests: Array<Record<string, unknown>> = [];
|
|
@@ -1779,6 +1922,7 @@ test("plain START adopts the pre-lineage selection retained by inspect and delet
|
|
|
1779
1922
|
retained,
|
|
1780
1923
|
);
|
|
1781
1924
|
assert.equal(started.operation, "start");
|
|
1925
|
+
assert.ok(typeof started.result === "object" && started.result !== null && "lineage_id" in started.result);
|
|
1782
1926
|
assert.equal(started.result.lineage_id, "review-started");
|
|
1783
1927
|
assert.equal(starts.length, 1);
|
|
1784
1928
|
assert.equal(requests.length, 3);
|
|
@@ -2234,6 +2378,7 @@ test("a failed START retains a pre-lineage selection for a same-candidate retry"
|
|
|
2234
2378
|
{ operation: "start", input: JSON.stringify({ mode: "ordinary" }) },
|
|
2235
2379
|
cwd, native, undefined, null, undefined, retained,
|
|
2236
2380
|
);
|
|
2381
|
+
assert.ok(typeof retried.result === "object" && retried.result !== null && "lineage_id" in retried.result);
|
|
2237
2382
|
assert.equal(retried.result.lineage_id, "retried");
|
|
2238
2383
|
assert.equal(starts, 2);
|
|
2239
2384
|
assert.equal(retained.has(`${cwd}\u0000`), false);
|
|
@@ -2439,6 +2584,115 @@ test("targeted-validator provider vectors preserve their nonuniform native closu
|
|
|
2439
2584
|
assert.equal("correction_target_identity" in result, false);
|
|
2440
2585
|
});
|
|
2441
2586
|
|
|
2587
|
+
test("approved capture closure envelope carries additive next_action alongside untouched raw acknowledgement", async () => {
|
|
2588
|
+
const closureLineage = "review-approved-closure-next-action";
|
|
2589
|
+
const input = {
|
|
2590
|
+
name: "provider_targeted_validator",
|
|
2591
|
+
schema: "https://gentle-ai.dev/schema/review/targeted-validator/v1",
|
|
2592
|
+
captureOperation: "review.capture-validation",
|
|
2593
|
+
arguments: [
|
|
2594
|
+
{ name: "lineage", value: closureLineage, token: `--lineage=${closureLineage}` },
|
|
2595
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
2596
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
2597
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
2598
|
+
],
|
|
2599
|
+
} as unknown as ReviewCollectInputV3;
|
|
2600
|
+
const roleStatus = { ...status(closureLineage), nextTransition: { kind: "collect", reasonCode: "provider_role_required", collect: { inputs: [input] } } } as ReviewStatusV3;
|
|
2601
|
+
const ackContinuation = {
|
|
2602
|
+
operation: "review.acknowledge-approved",
|
|
2603
|
+
command: "gentle-ai review acknowledge-approved --provider-vector",
|
|
2604
|
+
arguments: [
|
|
2605
|
+
{ name: "cwd", value: process.cwd(), token: `--cwd=${process.cwd()}` },
|
|
2606
|
+
{ name: "lineage", value: closureLineage, token: `--lineage=${closureLineage}` },
|
|
2607
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
2608
|
+
{ name: "expected-revision", value: SHA, token: `--expected-revision=${SHA}` },
|
|
2609
|
+
{ name: "token", value: "provider-tok", token: "--token=provider-tok" },
|
|
2610
|
+
],
|
|
2611
|
+
preconditions: [{ name: "state", value: "approved" }],
|
|
2612
|
+
binding: { lineageId: closureLineage, revision: SHA, targetIdentity: SHA },
|
|
2613
|
+
};
|
|
2614
|
+
const native = {
|
|
2615
|
+
targetStatus: async () => roleStatus,
|
|
2616
|
+
captureProviderRole: async () => ({
|
|
2617
|
+
schema: "gentle-ai.review-last-event-closure/v1",
|
|
2618
|
+
operation: "review/capture-validation",
|
|
2619
|
+
lineageId: closureLineage,
|
|
2620
|
+
state: "approved",
|
|
2621
|
+
action: "approved by validator",
|
|
2622
|
+
storeRevision: SHA,
|
|
2623
|
+
acknowledgement: {
|
|
2624
|
+
operation: "review.acknowledge-approved",
|
|
2625
|
+
command: "gentle-ai review acknowledge-approved --provider-vector",
|
|
2626
|
+
arguments: ackContinuation.arguments,
|
|
2627
|
+
preconditions: ackContinuation.preconditions,
|
|
2628
|
+
binding: ackContinuation.binding,
|
|
2629
|
+
raw: ackContinuation,
|
|
2630
|
+
},
|
|
2631
|
+
}),
|
|
2632
|
+
} as unknown as NativeReviewCli;
|
|
2633
|
+
const result = await __testing.executeReviewCaptureOperation({ lineageId: closureLineage, collectBinding: JSON.stringify(input) }, process.cwd(), native);
|
|
2634
|
+
assert.equal(result.status, "closed");
|
|
2635
|
+
assert.equal(result.outcome, "native-last-event-closure");
|
|
2636
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${closureLineage}"}`);
|
|
2637
|
+
assert.equal((result.closure as { next_action?: string }).next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${closureLineage}"}`);
|
|
2638
|
+
assert.deepEqual((result.closure as { acknowledgement?: unknown }).acknowledgement, ackContinuation);
|
|
2639
|
+
});
|
|
2640
|
+
|
|
2641
|
+
test("approved capture closure envelope preserves workspaceRoot in next_action when distinct from process cwd", async (t) => {
|
|
2642
|
+
const closureLineage = "review-approved-closure-next-action-worktree";
|
|
2643
|
+
const worktreeRoot = repository(t);
|
|
2644
|
+
const input = {
|
|
2645
|
+
name: "provider_targeted_validator",
|
|
2646
|
+
schema: "https://gentle-ai.dev/schema/review/targeted-validator/v1",
|
|
2647
|
+
captureOperation: "review.capture-validation",
|
|
2648
|
+
arguments: [
|
|
2649
|
+
{ name: "lineage", value: closureLineage, token: `--lineage=${closureLineage}` },
|
|
2650
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
2651
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
2652
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
2653
|
+
],
|
|
2654
|
+
} as unknown as ReviewCollectInputV3;
|
|
2655
|
+
const roleStatus = { ...status(closureLineage), nextTransition: { kind: "collect", reasonCode: "provider_role_required", collect: { inputs: [input] } } } as ReviewStatusV3;
|
|
2656
|
+
const ackContinuation = {
|
|
2657
|
+
operation: "review.acknowledge-approved",
|
|
2658
|
+
command: "gentle-ai review acknowledge-approved --provider-vector",
|
|
2659
|
+
arguments: [
|
|
2660
|
+
{ name: "cwd", value: worktreeRoot, token: `--cwd=${worktreeRoot}` },
|
|
2661
|
+
{ name: "lineage", value: closureLineage, token: `--lineage=${closureLineage}` },
|
|
2662
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
2663
|
+
{ name: "expected-revision", value: SHA, token: `--expected-revision=${SHA}` },
|
|
2664
|
+
{ name: "token", value: "provider-tok", token: "--token=provider-tok" },
|
|
2665
|
+
],
|
|
2666
|
+
preconditions: [{ name: "state", value: "approved" }],
|
|
2667
|
+
binding: { lineageId: closureLineage, revision: SHA, targetIdentity: SHA },
|
|
2668
|
+
};
|
|
2669
|
+
const native = {
|
|
2670
|
+
targetStatus: async () => roleStatus,
|
|
2671
|
+
captureProviderRole: async () => ({
|
|
2672
|
+
schema: "gentle-ai.review-last-event-closure/v1",
|
|
2673
|
+
operation: "review/capture-validation",
|
|
2674
|
+
lineageId: closureLineage,
|
|
2675
|
+
state: "approved",
|
|
2676
|
+
action: "approved by validator",
|
|
2677
|
+
storeRevision: SHA,
|
|
2678
|
+
acknowledgement: {
|
|
2679
|
+
operation: "review.acknowledge-approved",
|
|
2680
|
+
command: "gentle-ai review acknowledge-approved --provider-vector",
|
|
2681
|
+
arguments: ackContinuation.arguments,
|
|
2682
|
+
preconditions: ackContinuation.preconditions,
|
|
2683
|
+
binding: ackContinuation.binding,
|
|
2684
|
+
raw: ackContinuation,
|
|
2685
|
+
},
|
|
2686
|
+
}),
|
|
2687
|
+
} as unknown as NativeReviewCli;
|
|
2688
|
+
const result = await __testing.executeReviewCaptureOperation({ lineageId: closureLineage, collectBinding: JSON.stringify(input), workspaceRoot: worktreeRoot }, worktreeRoot, native);
|
|
2689
|
+
assert.equal(result.status, "closed");
|
|
2690
|
+
assert.equal(result.outcome, "native-last-event-closure");
|
|
2691
|
+
assert.equal(result.next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${closureLineage}","workspaceRoot":${JSON.stringify(worktreeRoot)}}`);
|
|
2692
|
+
assert.equal((result.closure as { next_action?: string }).next_action, `gentle_review {"operation":"acknowledge-approved","lineageId":"${closureLineage}","workspaceRoot":${JSON.stringify(worktreeRoot)}}`);
|
|
2693
|
+
assert.deepEqual((result.closure as { acknowledgement?: unknown }).acknowledgement, ackContinuation);
|
|
2694
|
+
});
|
|
2695
|
+
|
|
2442
2696
|
test("targeted-validator captures echo the distinct provider correction target identity", async () => {
|
|
2443
2697
|
const closureLineage = "review-validator-correction-target";
|
|
2444
2698
|
const correctionTarget = `sha256:${"d".repeat(64)}`;
|
|
@@ -38,7 +38,7 @@ function runtime(
|
|
|
38
38
|
const tools = new Map<string, RegisteredTool>();
|
|
39
39
|
let toolCall: ToolCallHandler | undefined;
|
|
40
40
|
const handlers = new Map<string, (event: unknown, ctx: ExtensionContext) => unknown>();
|
|
41
|
-
const dependencies = { nativeReviewCli, candidateViews } as unknown as Parameters<typeof createGentleAiExtension>[0];
|
|
41
|
+
const dependencies = { nativeReviewCli, candidateViews, processEnv: { GENTLE_PI_AGENTS_CHILD: "0" } } as unknown as Parameters<typeof createGentleAiExtension>[0];
|
|
42
42
|
createGentleAiExtension(dependencies)({
|
|
43
43
|
on(name: string, handler: ToolCallHandler) {
|
|
44
44
|
if (name === "tool_call") toolCall = handler;
|
|
@@ -192,6 +192,47 @@ test("invalid bootstrap target selectors reject before native STATUS", async ()
|
|
|
192
192
|
assert.equal(statusCalls, 0);
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
test("START forwards the agent lens selection in canonical order and rejects malformed selections before native STATUS", async (t) => {
|
|
196
|
+
const cwd = repository(t);
|
|
197
|
+
writeFileSync(join(cwd, "app.ts"), "export const value = 2;\n");
|
|
198
|
+
const startRequests: Parameters<NativeReviewCli["start"]>[0][] = [];
|
|
199
|
+
const { controller } = runtime(fakeNative({
|
|
200
|
+
start: async (request) => {
|
|
201
|
+
startRequests.push(request);
|
|
202
|
+
return { lineageId: "lens-selection-lineage", state: "reviewing", riskLevel: "medium", selectedLenses: ["review-risk", "review-reliability"], changedFiles: 1, changedLines: 1, correctionBudget: 1, action: "created", lensesRequired: true };
|
|
203
|
+
},
|
|
204
|
+
}));
|
|
205
|
+
const started = await controller.execute("start-lenses", { operation: "start", input: JSON.stringify({ mode: "ordinary", lenses: ["reliability", "review-risk"], lensesReason: "changes token parsing" }) }, undefined, undefined, context(cwd));
|
|
206
|
+
assert.notEqual((started.details as { status?: string }).status, "blocked");
|
|
207
|
+
assert.equal(startRequests.length, 1);
|
|
208
|
+
assert.deepEqual(startRequests[0]?.lenses, ["review-risk", "review-reliability"]);
|
|
209
|
+
assert.equal(startRequests[0]?.lensesReason, "changes token parsing");
|
|
210
|
+
|
|
211
|
+
let statusCalls = 0;
|
|
212
|
+
const native = fakeNative({
|
|
213
|
+
reviewMode: async () => ({ operation: "status", scope: "clone", status: { global: "on", cloneLocal: "", effective: "on", source: "global" } }),
|
|
214
|
+
targetStatus: async () => { statusCalls += 1; throw new Error("must not reach STATUS"); },
|
|
215
|
+
start: async () => { throw new Error("must not reach START"); },
|
|
216
|
+
});
|
|
217
|
+
for (const input of [
|
|
218
|
+
{ mode: "ordinary", lenses: ["risk"] },
|
|
219
|
+
{ mode: "ordinary", lensesReason: "why" },
|
|
220
|
+
{ mode: "ordinary", lenses: [], lensesReason: "why" },
|
|
221
|
+
{ mode: "ordinary", lenses: "risk", lensesReason: "why" },
|
|
222
|
+
{ mode: "ordinary", lenses: ["security"], lensesReason: "why" },
|
|
223
|
+
{ mode: "ordinary", lenses: ["risk", "review-risk"], lensesReason: "why" },
|
|
224
|
+
{ mode: "ordinary", lenses: ["risk"], lensesReason: " " },
|
|
225
|
+
{ mode: "ordinary", lenses: ["risk"], lensesReason: "x".repeat(501) },
|
|
226
|
+
{ mode: "ordinary", lenses: ["risk"], lensesReason: "why", focus: "risk" },
|
|
227
|
+
]) {
|
|
228
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "start", input: JSON.stringify(input) }, cwd, native);
|
|
229
|
+
assert.equal(result.status, "blocked", JSON.stringify(input));
|
|
230
|
+
assert.equal(result.reason, "lenses-invalid", JSON.stringify(input));
|
|
231
|
+
assert.equal(result.outcome, "native-start-input-invalid", JSON.stringify(input));
|
|
232
|
+
}
|
|
233
|
+
assert.equal(statusCalls, 0);
|
|
234
|
+
});
|
|
235
|
+
|
|
195
236
|
test("repository discovery ignores ambient Git root overrides", async (t) => {
|
|
196
237
|
const sessionRoot = repository(t);
|
|
197
238
|
const targetRoot = repository(t, "gentle-pi-isolated-target-");
|
|
@@ -698,7 +739,7 @@ test("same-session START binding migrates to one validation capture without a FI
|
|
|
698
739
|
captureProviderRole: async (request) => {
|
|
699
740
|
captureCalls += 1;
|
|
700
741
|
assert.equal(request.captureOperation, "review.capture-validation");
|
|
701
|
-
return { schema: "gentle-ai.review-last-event-closure/v1", operation: "review
|
|
742
|
+
return { schema: "gentle-ai.review-last-event-closure/v1", operation: "review/capture-validation", lineageId, state: "approved", storeRevision: `sha256:${"a".repeat(64)}` };
|
|
702
743
|
},
|
|
703
744
|
});
|
|
704
745
|
const { controller } = runtime(native, candidateViews);
|
|
@@ -371,9 +371,9 @@ test("failed START gives exact mode and serialization guidance and creates no li
|
|
|
371
371
|
operation: "start",
|
|
372
372
|
lineageId: "nested-start-input",
|
|
373
373
|
idempotencyKey: "nested-start-input-key",
|
|
374
|
-
input: { mode: "
|
|
374
|
+
input: { mode: "standard" },
|
|
375
375
|
}, undefined, undefined, ctx),
|
|
376
|
-
/
|
|
376
|
+
/only "ordinary" or "judgment-day".*no lineage was created.*do not call STATUS or ADVANCE/is,
|
|
377
377
|
);
|
|
378
378
|
await assert.rejects(
|
|
379
379
|
controller.execute("invalid-json-start-input", {
|
|
@@ -61,7 +61,7 @@ test("passed is the only outcome that unlocks targeted validation", () => {
|
|
|
61
61
|
assert.equal(step.kind, "run-targeted-validation");
|
|
62
62
|
assert.equal(step.unlocksTargetedValidation, true);
|
|
63
63
|
assert.equal(step.transactionOpen, false);
|
|
64
|
-
assert.equal(step
|
|
64
|
+
assert.equal(Object.hasOwn(step, "escalation"), false);
|
|
65
65
|
// The provider issues the request; Pi must re-query STATUS for it rather
|
|
66
66
|
// than inventing one, which is why the step names the operation instead of
|
|
67
67
|
// carrying a fabricated payload.
|
|
@@ -95,6 +95,7 @@ test("verification_failed carries the prior identity as supersedes and demands a
|
|
|
95
95
|
test("verification_failed never consumes budget even when lines were already charged", () => {
|
|
96
96
|
const charged = { ...STATUS, changedLinesCharged: 40 };
|
|
97
97
|
const step = resolveCorrectionStep(charged, evidence("verification_failed"));
|
|
98
|
+
assert.equal(step.kind, "recapture-required");
|
|
98
99
|
|
|
99
100
|
// The invariant is about what THIS outcome adds, not about resetting prior
|
|
100
101
|
// accounting: a failed verification must not move the needle at all.
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
recheckReleaseFastPathRemoteHeadV1,
|
|
15
15
|
resolveConfiguredPushDestinationV1,
|
|
16
16
|
type GateTargetV1,
|
|
17
|
+
type ReleaseGateTargetV1,
|
|
17
18
|
type GhCommandRunnerV1,
|
|
18
19
|
type ReleaseFastPathEvidenceV1,
|
|
19
20
|
} from "../lib/review-publication-gate.ts";
|
|
@@ -177,14 +178,14 @@ function temporaryAuthority(t: test.TestContext): GateRepository & {
|
|
|
177
178
|
test("unbranded receipts are rejected before lifecycle gate evaluation", (t) => {
|
|
178
179
|
const { repository, finalTree, store, receipt } = temporaryAuthority(t);
|
|
179
180
|
execFileSync("git", ["read-tree", finalTree], { cwd: repository });
|
|
180
|
-
assert.throws(() => validateReviewGate
|
|
181
|
+
assert.throws(() => Reflect.apply(validateReviewGate, undefined, [{
|
|
181
182
|
store,
|
|
182
183
|
receipt,
|
|
183
184
|
target: { kind: GATE_TARGET_KIND.INTENDED_COMMIT, intended_commit_tree: finalTree },
|
|
184
185
|
repositoryCwd: repository,
|
|
185
186
|
idempotencyKey: "unbranded-gate",
|
|
186
187
|
scopeBudget: budget(),
|
|
187
|
-
}), /branded authoritative receipt/i);
|
|
188
|
+
}]), /branded authoritative receipt/i);
|
|
188
189
|
});
|
|
189
190
|
|
|
190
191
|
test("authoritative receipts cannot be validated through another repository store", (t) => {
|
|
@@ -333,6 +334,7 @@ test("push gate allows normal same-name updates while preserving exact-old and c
|
|
|
333
334
|
const driftedUpdate = {
|
|
334
335
|
kind: GATE_TARGET_KIND.PUSH,
|
|
335
336
|
remote,
|
|
337
|
+
destination_id: target.destination_id,
|
|
336
338
|
updates: [{
|
|
337
339
|
...target.updates[1],
|
|
338
340
|
old_object: finalCommit,
|
|
@@ -347,6 +349,7 @@ test("push gate allows normal same-name updates while preserving exact-old and c
|
|
|
347
349
|
const createOverExisting = {
|
|
348
350
|
kind: GATE_TARGET_KIND.PUSH,
|
|
349
351
|
remote,
|
|
352
|
+
destination_id: target.destination_id,
|
|
350
353
|
updates: [{ ...target.updates[0], destination_ref: "refs/heads/final" }],
|
|
351
354
|
} as const;
|
|
352
355
|
assert.equal(
|
|
@@ -580,7 +583,7 @@ test("scope child claim and parent gate journal publish atomically across faults
|
|
|
580
583
|
assert.equal(store.read(receipt.body.lineage_id).child_claims?.length, 1);
|
|
581
584
|
});
|
|
582
585
|
|
|
583
|
-
function releaseTarget(repository: GateRepository):
|
|
586
|
+
function releaseTarget(repository: GateRepository): ReleaseGateTargetV1 {
|
|
584
587
|
return {
|
|
585
588
|
kind: GATE_TARGET_KIND.RELEASE,
|
|
586
589
|
tag_ref: "refs/tags/v1.2.3",
|
|
@@ -628,7 +631,7 @@ test("release fast path independently accepts complete successful Check Runs des
|
|
|
628
631
|
const repository = createGateRepository(t);
|
|
629
632
|
setRemoteMain(repository, repository.finalCommit);
|
|
630
633
|
const calls: string[][] = [];
|
|
631
|
-
const checkRuns = (checks: unknown[], totalCount = checks.length, legacyStatus = "pending"): GhCommandRunnerV1 => (args) => {
|
|
634
|
+
const checkRuns = (checks: readonly unknown[], totalCount = checks.length, legacyStatus = "pending"): GhCommandRunnerV1 => (args) => {
|
|
632
635
|
calls.push([...args]);
|
|
633
636
|
if (args[1] === `repos/{owner}/{repo}/commits/${repository.finalCommit}/check-runs?per_page=100`) {
|
|
634
637
|
return { status: 0, stdout: JSON.stringify({ total_count: totalCount, returned: checks.length, checks }) };
|
|
@@ -63,8 +63,8 @@ test("event schema commits a canonical reducer transition and input", () => {
|
|
|
63
63
|
payload: { initial: true },
|
|
64
64
|
reduced_state_hash: stateHash,
|
|
65
65
|
} as never);
|
|
66
|
-
assert.equal(
|
|
67
|
-
assert.deepEqual(
|
|
66
|
+
assert.equal(event.body.reducer_transition, "start");
|
|
67
|
+
assert.deepEqual(event.body.reducer_input, { source: "controller" });
|
|
68
68
|
assert.throws(() => createReviewEventV1({
|
|
69
69
|
lineage_id: "lineage-a",
|
|
70
70
|
sequence: 0,
|
|
@@ -211,6 +211,8 @@ function nativeHarness(statuses: readonly ReviewStatusV3[], unachievableResponde
|
|
|
211
211
|
native: undefined as unknown as NativeReviewCli,
|
|
212
212
|
};
|
|
213
213
|
harness.native = {
|
|
214
|
+
start: async () => { throw new Error("unexpected START"); },
|
|
215
|
+
reviewStatus: async () => { throw new Error("unexpected compact STATUS"); },
|
|
214
216
|
targetStatus: async (request) => {
|
|
215
217
|
harness.statusCalls.push({ cwd: request.cwd, ...(request.lineageId === undefined ? {} : { lineageId: request.lineageId }), ...(request.agent === undefined ? {} : { agent: request.agent }) });
|
|
216
218
|
const next = harness.statusQueue.shift();
|
|
@@ -226,7 +228,7 @@ async function runCapture(cwd: string, harness: RoutingHarness, lineageId: strin
|
|
|
226
228
|
const selected = harness.statusQueue[0]?.nextTransition?.collect?.inputs[0];
|
|
227
229
|
if (selected === undefined) throw new Error("capture test requires one current collect input");
|
|
228
230
|
return await __testing.executeReviewCaptureOperation(
|
|
229
|
-
{ lineageId, collectBinding:
|
|
231
|
+
{ lineageId, collectBinding: selected, ...input },
|
|
230
232
|
cwd,
|
|
231
233
|
harness.native,
|
|
232
234
|
undefined, undefined, undefined, false, modelRegistry,
|
|
@@ -436,9 +438,9 @@ test("Pi-authored review documents are rejected at the capture input boundary",
|
|
|
436
438
|
assert.equal(relayCalls, 0);
|
|
437
439
|
});
|
|
438
440
|
|
|
439
|
-
function groupInputs(lineageId: string, revision = SHA): ReviewCollectInputV3[] { return ["review-risk", "review-resilience", "review-readability", "review-reliability"].map((lens, order) => relayCollectInput(lineageId, lens, order, true, "provider", revision)); }
|
|
441
|
+
function groupInputs(lineageId: string, revision = SHA): ReviewCollectInputV3[] { return (["review-risk", "review-resilience", "review-readability", "review-reliability"] as const).map((lens, order) => relayCollectInput(lineageId, lens, order, true, "provider", revision)); }
|
|
440
442
|
|
|
441
|
-
async function runCaptureGroup(cwd: string, harness: RoutingHarness, lineageId: string, inputs: readonly ReviewCollectInputV3[], reviewerRunAcknowledged = true, modelRegistry?: InProcessReviewerRegistry): Promise<Record<string, unknown>> { return await __testing.executeReviewCaptureGroupOperation({ lineageId, collectBindings: inputs.map((input) => JSON.stringify(input)), reviewerRunAcknowledged }, cwd, harness.native, undefined, undefined, undefined, false, modelRegistry) as Record<string, unknown>; }
|
|
443
|
+
async function runCaptureGroup(cwd: string, harness: RoutingHarness, lineageId: string, inputs: readonly ReviewCollectInputV3[], reviewerRunAcknowledged = true, modelRegistry?: InProcessReviewerRegistry): Promise<Record<string, unknown>> { return await __testing.executeReviewCaptureGroupOperation({ lineageId, collectBindings: inputs.map((input, index) => index % 2 === 0 ? input : JSON.stringify(input)), reviewerRunAcknowledged }, cwd, harness.native, undefined, undefined, undefined, false, modelRegistry) as Record<string, unknown>; }
|
|
442
444
|
|
|
443
445
|
function prepared(request: ReviewHostRelayRequest) { return { request, promptByteLength: 64, resultByteLength: 32 }; }
|
|
444
446
|
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
import { createNodeExecFileAdapter } from "../lib/native-review-cli.ts";
|
|
7
|
+
import { setBundledInstallForTesting } from "../lib/gentle-shell-bundled.ts";
|
|
7
8
|
import { INPROCESS_REVIEWER_FAILURE, type InProcessReviewerOutcome, type InProcessReviewerRequest, type InProcessReviewerDeps, runInProcessReviewer } from "../lib/inprocess-reviewer.ts";
|
|
8
9
|
import {
|
|
9
10
|
REVIEW_HOST_RELAY_FAILURE,
|
|
@@ -53,7 +54,7 @@ const FAKE_GENTLE_AI = `#!/usr/bin/env node
|
|
|
53
54
|
const fs = require("node:fs");
|
|
54
55
|
const path = require("node:path");
|
|
55
56
|
const argv = process.argv.slice(2);
|
|
56
|
-
if (process.env.RELAY_FAKE_LOG) fs.appendFileSync(process.env.RELAY_FAKE_LOG, JSON.stringify({ argv, cwd: process.cwd(), contract: process.env.${GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV} ?? null }) + "\\n");
|
|
57
|
+
if (process.env.RELAY_FAKE_LOG) fs.appendFileSync(process.env.RELAY_FAKE_LOG, JSON.stringify({ argv, cwd: process.cwd(), contract: process.env.${GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV} ?? null, home: process.env.GENTLE_AI_HOME ?? null }) + "\\n");
|
|
57
58
|
if (argv.some((token) => token === "--materialize" || token.startsWith("--materialize="))) {
|
|
58
59
|
const mode = process.env.RELAY_FAKE_MATERIALIZE_MODE || "ok";
|
|
59
60
|
if (mode === "ok") { process.stdout.write(Buffer.from(process.env.RELAY_FAKE_PROMPT_B64 || "", "base64")); process.exit(0); }
|
|
@@ -137,7 +138,7 @@ function harness(t: test.TestContext, overrides: Record<string, string> = {}): R
|
|
|
137
138
|
return { directory, gentleAi, logPath, submitCapturePath, targetCwd, environment };
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
function readLog(path: string): Array<{ argv: string[]; contract: string | null; cwd?: string }> {
|
|
141
|
+
function readLog(path: string): Array<{ argv: string[]; contract: string | null; cwd?: string; home?: string | null }> {
|
|
141
142
|
if (!existsSync(path)) return [];
|
|
142
143
|
return readFileSync(path, "utf8").split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
143
144
|
}
|
|
@@ -273,6 +274,24 @@ test("the central native CLI runner declares the relay contract on every gentle-
|
|
|
273
274
|
}
|
|
274
275
|
});
|
|
275
276
|
|
|
277
|
+
// S9: in a bundled install (the testing overlay stands in for this package
|
|
278
|
+
// running from it) both relay invocations get Gentle AI's isolation
|
|
279
|
+
// variables; otherwise neither does.
|
|
280
|
+
test("both relay gentle-ai invocations carry Gentle AI's isolation variables only in a bundled install", async (t) => {
|
|
281
|
+
const ordinary = harness(t);
|
|
282
|
+
await runRelay(ordinary);
|
|
283
|
+
assert.deepEqual(readLog(ordinary.logPath).map((call) => call.home), [null, null]);
|
|
284
|
+
const prefix = realpathSync(mkdtempSync(join(tmpdir(), "gentle-ai-isolation-relay-")));
|
|
285
|
+
setBundledInstallForTesting({ root: prefix });
|
|
286
|
+
t.after(() => {
|
|
287
|
+
setBundledInstallForTesting(undefined);
|
|
288
|
+
rmSync(prefix, { recursive: true, force: true });
|
|
289
|
+
});
|
|
290
|
+
const bundled = harness(t, { GENTLE_SHELL_PREFIX: prefix });
|
|
291
|
+
await runRelay(bundled);
|
|
292
|
+
assert.deepEqual(readLog(bundled.logPath).map((call) => call.home), [join(prefix, "gentle-ai"), join(prefix, "gentle-ai")]);
|
|
293
|
+
});
|
|
294
|
+
|
|
276
295
|
test("relay contract constants are the compiled gentle-ai handshake values", () => {
|
|
277
296
|
assert.equal(GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV, "GENTLE_PI_REVIEW_RELAY_CONTRACT");
|
|
278
297
|
assert.equal(GENTLE_PI_REVIEW_RELAY_CONTRACT, "gentle-pi.review-relay/v1");
|
|
@@ -1036,3 +1055,31 @@ test("the negotiated decoder carries the provider submission through the capture
|
|
|
1036
1055
|
submission: { ...rawSubmission, values: [{ slot: "reviewer_result", domain: "artifact_path_or_stdin", substitution_location: rawSubmission.argument_tokens.length }] },
|
|
1037
1056
|
}] } }), /substitution_location/);
|
|
1038
1057
|
});
|
|
1058
|
+
|
|
1059
|
+
for (const transport of ["sse", "websocket", "websocket-cached", "auto", undefined] as const) {
|
|
1060
|
+
test(`relay forwards reviewer transport ${transport ?? "absent"} unchanged`, async (t) => {
|
|
1061
|
+
const fixture = harness(t);
|
|
1062
|
+
const reviewer = textReviewer(REVIEWER_TEXT);
|
|
1063
|
+
const result = await runRelay(fixture, { reviewerTransport: transport }, reviewer.runReviewer);
|
|
1064
|
+
assert.equal(reviewer.calls.length, 1);
|
|
1065
|
+
assert.equal(reviewer.calls[0]!.transport, transport);
|
|
1066
|
+
assert.equal(Object.hasOwn(reviewer.calls[0]!, "transport"), transport !== undefined);
|
|
1067
|
+
assert.equal(reviewer.calls[0]!.selection, "openai/gpt-5");
|
|
1068
|
+
assert.equal(result.resultByteLength, Buffer.byteLength(REVIEWER_TEXT));
|
|
1069
|
+
assert.equal(readFileSync(fixture.submitCapturePath, "utf8"), REVIEWER_TEXT);
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
test(`group relay forwards reviewer transport ${transport ?? "absent"} to every completion`, async (t) => {
|
|
1073
|
+
const fixture = harness(t);
|
|
1074
|
+
const reviewer = textReviewer(REVIEWER_TEXT);
|
|
1075
|
+
const requests = reviewerGroupRequests(fixture).map((request) => ({ ...request, reviewerTransport: transport }));
|
|
1076
|
+
const results = await runReviewHostRelayReviewerGroup(requests, (request) => prepareReviewHostRelaySlot(request, reviewer.runReviewer));
|
|
1077
|
+
assert.equal(results.length, requests.length);
|
|
1078
|
+
assert.equal(reviewer.calls.length, requests.length);
|
|
1079
|
+
for (const request of reviewer.calls) {
|
|
1080
|
+
assert.equal(request.transport, transport);
|
|
1081
|
+
assert.equal(Object.hasOwn(request, "transport"), transport !== undefined);
|
|
1082
|
+
}
|
|
1083
|
+
assert.equal(readLog(fixture.logPath).length, requests.length, "group preparation only materializes, never submits");
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
@@ -947,7 +947,7 @@ test("review-acknowledged/v1 is disjoint from every prior captured identity in b
|
|
|
947
947
|
["status/v3", decodeReviewStatusV3],
|
|
948
948
|
["start/v3", decodeReviewStartV3],
|
|
949
949
|
["start/v4", decodeReviewStartV4],
|
|
950
|
-
["capabilities/v2", decodeReviewCapabilitiesV2],
|
|
950
|
+
["capabilities/v2", (value) => decodeReviewCapabilitiesV2(value, CAPTURED_DIGEST)],
|
|
951
951
|
["consent/v2", decodeReviewConsentV2],
|
|
952
952
|
["consent/v3", decodeReviewConsentV3],
|
|
953
953
|
["last-event-closure/v1", decodeReviewLastEventClosureV1],
|
|
@@ -1004,3 +1004,23 @@ test("v7 escalation strictly decodes all causes and optional refuter evidence",
|
|
|
1004
1004
|
assert.throws(() => decodeReviewStatusV3({ ...escalatedStatusV7(), schema: `gentle-ai.review-integration.status/v${version}` }), /not allowed/);
|
|
1005
1005
|
}
|
|
1006
1006
|
});
|
|
1007
|
+
|
|
1008
|
+
test("START/v4 accepts every risk reason code and signal the published start schema allows", () => {
|
|
1009
|
+
// gentle-ai publishes dangerous_sink (code and signal) and agent_escalation
|
|
1010
|
+
// (code and signal) in start-v4.schema.json; a high-risk START that names
|
|
1011
|
+
// them must decode instead of failing as schema-incompatible (#5374).
|
|
1012
|
+
const start = reviewingStartV4();
|
|
1013
|
+
start.risk_reasons = [
|
|
1014
|
+
{ code: "dangerous_sink", signal: "dangerous_sink", path: "auth/session.py" },
|
|
1015
|
+
{ code: "hot_path", signal: "auth", path: "auth/session.py" },
|
|
1016
|
+
{ code: "process_boundary", signal: "shell_process", path: "auth/session.py" },
|
|
1017
|
+
{ code: "agent_escalation", signal: "agent_escalation" },
|
|
1018
|
+
];
|
|
1019
|
+
const decoded = decodeReviewStartV4(start);
|
|
1020
|
+
assert.deepEqual(decoded.riskReasons.map((reason) => reason.code), ["dangerous_sink", "hot_path", "process_boundary", "agent_escalation"]);
|
|
1021
|
+
assert.deepEqual(decoded.riskReasons.map((reason) => reason.signal), ["dangerous_sink", "auth", "shell_process", "agent_escalation"]);
|
|
1022
|
+
|
|
1023
|
+
const unknown = reviewingStartV4();
|
|
1024
|
+
unknown.risk_reasons = [{ code: "code_from_a_newer_cli" }];
|
|
1025
|
+
assert.throws(() => decodeReviewStartV4(unknown), /risk_reasons\[0\]\.code is unsupported/);
|
|
1026
|
+
});
|