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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import { __testing } from "../extensions/gentle-ai.ts";
|
|
6
|
+
|
|
7
|
+
// gentle-shell#1731 T26 (L54): in the final bench blind review of x2, the
|
|
8
|
+
// Gentle Shell inline solutions ranked below Codex on tests: they skipped the
|
|
9
|
+
// empty-stdout check, put the bad row last (no proof that later valid rows are
|
|
10
|
+
// not saved), missed the empty value, and asserted internal storage instead of
|
|
11
|
+
// the public output. The verify-backed arm ranked first because verify's probe
|
|
12
|
+
// checklist covers exactly these. Writers and inline work get the same
|
|
13
|
+
// checklist for the tests they write. Instruction-delivery contract only.
|
|
14
|
+
|
|
15
|
+
const TEST_RULE = [
|
|
16
|
+
"Each test asserts every observable effect of the rule it covers",
|
|
17
|
+
"exit code, exact stdout and stderr",
|
|
18
|
+
"rejected input leaves stored data and counters unchanged",
|
|
19
|
+
"covers the cases the rule itself names (its examples, boundaries, and errors)",
|
|
20
|
+
"through the public interface, never internal storage",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const read = (relative: string): string => readFileSync(join(import.meta.dirname, "..", relative), "utf8");
|
|
24
|
+
|
|
25
|
+
test("T26: the always-on Implement step gives inline work the test checklist", () => {
|
|
26
|
+
for (const persona of ["gentleman", "neutral"] as const) {
|
|
27
|
+
const step = __testing.buildGentlePrompt(persona).split("\n").find((line) => line.startsWith("6. **Implement"));
|
|
28
|
+
assert.ok(step, "ODD step 6 Implement is missing");
|
|
29
|
+
for (const clause of TEST_RULE) assert.ok(step.includes(clause), `${persona} Implement step is missing: ${clause}`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("T26: the worker test discipline carries the same checklist", () => {
|
|
34
|
+
const worker = read("assets/agents/gentle-ai-worker.md");
|
|
35
|
+
for (const clause of TEST_RULE) assert.ok(worker.includes(clause), `worker test discipline is missing: ${clause}`);
|
|
36
|
+
assert.ok(!worker.includes("add the smallest behavior-level test"), "RED must not ask for the smallest test");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// T26b (L56): blind review of x5 ranked Codex above the inline Gentle Shell
|
|
40
|
+
// arms partly because they added options without updating help and README.
|
|
41
|
+
test("T26b: inline and delegated work update the help and docs that describe a changed option", () => {
|
|
42
|
+
const clause = "When you add or change a command, option, or message, update the help text and docs that describe it";
|
|
43
|
+
for (const persona of ["gentleman", "neutral"] as const) {
|
|
44
|
+
const step = __testing.buildGentlePrompt(persona).split("\n").find((line) => line.startsWith("6. **Implement"));
|
|
45
|
+
assert.ok(step?.includes(clause), `${persona} Implement step is missing the docs rule`);
|
|
46
|
+
}
|
|
47
|
+
assert.ok(read("assets/agents/gentle-ai-worker.md").includes(clause), "worker is missing the docs rule");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// T27 (L56): in `pi --mode json` the runtime already rejects background
|
|
51
|
+
// launches (T18), but the prompt still said "Background subagent policy: on",
|
|
52
|
+
// so B-bg tried background twice and took the parallel-writer path; its worker
|
|
53
|
+
// shipped the silent `budget set --year` defect. Single-shot hosts render off.
|
|
54
|
+
test("T27: single-shot host modes render the background policy as off", () => {
|
|
55
|
+
for (const mode of ["json", "print"]) {
|
|
56
|
+
const prompt = __testing.buildGentlePrompt("gentleman", process.cwd(), undefined, undefined, mode);
|
|
57
|
+
assert.match(prompt, /Background subagent policy: off \(single-shot mode\)/, `${mode} must render off`);
|
|
58
|
+
}
|
|
59
|
+
for (const mode of ["tui", "rpc", undefined]) {
|
|
60
|
+
const prompt = __testing.buildGentlePrompt("gentleman", process.cwd(), undefined, undefined, mode);
|
|
61
|
+
assert.doesNotMatch(prompt, /single-shot mode/, `${String(mode)} must keep the configured policy`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// T28b (L60-L62): a fixed quota of generic edge cases is test padding. Every
|
|
66
|
+
// real defect in the blind reviews came from a case derived from the request
|
|
67
|
+
// or from an existing command the change touched (budget set --year, edit
|
|
68
|
+
// --amount ignored), never from an invented edge. Tests are derived: one per
|
|
69
|
+
// requested rule with the cases it names, plus one per touched existing
|
|
70
|
+
// command or option proving its previous behavior still holds. Nothing else.
|
|
71
|
+
const PRESERVE = "For every existing command or option the change touches, add one test proving its previous behavior still holds; add no other cases";
|
|
72
|
+
test("T28b: inline test-first derives cases from the rules and touched commands, with no edge-case quota", () => {
|
|
73
|
+
for (const persona of ["gentleman", "neutral"] as const) {
|
|
74
|
+
const prompt = __testing.buildGentlePrompt(persona);
|
|
75
|
+
const principle = prompt.split("\n").find((line) => line.includes("use test-first by default"));
|
|
76
|
+
assert.ok(principle?.includes("observe RED, GREEN, then refactor with focused checks"), `${persona} principle keeps RED, GREEN, refactor`);
|
|
77
|
+
assert.ok(principle?.includes("one RED test per requested rule"), `${persona} principle must ask for one RED test per rule`);
|
|
78
|
+
assert.ok(principle?.includes(PRESERVE), `${persona} principle must add the preserve test`);
|
|
79
|
+
assert.doesNotMatch(prompt, /TRIANGULATE|at least two edge cases/, `${persona} prompt must not keep the edge-case quota`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("T28b: the worker preserves touched behavior instead of inventing edge cases", () => {
|
|
84
|
+
const worker = read("assets/agents/gentle-ai-worker.md");
|
|
85
|
+
assert.ok(worker.includes(`3. PRESERVE — ${PRESERVE}`), "worker step 3 must be PRESERVE");
|
|
86
|
+
assert.doesNotMatch(worker, /at least two edge cases|3\. TRIANGULATE/, "worker must not keep the edge-case quota");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// T31 (L68-L70): T30 named a CLI-parser case and did not work (0/2 probes
|
|
90
|
+
// wrote the sibling test); it was fixture-specific. Replaced with the general
|
|
91
|
+
// definition of "touched", and high-risk item 3 now separates a requested
|
|
92
|
+
// contract change (covered by the request's tests) from existing behavior
|
|
93
|
+
// nobody asked to change that shares the changed code (the real regressions).
|
|
94
|
+
const TOUCHED = "An existing behavior counts as touched when it shares the code you changed (options, parsers, helpers, validation)";
|
|
95
|
+
test("T31: PRESERVE uses the general definition of touched, not a parser-specific case", () => {
|
|
96
|
+
for (const persona of ["gentleman", "neutral"] as const) {
|
|
97
|
+
const prompt = __testing.buildGentlePrompt(persona);
|
|
98
|
+
assert.ok(prompt.includes(TOUCHED), `${persona} prompt must define touched behavior`);
|
|
99
|
+
assert.doesNotMatch(prompt, /shared parser or command, test that every sibling subcommand/, `${persona} must drop the T30 parser case`);
|
|
100
|
+
}
|
|
101
|
+
const worker = read("assets/agents/gentle-ai-worker.md");
|
|
102
|
+
assert.ok(worker.includes(TOUCHED), "worker must define touched behavior");
|
|
103
|
+
assert.doesNotMatch(worker, /shared parser or command, test that every sibling subcommand/, "worker must drop the T30 parser case");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("T31: high-risk item 3 is about unrequested breaks, not the requested change", () => {
|
|
107
|
+
const core = read("assets/orchestrator.md");
|
|
108
|
+
assert.ok(
|
|
109
|
+
core.includes("requested changes are not; unrequested breaks in shared code are"),
|
|
110
|
+
"item 3 must separate requested changes from unrequested breaks",
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// T33 (L74): in p31 x5 both runs declared `Risk: none` after adding --year to
|
|
115
|
+
// the shared budget options; one shipped `budget set --year` silently saving a
|
|
116
|
+
// wrong budget, the regression verify used to catch. The Risk line must check
|
|
117
|
+
// shared code before claiming none.
|
|
118
|
+
const SHARED_CHECK = "Before writing `Risk: none`, check whether your diff changes code that existing behavior the request did not mention also uses (shared options, parsers, helpers); if it does, that is item 3";
|
|
119
|
+
test("T33: the Close step checks shared code before Risk: none", () => {
|
|
120
|
+
for (const persona of ["gentleman", "neutral"] as const) {
|
|
121
|
+
const step = __testing.buildGentlePrompt(persona).split("\n").find((line) => line.startsWith("7. **Close"));
|
|
122
|
+
assert.ok(step?.includes(SHARED_CHECK), `${persona} Close step must check shared code before Risk: none`);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
|
|
6
|
+
// gentle-shell#1731 T11 (S4, S5; L24): in the bench the independent verifier
|
|
7
|
+
// re-ran the writer's own tests and happy-path examples and missed defects that
|
|
8
|
+
// blind adversarial probes found (an import accepting invalid split groups; a
|
|
9
|
+
// budget ignoring refund parts whose own test locked the bug in). The verifier
|
|
10
|
+
// now derives its own probes from the spec. These are instruction-delivery
|
|
11
|
+
// contracts, not proof of model adherence.
|
|
12
|
+
|
|
13
|
+
const read = (relative: string): string => readFileSync(join(import.meta.dirname, "..", relative), "utf8");
|
|
14
|
+
const verify = read("assets/agents/gentle-ai-verify.md");
|
|
15
|
+
|
|
16
|
+
function checklistItem(n: number): string {
|
|
17
|
+
const line = verify.split("\n").find((entry) => entry.startsWith(`${n}. **`));
|
|
18
|
+
assert.ok(line, `verify checklist item ${n} is missing`);
|
|
19
|
+
return line;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function containsAll(text: string, clauses: readonly string[], label: string): void {
|
|
23
|
+
for (const clause of clauses) assert.ok(text.includes(clause), `${label} is missing: ${clause}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test("T11.1: verify derives its own probes from every numbered rule and never trusts the writer's tests", () => {
|
|
27
|
+
assert.ok(verify.includes("The writer's tests and examples are never evidence of correctness"), "verify must not treat the writer's tests as evidence");
|
|
28
|
+
containsAll(checklistItem(1), [
|
|
29
|
+
"verbatim request",
|
|
30
|
+
"every numbered rule or requirement",
|
|
31
|
+
"derive your own probes",
|
|
32
|
+
"positive examples",
|
|
33
|
+
"negative and boundary cases",
|
|
34
|
+
"exact error messages and exit codes",
|
|
35
|
+
], "probe item");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("T11.2: verify checks stored-data and legacy-output invariants against the baseline", () => {
|
|
39
|
+
containsAll(checklistItem(2), [
|
|
40
|
+
"after every rejected or failing command",
|
|
41
|
+
"byte-identical",
|
|
42
|
+
"hash before and after",
|
|
43
|
+
"commands that existed before the change",
|
|
44
|
+
"same output as at the baseline commit",
|
|
45
|
+
"unless the request changes them",
|
|
46
|
+
], "invariant item");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("T11.3: verify probes the cross-feature interactions the request implies", () => {
|
|
50
|
+
containsAll(checklistItem(3), ["cross-feature interactions", "new data", "existing reports, totals, and exports"], "interaction item");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("T11.4: verify runs the typecheck or build and flags scope creep", () => {
|
|
54
|
+
containsAll(checklistItem(4), ["typecheck or build", "when the project has one", "outside the request's scope"], "build and scope item");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("T11.5: verify returns a verdict for every spec item with no partial-scope carve-out", () => {
|
|
58
|
+
containsAll(checklistItem(5), [
|
|
59
|
+
"verdict for EVERY spec item",
|
|
60
|
+
"met, unmet, or not implemented",
|
|
61
|
+
"no partial-scope carve-out",
|
|
62
|
+
"even when the parent named one task or unit",
|
|
63
|
+
"the parent resolves every unmet item before closing, with one correction batch and one recheck limited to the reported blockers",
|
|
64
|
+
], "verdict item");
|
|
65
|
+
assert.ok(verify.includes("verdict per `S#`"), "the feature-document verdict per S# must stay");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("T11.6: verify leaves its probes as regression tests the writer commits", () => {
|
|
69
|
+
containsAll(checklistItem(6), [
|
|
70
|
+
"regression tests",
|
|
71
|
+
"the project's test suite",
|
|
72
|
+
"the writer to commit",
|
|
73
|
+
"you never write them",
|
|
74
|
+
], "durable probe item");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// T16 replaced "Run every probe only through command forms the parent authorized and only on isolated state the
|
|
78
|
+
// parent named": probes now run on the first launch in a fresh scratch copy (verify-first-launch-contract).
|
|
79
|
+
test("T11: probes stay inside the authorized commands or the scratch copy, and verify stays read-only", () => {
|
|
80
|
+
containsAll(verify, [
|
|
81
|
+
"execute only exact test, build, lint, or spec example commands explicitly authorized by the parent",
|
|
82
|
+
"only inside your scratch copy or on isolated state the parent named",
|
|
83
|
+
"report that probe as unverified with its exact command instead of running it",
|
|
84
|
+
"Do not edit, write, or fix findings.",
|
|
85
|
+
"Treat every unexpected mutation as a blocker",
|
|
86
|
+
"at most ~2k tokens",
|
|
87
|
+
], "verify agent");
|
|
88
|
+
const tools = verify.split("---")[1] ?? "";
|
|
89
|
+
assert.ok(!/\n {2}- (edit|write)\n/.test(tools), "verify must not gain edit or write tools");
|
|
90
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import { __testing } from "../extensions/gentle-ai.ts";
|
|
6
|
+
|
|
7
|
+
// gentle-shell#1731 T16 (S5; L33-L34): in the after3 bench (B x4/x5) the first
|
|
8
|
+
// verify launch ran no probes because the handoff said only "read-only" and
|
|
9
|
+
// named no probe commands or isolated state, so every probe was reported
|
|
10
|
+
// unverified and the parent continued the task (~2 min per cell). Read-only now
|
|
11
|
+
// means no repository or git-state edits, and probes in a fresh scratch copy run
|
|
12
|
+
// on the first launch.
|
|
13
|
+
// T17 (S3; L33-L34): B x4 delegated a 1.2 min exploration and then the parent
|
|
14
|
+
// re-read the same 10 files; exploration is delegated only for a map the parent
|
|
15
|
+
// needs to decide or route.
|
|
16
|
+
// These are instruction-delivery contracts, not proof of model adherence.
|
|
17
|
+
|
|
18
|
+
const read = (relative: string): string => readFileSync(join(import.meta.dirname, "..", relative), "utf8");
|
|
19
|
+
const verify = read("assets/agents/gentle-ai-verify.md");
|
|
20
|
+
const verification = read("assets/orchestrator-verification.md");
|
|
21
|
+
|
|
22
|
+
function containsAll(text: string, clauses: readonly string[], label: string): void {
|
|
23
|
+
for (const clause of clauses) assert.ok(text.includes(clause), `${label} is missing: ${clause}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test("T16: read-only means no repository or git-state edits", () => {
|
|
27
|
+
containsAll(verify, [
|
|
28
|
+
"read-only technical verifier for generic ODD work",
|
|
29
|
+
"Read-only means no edits to the repository or its git state",
|
|
30
|
+
], "verify agent");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("T16: verify probes on its first launch inside a fresh mktemp scratch copy without waiting for authorization", () => {
|
|
34
|
+
containsAll(verify, [
|
|
35
|
+
"Probe on your first launch; never wait for further authorization",
|
|
36
|
+
"fresh `mktemp -d` scratch copy of the workspace",
|
|
37
|
+
"the project's own test, CLI, and typecheck commands",
|
|
38
|
+
"No network, no installs, no writes outside the scratch directory",
|
|
39
|
+
"report that probe as unverified with its exact command instead of running it",
|
|
40
|
+
], "first-launch probe rule");
|
|
41
|
+
assert.ok(
|
|
42
|
+
!verify.includes("Run every probe only through command forms the parent authorized and only on isolated state the parent named"),
|
|
43
|
+
"the old rule makes every probe unverified when the handoff names no commands",
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("T16/W1: the scratch copy method cannot write outside the scratch directory", () => {
|
|
48
|
+
containsAll(verify, [
|
|
49
|
+
"copy the workspace files without `.git`",
|
|
50
|
+
"never `git worktree`, `git stash`, or a symlink into the workspace",
|
|
51
|
+
"set `HOME` and `TMPDIR` inside the scratch directory",
|
|
52
|
+
], "scratch copy method");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// T21 (L44): in after4, verify reported a behavior already present at the
|
|
56
|
+
// baseline (B x5, unchecked import amounts the request kept "as they do today")
|
|
57
|
+
// and a value far outside the realistic domain (B x4, sums near 9e15) as
|
|
58
|
+
// blockers; each forced a correction round. Blockers need provenance and
|
|
59
|
+
// realistic inputs; the rest are reported as advisories.
|
|
60
|
+
test("T21: verify grades severity by provenance and realistic inputs", () => {
|
|
61
|
+
containsAll(verify, [
|
|
62
|
+
"## Severity",
|
|
63
|
+
"A blocker is a defect the change caused or a spec item the change leaves unmet",
|
|
64
|
+
"Before calling a finding a blocker, reproduce it at the baseline",
|
|
65
|
+
"already present at the baseline and not asked to change is a pre-existing advisory",
|
|
66
|
+
"only with inputs outside the realistic domain",
|
|
67
|
+
"is an advisory",
|
|
68
|
+
"Any file, flag, or value a user can feed through the program's own commands is realistic input, hand-edited files included",
|
|
69
|
+
"a rule the request sets for one entry path holds for every path that creates the same data",
|
|
70
|
+
"Silently ignoring an option or value the user passed explicitly, with a success exit, is always a blocker",
|
|
71
|
+
"An unrequested change to the output, error text, or line numbering of a command that existed at the baseline is change-caused and a blocker, even when it looks like an improvement",
|
|
72
|
+
], "verify severity rule");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// T22 (L45): R-xh x4 ran about six verify/fix/recheck rounds on one delivery
|
|
76
|
+
// (each recheck found something new) and hit the 60 min timeout; the only rule
|
|
77
|
+
// was "resolve every unmet item before closing". Corrections are bounded.
|
|
78
|
+
test("T22: verify blockers get one correction batch and one scoped recheck", () => {
|
|
79
|
+
containsAll(verification, [
|
|
80
|
+
"5. **One correction**",
|
|
81
|
+
"one correction batch that fixes every reported blocker",
|
|
82
|
+
"one re-verify limited to those blockers",
|
|
83
|
+
"never a new full sweep",
|
|
84
|
+
"Blockers still open after that stop as one **Needs your decision**",
|
|
85
|
+
"Advisories never start a correction",
|
|
86
|
+
"A second correction runs automatically only when the recheck shows the same blocker still failing",
|
|
87
|
+
"a new finding never earns one",
|
|
88
|
+
], "verification correction bound");
|
|
89
|
+
containsAll(verify, [
|
|
90
|
+
"Keep the scratch path in your commands; never write it, or anything else, to a file outside the scratch",
|
|
91
|
+
"NO_UPDATE_NOTIFIER=1",
|
|
92
|
+
"Create the scratch directory with `mktemp -d` under the system temp directory, never inside the workspace",
|
|
93
|
+
"`NODE_COMPILE_CACHE`",
|
|
94
|
+
"leave no new file in the workspace",
|
|
95
|
+
], "verify scratch hygiene");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("T16: verify keeps its repository and tool boundaries", () => {
|
|
99
|
+
containsAll(verify, [
|
|
100
|
+
"Do not edit, write, or fix findings.",
|
|
101
|
+
"install dependencies, or mutate repository state",
|
|
102
|
+
"Treat every unexpected mutation as a blocker",
|
|
103
|
+
], "verify boundaries");
|
|
104
|
+
const tools = verify.split("---")[1] ?? "";
|
|
105
|
+
assert.ok(!/\n {2}- (edit|write)\n/.test(tools), "verify must not gain edit or write tools");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("T16: the verify handoff names probe command forms and the scratch location, never just read-only", () => {
|
|
109
|
+
const item = verification.split("\n").find((line) => line.startsWith("4. **Verify handoff**"));
|
|
110
|
+
assert.ok(item, "verify handoff item is missing");
|
|
111
|
+
containsAll(item, [
|
|
112
|
+
"the probe command forms (tests, CLI, typecheck)",
|
|
113
|
+
"probes on isolated state",
|
|
114
|
+
"fresh `mktemp -d` scratch copy",
|
|
115
|
+
"never just \"read-only\"",
|
|
116
|
+
], "verify handoff");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// T17b (L42): after4 B x5 read two files, then the core Evidence-budget rule
|
|
120
|
+
// fired and delegated a 1.5 min exploration on a small task the parent then
|
|
121
|
+
// implemented inline. The trigger itself must exclude reading for an inline write.
|
|
122
|
+
test("T17b: the core Evidence-budget rule never fires to prepare an inline write", () => {
|
|
123
|
+
const core = read("assets/orchestrator.md");
|
|
124
|
+
const rule = core.split("\n").find((line) => line.includes("**Evidence-budget rule**"));
|
|
125
|
+
assert.ok(rule, "Evidence-budget rule is missing");
|
|
126
|
+
assert.ok(rule.includes("never for reading before an inline write"), "Evidence-budget rule must exclude reading for an inline write");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("T17: the always-on Explore step delegates exploration only for a map the parent needs", () => {
|
|
130
|
+
for (const persona of ["gentleman", "neutral"] as const) {
|
|
131
|
+
const prompt = __testing.buildGentlePrompt(persona);
|
|
132
|
+
const step = prompt.split("\n").find((line) => line.startsWith("2. **Explore.**"));
|
|
133
|
+
assert.ok(step, "ODD step 2 Explore is missing");
|
|
134
|
+
containsAll(step, [
|
|
135
|
+
"Do not delegate exploration of files you will read anyway to work inline",
|
|
136
|
+
"explore only for a map you need to decide or route",
|
|
137
|
+
], "ODD Explore step");
|
|
138
|
+
}
|
|
139
|
+
});
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
5
7
|
import test from "node:test";
|
|
6
8
|
import {
|
|
7
9
|
gentleAiVersionPinMismatches,
|
|
10
|
+
installerPaths,
|
|
11
|
+
missingRequiredPaths,
|
|
8
12
|
reconcileContractsOnDisk,
|
|
9
13
|
reconcileGeneratedRuntimeSources,
|
|
14
|
+
requiredPaths,
|
|
10
15
|
} from "../scripts/verify-package-files.mjs";
|
|
11
16
|
import { INSTALLER_VERSION, RELEASE_BASE_URL, GENTLE_AI_WINDOWS_SOURCE_TAG } from "../scripts/gentle-ai-installer.mjs";
|
|
12
17
|
import { GENTLE_AI_VERSION } from "../lib/gentle-ai-binary.ts";
|
|
13
18
|
|
|
19
|
+
test("package resource CLI reconciles the STATUS diagnostic runtime with the generated inventory", () => {
|
|
20
|
+
const result = spawnSync(process.execPath, [join(process.cwd(), "scripts/verify-package-files.mjs")], {
|
|
21
|
+
cwd: process.cwd(), encoding: "utf8", env: { ...process.env, NODE_NO_WARNINGS: "1" },
|
|
22
|
+
});
|
|
23
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
24
|
+
assert.equal(result.stderr, "");
|
|
25
|
+
assert.match(result.stdout, /^gentle-pi package resource check passed \(/);
|
|
26
|
+
});
|
|
27
|
+
|
|
14
28
|
function makeFixtureRoot(): string {
|
|
15
29
|
return mkdtempSync(join(tmpdir(), "gentle-pi-verify-package-files-"));
|
|
16
30
|
}
|
|
@@ -179,3 +193,45 @@ test("both walks report no drift when sources, runtime/*.mjs, requiredPaths, and
|
|
|
179
193
|
rmSync(fixtureRoot, { recursive: true, force: true });
|
|
180
194
|
}
|
|
181
195
|
});
|
|
196
|
+
|
|
197
|
+
test("required package paths include every browser installation wizard file", () => {
|
|
198
|
+
const expected = [
|
|
199
|
+
"bin/gentle-shell-install.mjs",
|
|
200
|
+
"scripts/installer-server.mjs",
|
|
201
|
+
"scripts/installer-runner.mjs",
|
|
202
|
+
"scripts/installer-preflight.mjs",
|
|
203
|
+
"scripts/installer-probes.mjs",
|
|
204
|
+
"scripts/installer-downloads.mjs",
|
|
205
|
+
"scripts/main-channel.mjs",
|
|
206
|
+
"scripts/installer-windows.mjs",
|
|
207
|
+
"scripts/installer-windows-artifacts.json",
|
|
208
|
+
// The bundled install the wizard runs for a new release installation (T1c).
|
|
209
|
+
"scripts/bundled-wizard.mjs",
|
|
210
|
+
"scripts/bundled-install.mjs",
|
|
211
|
+
"scripts/bootstrap.sh",
|
|
212
|
+
"scripts/bootstrap.cmd",
|
|
213
|
+
"assets/install-wizard/index.html",
|
|
214
|
+
"assets/install-wizard/wizard.js",
|
|
215
|
+
"assets/install-wizard/wizard.css",
|
|
216
|
+
];
|
|
217
|
+
assert.deepEqual([...installerPaths].sort(), [...expected].sort());
|
|
218
|
+
for (const relativePath of expected) assert.ok(requiredPaths.includes(relativePath), relativePath);
|
|
219
|
+
const repositoryRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
220
|
+
assert.deepEqual(missingRequiredPaths(repositoryRoot, installerPaths), []);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("missing required paths are reported, including directories in place of files", () => {
|
|
224
|
+
const fixtureRoot = makeFixtureRoot();
|
|
225
|
+
try {
|
|
226
|
+
mkdirSync(join(fixtureRoot, "bin"), { recursive: true });
|
|
227
|
+
mkdirSync(join(fixtureRoot, "assets/install-wizard/index.html"), { recursive: true });
|
|
228
|
+
writeFileSync(join(fixtureRoot, "bin/gentle-shell-install.mjs"), "// entry\n");
|
|
229
|
+
|
|
230
|
+
assert.deepEqual(
|
|
231
|
+
missingRequiredPaths(fixtureRoot, ["bin/gentle-shell-install.mjs", "assets/install-wizard/index.html", "scripts/installer-server.mjs"]),
|
|
232
|
+
["assets/install-wizard/index.html", "scripts/installer-server.mjs"],
|
|
233
|
+
);
|
|
234
|
+
} finally {
|
|
235
|
+
rmSync(fixtureRoot, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
@@ -5,12 +5,17 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
import { dirname, resolve } from "node:path";
|
|
8
|
-
import { CURSOR_MARKER, Editor, visibleWidth } from "@earendil-works/pi-tui";
|
|
8
|
+
import { CURSOR_MARKER, Editor, TuiMainScreen, visibleWidth } from "@earendil-works/pi-tui";
|
|
9
9
|
import { AUDITED_PI_EDITOR_VERSIONS, createVimEditorAdapter, isAuditedPiEditorVersion } from "../lib/vim-editor-adapter.ts";
|
|
10
10
|
import { resolveVimRuntime, VIM_AGENT_INDEX_PATTERN, VIM_CLI_ENTRY_PATTERN } from "../extensions/gentle-shell.ts";
|
|
11
11
|
import { VimOperatorEngine } from "../lib/vim-operator-engine.ts";
|
|
12
12
|
import { VimVisualEngine } from "../lib/vim-visual-engine.ts";
|
|
13
13
|
|
|
14
|
+
function resolveFakeCursors(rows: string[], hardware = false): string[] {
|
|
15
|
+
const host = TuiMainScreen.prototype as unknown as { resolveFakeCursors(rows: string[]): string[] };
|
|
16
|
+
return host.resolveFakeCursors.call({ showHardwareCursor: hardware }, rows);
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
function editor(): Editor {
|
|
15
20
|
return new Editor({ terminal: { rows: 24 }, requestRender() {} } as never, { borderColor: (s: string) => s } as never);
|
|
16
21
|
}
|
|
@@ -20,6 +25,8 @@ function editor(): Editor {
|
|
|
20
25
|
function paintedCells(row: string): Array<{ char: string; inverse: boolean }> {
|
|
21
26
|
const cells: Array<{ char: string; inverse: boolean }> = [];
|
|
22
27
|
let inverse = false;
|
|
28
|
+
// Decode the actual host post-pass, including hardware cursor suppression.
|
|
29
|
+
row = resolveFakeCursors([row])[0]!;
|
|
23
30
|
const tokens = row.match(/\x1b\[[0-9;]*m|\x1b_pi:c\x07|[^\x1b]/g) ?? [];
|
|
24
31
|
assert.equal(tokens.join(""), row, "only known editor escape sequences are present");
|
|
25
32
|
for (const token of tokens) {
|
|
@@ -229,8 +236,8 @@ function assertInstalledPiPairBehavior(version: string, EditorClass: typeof Edit
|
|
|
229
236
|
assert.equal(adapter.renderSelection(18, { line: 0, col: 0 }, { line: 0, col: 6 }, scrolled).length, scrolled.length);
|
|
230
237
|
}
|
|
231
238
|
|
|
232
|
-
test(
|
|
233
|
-
assert.equal(bundledAgent.VERSION,
|
|
239
|
+
test(`actual bundled Pi ${INSTALLED_PI} proves editing, undo, paste, selection, wrap and autocomplete`, () => {
|
|
240
|
+
assert.equal(bundledAgent.VERSION, INSTALLED_PI);
|
|
234
241
|
assertInstalledPiPairBehavior(bundledAgent.VERSION, BundledEditor, bundledAgent.CustomEditor, bundledAgent.VERSION);
|
|
235
242
|
});
|
|
236
243
|
|
|
@@ -270,12 +277,12 @@ test("resolveVimRuntime resolves local bundle cli entrypoint when provided", ()
|
|
|
270
277
|
});
|
|
271
278
|
|
|
272
279
|
test("audited Pi editor releases are one frozen exact allowlist that includes the installed TUI", () => {
|
|
273
|
-
assert.deepEqual([...AUDITED_PI_EDITOR_VERSIONS], ["0.99.1", "0.99.2", "1.0.0"]);
|
|
274
|
-
assert.equal(
|
|
280
|
+
assert.deepEqual([...AUDITED_PI_EDITOR_VERSIONS], ["0.99.1", "0.99.2", "1.0.0", "1.1.0", "1.2.0"]);
|
|
281
|
+
assert.equal(bundledAgent.VERSION, INSTALLED_PI, "bundled and local metadata must describe the same actual installed release");
|
|
275
282
|
assert.ok(Object.isFrozen(AUDITED_PI_EDITOR_VERSIONS));
|
|
276
283
|
assert.throws(() => (AUDITED_PI_EDITOR_VERSIONS as unknown as string[]).push("0.99.3"), TypeError);
|
|
277
284
|
assert.ok(isAuditedPiEditorVersion(INSTALLED_PI), `installed pi-tui ${INSTALLED_PI} must be audited`);
|
|
278
|
-
for (const unknown of ["0.99.3", "0.99.10", "0.99", "v0.99.2", " 0.99.2", "0.99.2-rc.1", ">=0.99.2", "1.0.1", ">=1.0.0", "v1.0.0", " 1.0.0", "1.0.0-rc.1", undefined, 99.2]) {
|
|
285
|
+
for (const unknown of ["0.99.3", "0.99.10", "0.99", "v0.99.2", " 0.99.2", "0.99.2-rc.1", ">=0.99.2", "1.0.1", ">=1.0.0", "v1.0.0", " 1.0.0", "1.0.0-rc.1", "1.2.1", "1.2.0-rc.1", "v1.2.0", ">=1.2.0", undefined, 99.2]) {
|
|
279
286
|
assert.equal(isAuditedPiEditorVersion(unknown), false, String(unknown));
|
|
280
287
|
}
|
|
281
288
|
});
|
|
@@ -302,17 +309,19 @@ function fabricatedPiCli(t: test.TestContext, version: string, tuiVersion = vers
|
|
|
302
309
|
}
|
|
303
310
|
|
|
304
311
|
test("bundled host admits each audited release and rejects unknown or mismatched bundle metadata", (t) => {
|
|
305
|
-
for (const version of
|
|
312
|
+
for (const version of AUDITED_PI_EDITOR_VERSIONS) {
|
|
306
313
|
assert.deepEqual(resolveVimRuntime(fabricatedPiCli(t, version), bundledAgent.CustomEditor), { version, editorClass: BundledEditor }, version);
|
|
307
314
|
}
|
|
308
315
|
for (const version of ["0.99.3", "1.0.1", ">=1.0.0", "v1.0.0"]) {
|
|
309
316
|
assert.equal(resolveVimRuntime(fabricatedPiCli(t, version), bundledAgent.CustomEditor), undefined, version);
|
|
310
317
|
}
|
|
311
318
|
assert.equal(resolveVimRuntime(fabricatedPiCli(t, "1.0.0", "1.0.0", "0.99.2"), bundledAgent.CustomEditor), undefined);
|
|
319
|
+
assert.equal(resolveVimRuntime(fabricatedPiCli(t, "1.2.1"), bundledAgent.CustomEditor), undefined);
|
|
320
|
+
assert.equal(resolveVimRuntime(fabricatedPiCli(t, "1.2.0", "1.2.0", "1.1.0"), bundledAgent.CustomEditor), undefined);
|
|
312
321
|
});
|
|
313
322
|
|
|
314
323
|
test("installed agent/TUI pair admits each audited release and skips unknown or mismatched pairs", (t) => {
|
|
315
|
-
for (const version of
|
|
324
|
+
for (const version of AUDITED_PI_EDITOR_VERSIONS) {
|
|
316
325
|
assert.deepEqual(resolveVimRuntime(fabricatedPiCli(t, version), runtimeAgent.CustomEditor), { version, editorClass: runtimeTui.Editor }, version);
|
|
317
326
|
}
|
|
318
327
|
// Rejected candidates must resolve to the real local pair, not their claim.
|
|
@@ -325,7 +334,7 @@ test("installed agent/TUI pair admits each audited release and skips unknown or
|
|
|
325
334
|
|
|
326
335
|
test("adapter admits each verified audited release and rejects unknown or mismatched claims without mutation", () => {
|
|
327
336
|
const host = () => new BundledEditor!({ terminal: { rows: 6 }, requestRender() {} } as never, { borderColor: (s: string) => s } as never);
|
|
328
|
-
for (const version of
|
|
337
|
+
for (const version of AUDITED_PI_EDITOR_VERSIONS) {
|
|
329
338
|
const e = host();
|
|
330
339
|
e.setText("alpha beta");
|
|
331
340
|
const adapter = createVimEditorAdapter(e, version, BundledEditor, version);
|
|
@@ -343,6 +352,8 @@ test("adapter admits each verified audited release and rejects unknown or mismat
|
|
|
343
352
|
assert.throws(() => createVimEditorAdapter(editor(), version), /unsupported/i, version);
|
|
344
353
|
}
|
|
345
354
|
assert.throws(() => createVimEditorAdapter(e, "1.0.0", BundledEditor, "0.99.2"), /unsupported/i);
|
|
355
|
+
assert.throws(() => createVimEditorAdapter(e, "1.2.1", BundledEditor, "1.2.1"), /unsupported/i);
|
|
356
|
+
assert.throws(() => createVimEditorAdapter(e, "1.2.0", BundledEditor, "1.1.0"), /unsupported/i);
|
|
346
357
|
assert.equal(e.getText(), "untouched");
|
|
347
358
|
assert.deepEqual(e.getCursor(), cursor);
|
|
348
359
|
assert.deepEqual(e.render(30), frame);
|
|
@@ -638,24 +649,39 @@ test("operator insertion and registered marker guards keep one undo snapshot", (
|
|
|
638
649
|
assert.equal(paste.getExpandedText(), "z".repeat(1001));
|
|
639
650
|
});
|
|
640
651
|
|
|
652
|
+
test("Pi 1.2 selection preserves fake markers and host hardware-cursor suppression", () => {
|
|
653
|
+
const e = editor();
|
|
654
|
+
e.setText("abcd");
|
|
655
|
+
e.focused = true;
|
|
656
|
+
const adapter = createVimEditorAdapter(e, INSTALLED_PI);
|
|
657
|
+
adapter.move({ line: 0, col: 1 });
|
|
658
|
+
const rows = adapter.renderSelection(20, { line: 0, col: 0 }, { line: 0, col: 4 });
|
|
659
|
+
assert.ok(rows[1]!.includes(`${CURSOR_MARKER}\x1b_pi:fc\x07`));
|
|
660
|
+
assert.ok(rows[1]!.includes("\x1b_pi:/fc\x07"));
|
|
661
|
+
for (const hardware of [false, true]) {
|
|
662
|
+
const resolved = resolveFakeCursors(rows, hardware);
|
|
663
|
+
assert.deepEqual(paintedCells(resolved[1]!).slice(0, 4), "abcd".split("").map(char => ({ char, inverse: true })));
|
|
664
|
+
assert.ok(resolved.every(row => visibleWidth(row) === 20));
|
|
665
|
+
}
|
|
666
|
+
const boundary = adapter.renderSelection(20, { line: 0, col: 0 }, { line: 0, col: 1 });
|
|
667
|
+
assert.ok(boundary[1]!.includes(`${CURSOR_MARKER}\x1b_pi:fc\x07`));
|
|
668
|
+
assert.deepEqual(selectedColumns(resolveFakeCursors(boundary, true)[1]!), [0]);
|
|
669
|
+
assert.deepEqual(selectedColumns(resolveFakeCursors(boundary, false)[1]!), [0, 1]);
|
|
670
|
+
const legacy = e.render(20);
|
|
671
|
+
legacy[1] = legacy[1]!.replaceAll("\x1b_pi:fc\x07", "\x1b[7m").replaceAll("\x1b_pi:/fc\x07", "\x1b[0m");
|
|
672
|
+
assert.deepEqual(selectedColumns(adapter.renderSelection(20, { line: 0, col: 0 }, { line: 0, col: 4 }, legacy)[1]!), [0, 1, 2, 3]);
|
|
673
|
+
const unknown = e.render(20).map(row => row.replace("\x1b_pi:fc\x07", "\x1b_pi:unknown\x07"));
|
|
674
|
+
assert.deepEqual(adapter.renderSelection(20, { line: 0, col: 0 }, { line: 0, col: 4 }, unknown), unknown);
|
|
675
|
+
});
|
|
676
|
+
|
|
641
677
|
test("selection survives fake cursor reset and retains surrounding color", () => {
|
|
642
678
|
const e = editor();
|
|
643
679
|
e.setText("abcd");
|
|
644
680
|
createVimEditorAdapter(e, INSTALLED_PI).move({ line: 0, col: 1 });
|
|
645
681
|
const frame = createVimEditorAdapter(e, INSTALLED_PI).renderSelection(20, { line: 0, col: 0 }, { line: 0, col: 4 });
|
|
646
682
|
const row = frame.find((line) => line.includes("a")) ?? frame.join("");
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
for (const token of row.matchAll(/\x1b\[[0-9;]*m|[^\x1b]/g)) {
|
|
650
|
-
const value = token[0];
|
|
651
|
-
if (value.startsWith("\x1b[")) {
|
|
652
|
-
for (const code of value.slice(2, -1).split(";").map(Number)) {
|
|
653
|
-
if (code === 0 || code === 27) reverse = false;
|
|
654
|
-
if (code === 7) reverse = true;
|
|
655
|
-
}
|
|
656
|
-
} else if ("abcd".includes(value)) selected.push(reverse ? value : `!${value}`);
|
|
657
|
-
}
|
|
658
|
-
assert.deepEqual(selected, ["a", "b", "c", "d"], "every visible cell must have effective reverse SGR");
|
|
683
|
+
assert.deepEqual(paintedCells(row).slice(0, 4), "abcd".split("").map(char => ({ char, inverse: true })),
|
|
684
|
+
"every visible cell must have effective reverse SGR after the host post-pass");
|
|
659
685
|
});
|
|
660
686
|
|
|
661
687
|
test("truecolor channels do not change inverse state across selected and unselected cells", () => {
|
|
@@ -13,7 +13,7 @@ test("missing settings use responsive defaults and return independent copies", (
|
|
|
13
13
|
assert.equal(DEFAULT_VISUAL_SETTINGS.statusPlacement, "auto");
|
|
14
14
|
assert.equal(DEFAULT_VISUAL_SETTINGS.headerPlacement, "top");
|
|
15
15
|
assert.equal(DEFAULT_VISUAL_SETTINGS.density, "comfortable");
|
|
16
|
-
assert.deepEqual(DEFAULT_VISUAL_SETTINGS.visibility, { changes: true, rdd: true, agents: true, todo: true, usageCost: true, modelDetails: true });
|
|
16
|
+
assert.deepEqual(DEFAULT_VISUAL_SETTINGS.visibility, { changes: true, rdd: true, agents: true, todo: true, usageCost: true, modelDetails: true, contextWarning: true });
|
|
17
17
|
resolved.settings.visibility.agents = false;
|
|
18
18
|
assert.equal(DEFAULT_VISUAL_SETTINGS.visibility.agents, true);
|
|
19
19
|
assert.equal(resolveVisualSettings({ gentlePiConfigHome: home }).settings.visibility.agents, true);
|
|
@@ -29,13 +29,13 @@ test("strict versioned parsing rejects malformed and partial settings", () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
test("Sections rows follow one ordered key list that covers every visibility key", () => {
|
|
32
|
-
assert.deepEqual(VISUAL_SECTION_KEYS, ["changes", "rdd", "agents", "todo", "usageCost", "modelDetails"]);
|
|
32
|
+
assert.deepEqual(VISUAL_SECTION_KEYS, ["changes", "rdd", "agents", "todo", "usageCost", "modelDetails", "contextWarning"]);
|
|
33
33
|
assert.deepEqual([...VISUAL_SECTION_KEYS].sort(), Object.keys(DEFAULT_VISUAL_SETTINGS.visibility).sort());
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
test("settings saved before the rdd section existed load as valid with rdd shown", () => {
|
|
37
37
|
const legacy = { schema: VISUAL_SCHEMA, ...DEFAULT_VISUAL_SETTINGS, visibility: { changes: false, agents: true, todo: false, usageCost: true, modelDetails: false } };
|
|
38
|
-
const expected = { ...DEFAULT_VISUAL_SETTINGS, visibility: { changes: false, rdd: true, agents: true, todo: false, usageCost: true, modelDetails: false } };
|
|
38
|
+
const expected = { ...DEFAULT_VISUAL_SETTINGS, visibility: { changes: false, rdd: true, agents: true, todo: false, usageCost: true, modelDetails: false, contextWarning: true } };
|
|
39
39
|
assert.deepEqual(parseVisualSettingsFile(JSON.stringify(legacy)), expected);
|
|
40
40
|
assert.deepEqual(parseVisualSettingsFile(JSON.stringify({ ...legacy, visibility: { ...expected.visibility, rdd: false } })), { ...expected, visibility: { ...expected.visibility, rdd: false } });
|
|
41
41
|
for (const visibility of [{ ...legacy.visibility, extra: true }, { ...legacy.visibility, rdd: "no" }, { ...legacy.visibility, todo: "yes" }, { agents: true, todo: true, usageCost: true, modelDetails: true, rdd: true }]) {
|
|
@@ -52,6 +52,35 @@ test("settings saved before the rdd section existed load as valid with rdd shown
|
|
|
52
52
|
} finally { rmSync(home, { recursive: true, force: true }); }
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
test("settings saved before contextWarning existed load it shown and preserve rdd", () => {
|
|
56
|
+
// Both historical shapes: the earliest pre-rdd visibility and the previous-current shape that
|
|
57
|
+
// already had rdd but not contextWarning. Only the missing added keys default to shown.
|
|
58
|
+
const shared = { schema: VISUAL_SCHEMA, ...DEFAULT_VISUAL_SETTINGS };
|
|
59
|
+
const preRdd = { ...shared, visibility: { changes: false, agents: true, todo: false, usageCost: true, modelDetails: false } };
|
|
60
|
+
assert.deepEqual(parseVisualSettingsFile(JSON.stringify(preRdd)), { ...DEFAULT_VISUAL_SETTINGS,
|
|
61
|
+
visibility: { changes: false, rdd: true, agents: true, todo: false, usageCost: true, modelDetails: false, contextWarning: true } });
|
|
62
|
+
const preContext = { ...shared, visibility: { changes: false, rdd: false, agents: true, todo: false, usageCost: true, modelDetails: false } };
|
|
63
|
+
assert.deepEqual(parseVisualSettingsFile(JSON.stringify(preContext)), { ...DEFAULT_VISUAL_SETTINGS,
|
|
64
|
+
visibility: { changes: false, rdd: false, agents: true, todo: false, usageCost: true, modelDetails: false, contextWarning: true } });
|
|
65
|
+
const withoutRdd = { ...shared, visibility: { changes: true, agents: true, todo: true, usageCost: true, modelDetails: true, contextWarning: false } };
|
|
66
|
+
assert.deepEqual(parseVisualSettingsFile(JSON.stringify(withoutRdd)), { ...DEFAULT_VISUAL_SETTINGS,
|
|
67
|
+
visibility: { changes: true, rdd: true, agents: true, todo: true, usageCost: true, modelDetails: true, contextWarning: false } });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("a present non-boolean added key in a partial legacy shape is rejected before default fill", () => {
|
|
71
|
+
// Missing added keys default to shown, but a key that is present must already be a
|
|
72
|
+
// boolean: `null` (or any other value) must not be silently coerced by the fill.
|
|
73
|
+
const base = { changes: true, agents: true, todo: true, usageCost: true, modelDetails: true };
|
|
74
|
+
for (const visibility of [{ ...base, rdd: null }, { ...base, contextWarning: null }]) {
|
|
75
|
+
assert.equal(parseVisualSettingsFile(JSON.stringify({ schema: VISUAL_SCHEMA, ...DEFAULT_VISUAL_SETTINGS, visibility })), undefined,
|
|
76
|
+
`${JSON.stringify(visibility)} must stay invalid`);
|
|
77
|
+
}
|
|
78
|
+
const previousCurrent = structuredClone(DEFAULT_VISUAL_SETTINGS) as unknown as { visibility: Record<string, unknown> };
|
|
79
|
+
delete previousCurrent.visibility.contextWarning;
|
|
80
|
+
previousCurrent.visibility.rdd = null;
|
|
81
|
+
assert.equal(parseVisualSettingsFile(JSON.stringify({ schema: VISUAL_SCHEMA, ...previousCurrent })), undefined);
|
|
82
|
+
});
|
|
83
|
+
|
|
55
84
|
test("an unreadable settings path is a read error, not malformed content", () => {
|
|
56
85
|
const home = mkdtempSync(join(tmpdir(), "visual-policy-"));
|
|
57
86
|
try {
|