gsd-pi 2.63.0-dev.d04bbc5 → 2.64.0-dev.05b8a94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless.js +3 -1
- package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +22 -7
- package/dist/resources/extensions/bg-shell/process-manager.js +6 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +5 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +98 -1
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto.js +5 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +24 -13
- package/dist/resources/extensions/gsd/bootstrap/notify-interceptor.js +28 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +15 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +20 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +103 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +224 -0
- package/dist/resources/extensions/gsd/notification-store.js +268 -0
- package/dist/resources/extensions/gsd/notification-widget.js +56 -0
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
- package/dist/resources/extensions/gsd/preferences-types.js +4 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
- package/dist/resources/extensions/gsd/preferences.js +4 -0
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +8 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -19
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/routes-manifest.json +6 -0
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +3 -0
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -0
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -0
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +20 -19
- package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/Vbx2-SrSBOgta6576xj9m/_buildManifest.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/_global-error/page-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/boot/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/notifications/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +26 -9
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +100 -4
- package/packages/pi-agent-core/src/agent-loop.ts +43 -12
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +38 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +11 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +64 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +10 -0
- package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +42 -0
- package/packages/pi-coding-agent/src/core/resource-loader.ts +5 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +9 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +33 -0
- package/packages/pi-tui/dist/components/loader.d.ts +4 -2
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +27 -9
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/dist/components/text.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/text.js +2 -0
- package/packages/pi-tui/dist/components/text.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +2 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +35 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/components/loader.ts +27 -10
- package/packages/pi-tui/src/components/text.ts +1 -0
- package/packages/pi-tui/src/tui.ts +32 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +19 -7
- package/src/resources/extensions/bg-shell/process-manager.ts +8 -2
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +122 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +25 -13
- package/src/resources/extensions/gsd/bootstrap/notify-interceptor.ts +34 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +19 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +28 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +139 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +267 -0
- package/src/resources/extensions/gsd/notification-store.ts +288 -0
- package/src/resources/extensions/gsd/notification-widget.ts +68 -0
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
- package/src/resources/extensions/gsd/preferences-types.ts +28 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
- package/src/resources/extensions/gsd/preferences.ts +4 -0
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +249 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +163 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +13 -0
- package/dist/web/standalone/.next/static/chunks/app/_global-error/page-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/boot/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/vIq9fmvRUaFOpguoX5j4W/_buildManifest.js +0 -1
- /package/dist/web/standalone/.next/static/{vIq9fmvRUaFOpguoX5j4W → Vbx2-SrSBOgta6576xj9m}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,999 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pre-execution-checks.test.ts — Unit tests for pre-execution validation checks.
|
|
3
|
+
*
|
|
4
|
+
* Tests all 4 check types:
|
|
5
|
+
* 1. Package existence — npm view mocking, timeout handling
|
|
6
|
+
* 2. File path consistency — files exist vs prior expected_output
|
|
7
|
+
* 3. Task ordering — detect impossible read-before-create
|
|
8
|
+
* 4. Interface contracts — contradictory function signatures
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, test, mock } from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
extractPackageReferences,
|
|
19
|
+
checkFilePathConsistency,
|
|
20
|
+
checkTaskOrdering,
|
|
21
|
+
checkInterfaceContracts,
|
|
22
|
+
runPreExecutionChecks,
|
|
23
|
+
normalizeFilePath,
|
|
24
|
+
type PreExecutionResult,
|
|
25
|
+
} from "../pre-execution-checks.ts";
|
|
26
|
+
import type { TaskRow } from "../gsd-db.ts";
|
|
27
|
+
|
|
28
|
+
// ─── Test Fixtures ───────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a minimal TaskRow for testing.
|
|
32
|
+
*/
|
|
33
|
+
function createTask(overrides: Partial<TaskRow> = {}): TaskRow {
|
|
34
|
+
return {
|
|
35
|
+
milestone_id: "M001",
|
|
36
|
+
slice_id: "S01",
|
|
37
|
+
id: overrides.id ?? "T01",
|
|
38
|
+
title: "Test Task",
|
|
39
|
+
status: "pending",
|
|
40
|
+
one_liner: "",
|
|
41
|
+
narrative: "",
|
|
42
|
+
verification_result: "",
|
|
43
|
+
duration: "",
|
|
44
|
+
completed_at: null,
|
|
45
|
+
blocker_discovered: false,
|
|
46
|
+
deviations: "",
|
|
47
|
+
known_issues: "",
|
|
48
|
+
key_files: [],
|
|
49
|
+
key_decisions: [],
|
|
50
|
+
full_summary_md: "",
|
|
51
|
+
description: overrides.description ?? "",
|
|
52
|
+
estimate: "",
|
|
53
|
+
files: overrides.files ?? [],
|
|
54
|
+
verify: "",
|
|
55
|
+
inputs: overrides.inputs ?? [],
|
|
56
|
+
expected_output: overrides.expected_output ?? [],
|
|
57
|
+
observability_impact: "",
|
|
58
|
+
full_plan_md: "",
|
|
59
|
+
sequence: overrides.sequence ?? 0,
|
|
60
|
+
...overrides,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── Package Reference Extraction Tests ──────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
describe("extractPackageReferences", () => {
|
|
67
|
+
test("extracts npm install patterns", () => {
|
|
68
|
+
const desc = "Run npm install lodash then npm i axios";
|
|
69
|
+
const packages = extractPackageReferences(desc);
|
|
70
|
+
assert.deepEqual(packages.sort(), ["axios", "lodash"]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("extracts yarn add patterns", () => {
|
|
74
|
+
const desc = "yarn add react-dom";
|
|
75
|
+
const packages = extractPackageReferences(desc);
|
|
76
|
+
assert.deepEqual(packages, ["react-dom"]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("extracts scoped packages", () => {
|
|
80
|
+
const desc = "npm install @types/node @babel/core";
|
|
81
|
+
const packages = extractPackageReferences(desc);
|
|
82
|
+
assert.ok(packages.includes("@types/node"));
|
|
83
|
+
assert.ok(packages.includes("@babel/core"));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("extracts require statements from code blocks", () => {
|
|
87
|
+
const desc = `
|
|
88
|
+
\`\`\`javascript
|
|
89
|
+
const fs = require('fs-extra');
|
|
90
|
+
const path = require('path');
|
|
91
|
+
\`\`\`
|
|
92
|
+
`;
|
|
93
|
+
const packages = extractPackageReferences(desc);
|
|
94
|
+
assert.ok(packages.includes("fs-extra"));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("extracts import statements from code blocks", () => {
|
|
98
|
+
const desc = `
|
|
99
|
+
\`\`\`typescript
|
|
100
|
+
import express from 'express';
|
|
101
|
+
import { Router } from 'express';
|
|
102
|
+
import type { Request } from 'express';
|
|
103
|
+
\`\`\`
|
|
104
|
+
`;
|
|
105
|
+
const packages = extractPackageReferences(desc);
|
|
106
|
+
assert.ok(packages.includes("express"));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("ignores relative imports", () => {
|
|
110
|
+
const desc = `import { foo } from './local-file';`;
|
|
111
|
+
const packages = extractPackageReferences(desc);
|
|
112
|
+
assert.deepEqual(packages, []);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("ignores node builtins", () => {
|
|
116
|
+
const desc = `import fs from 'node:fs';`;
|
|
117
|
+
const packages = extractPackageReferences(desc);
|
|
118
|
+
assert.deepEqual(packages, []);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("normalizes package subpaths", () => {
|
|
122
|
+
const desc = "npm install lodash/get";
|
|
123
|
+
const packages = extractPackageReferences(desc);
|
|
124
|
+
assert.deepEqual(packages, ["lodash"]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("handles empty description", () => {
|
|
128
|
+
const packages = extractPackageReferences("");
|
|
129
|
+
assert.deepEqual(packages, []);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("ignores flags in npm install", () => {
|
|
133
|
+
const desc = "npm install -D typescript";
|
|
134
|
+
const packages = extractPackageReferences(desc);
|
|
135
|
+
assert.ok(packages.includes("typescript"));
|
|
136
|
+
assert.ok(!packages.includes("-D"));
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ─── File Path Consistency Tests ─────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
describe("checkFilePathConsistency", () => {
|
|
143
|
+
let tempDir: string;
|
|
144
|
+
|
|
145
|
+
test("passes when files exist on disk", () => {
|
|
146
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
147
|
+
mkdirSync(tempDir, { recursive: true });
|
|
148
|
+
writeFileSync(join(tempDir, "existing.ts"), "// content");
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const tasks = [
|
|
152
|
+
createTask({
|
|
153
|
+
id: "T01",
|
|
154
|
+
files: ["existing.ts"],
|
|
155
|
+
inputs: [],
|
|
156
|
+
expected_output: [],
|
|
157
|
+
}),
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
161
|
+
assert.deepEqual(results, []);
|
|
162
|
+
} finally {
|
|
163
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("passes when files are in prior expected_output", () => {
|
|
168
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
169
|
+
mkdirSync(tempDir, { recursive: true });
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const tasks = [
|
|
173
|
+
createTask({
|
|
174
|
+
id: "T01",
|
|
175
|
+
sequence: 0,
|
|
176
|
+
files: [],
|
|
177
|
+
inputs: [],
|
|
178
|
+
expected_output: ["generated.ts"],
|
|
179
|
+
}),
|
|
180
|
+
createTask({
|
|
181
|
+
id: "T02",
|
|
182
|
+
sequence: 1,
|
|
183
|
+
files: ["generated.ts"],
|
|
184
|
+
inputs: [],
|
|
185
|
+
expected_output: [],
|
|
186
|
+
}),
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
190
|
+
assert.deepEqual(results, []);
|
|
191
|
+
} finally {
|
|
192
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("fails when files don't exist and not in prior outputs", () => {
|
|
197
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
198
|
+
mkdirSync(tempDir, { recursive: true });
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const tasks = [
|
|
202
|
+
createTask({
|
|
203
|
+
id: "T01",
|
|
204
|
+
files: ["nonexistent.ts"],
|
|
205
|
+
inputs: [],
|
|
206
|
+
expected_output: [],
|
|
207
|
+
}),
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
211
|
+
assert.equal(results.length, 1);
|
|
212
|
+
assert.equal(results[0].category, "file");
|
|
213
|
+
assert.equal(results[0].passed, false);
|
|
214
|
+
assert.equal(results[0].blocking, true);
|
|
215
|
+
assert.ok(results[0].message.includes("nonexistent.ts"));
|
|
216
|
+
} finally {
|
|
217
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("checks both files and inputs arrays", () => {
|
|
222
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
223
|
+
mkdirSync(tempDir, { recursive: true });
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const tasks = [
|
|
227
|
+
createTask({
|
|
228
|
+
id: "T01",
|
|
229
|
+
files: ["missing-file.ts"],
|
|
230
|
+
inputs: ["missing-input.ts"],
|
|
231
|
+
expected_output: [],
|
|
232
|
+
}),
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
236
|
+
assert.equal(results.length, 2);
|
|
237
|
+
assert.ok(results.some((r) => r.target === "missing-file.ts"));
|
|
238
|
+
assert.ok(results.some((r) => r.target === "missing-input.ts"));
|
|
239
|
+
} finally {
|
|
240
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("skips empty file strings", () => {
|
|
245
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
246
|
+
mkdirSync(tempDir, { recursive: true });
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const tasks = [
|
|
250
|
+
createTask({
|
|
251
|
+
id: "T01",
|
|
252
|
+
files: ["", " "],
|
|
253
|
+
inputs: [],
|
|
254
|
+
expected_output: [],
|
|
255
|
+
}),
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
259
|
+
assert.deepEqual(results, []);
|
|
260
|
+
} finally {
|
|
261
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// ─── Path Normalization Tests ────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
describe("normalizeFilePath", () => {
|
|
269
|
+
test("strips leading ./", () => {
|
|
270
|
+
assert.equal(normalizeFilePath("./src/a.ts"), "src/a.ts");
|
|
271
|
+
assert.equal(normalizeFilePath("././foo.ts"), "foo.ts");
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("normalizes backslashes to forward slashes", () => {
|
|
275
|
+
assert.equal(normalizeFilePath("src\\a.ts"), "src/a.ts");
|
|
276
|
+
assert.equal(normalizeFilePath("src\\sub\\file.ts"), "src/sub/file.ts");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("removes duplicate slashes", () => {
|
|
280
|
+
assert.equal(normalizeFilePath("src//a.ts"), "src/a.ts");
|
|
281
|
+
assert.equal(normalizeFilePath("src///sub//file.ts"), "src/sub/file.ts");
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("handles empty string", () => {
|
|
285
|
+
assert.equal(normalizeFilePath(""), "");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("removes trailing slash", () => {
|
|
289
|
+
assert.equal(normalizeFilePath("src/"), "src");
|
|
290
|
+
assert.equal(normalizeFilePath("src/sub/"), "src/sub");
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("handles paths without any normalization needed", () => {
|
|
294
|
+
assert.equal(normalizeFilePath("src/a.ts"), "src/a.ts");
|
|
295
|
+
assert.equal(normalizeFilePath("index.ts"), "index.ts");
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe("checkFilePathConsistency with path normalization", () => {
|
|
300
|
+
let tempDir: string;
|
|
301
|
+
|
|
302
|
+
test("./path matches path in prior expected_output", () => {
|
|
303
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
304
|
+
mkdirSync(tempDir, { recursive: true });
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const tasks = [
|
|
308
|
+
createTask({
|
|
309
|
+
id: "T01",
|
|
310
|
+
sequence: 0,
|
|
311
|
+
files: [],
|
|
312
|
+
inputs: [],
|
|
313
|
+
expected_output: ["src/generated.ts"], // Output without ./
|
|
314
|
+
}),
|
|
315
|
+
createTask({
|
|
316
|
+
id: "T02",
|
|
317
|
+
sequence: 1,
|
|
318
|
+
files: ["./src/generated.ts"], // Input with ./
|
|
319
|
+
inputs: [],
|
|
320
|
+
expected_output: [],
|
|
321
|
+
}),
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
325
|
+
assert.deepEqual(results, [], "Should pass because ./src/generated.ts matches src/generated.ts");
|
|
326
|
+
} finally {
|
|
327
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("path matches ./path in prior expected_output", () => {
|
|
332
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
333
|
+
mkdirSync(tempDir, { recursive: true });
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const tasks = [
|
|
337
|
+
createTask({
|
|
338
|
+
id: "T01",
|
|
339
|
+
sequence: 0,
|
|
340
|
+
files: [],
|
|
341
|
+
inputs: [],
|
|
342
|
+
expected_output: ["./src/generated.ts"], // Output with ./
|
|
343
|
+
}),
|
|
344
|
+
createTask({
|
|
345
|
+
id: "T02",
|
|
346
|
+
sequence: 1,
|
|
347
|
+
files: ["src/generated.ts"], // Input without ./
|
|
348
|
+
inputs: [],
|
|
349
|
+
expected_output: [],
|
|
350
|
+
}),
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
354
|
+
assert.deepEqual(results, [], "Should pass because src/generated.ts matches ./src/generated.ts");
|
|
355
|
+
} finally {
|
|
356
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("paths with mixed separators match", () => {
|
|
361
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
362
|
+
mkdirSync(tempDir, { recursive: true });
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
const tasks = [
|
|
366
|
+
createTask({
|
|
367
|
+
id: "T01",
|
|
368
|
+
sequence: 0,
|
|
369
|
+
files: [],
|
|
370
|
+
inputs: [],
|
|
371
|
+
expected_output: ["src/sub/file.ts"],
|
|
372
|
+
}),
|
|
373
|
+
createTask({
|
|
374
|
+
id: "T02",
|
|
375
|
+
sequence: 1,
|
|
376
|
+
files: ["src\\sub\\file.ts"], // Backslash separators
|
|
377
|
+
inputs: [],
|
|
378
|
+
expected_output: [],
|
|
379
|
+
}),
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
383
|
+
assert.deepEqual(results, [], "Should pass because backslash paths normalize to forward slash");
|
|
384
|
+
} finally {
|
|
385
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe("checkTaskOrdering with path normalization", () => {
|
|
391
|
+
test("./path triggers ordering check for path in expected_output", () => {
|
|
392
|
+
const tasks = [
|
|
393
|
+
createTask({
|
|
394
|
+
id: "T01",
|
|
395
|
+
sequence: 0,
|
|
396
|
+
files: ["./generated.ts"], // Reads with ./
|
|
397
|
+
inputs: [],
|
|
398
|
+
expected_output: [],
|
|
399
|
+
}),
|
|
400
|
+
createTask({
|
|
401
|
+
id: "T02",
|
|
402
|
+
sequence: 1,
|
|
403
|
+
files: [],
|
|
404
|
+
inputs: [],
|
|
405
|
+
expected_output: ["generated.ts"], // Creates without ./
|
|
406
|
+
}),
|
|
407
|
+
];
|
|
408
|
+
|
|
409
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
410
|
+
assert.equal(results.length, 1, "Should detect ordering violation despite ./");
|
|
411
|
+
assert.ok(results[0].message.includes("T01"));
|
|
412
|
+
assert.ok(results[0].message.includes("T02"));
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test("path triggers ordering check for ./path in expected_output", () => {
|
|
416
|
+
const tasks = [
|
|
417
|
+
createTask({
|
|
418
|
+
id: "T01",
|
|
419
|
+
sequence: 0,
|
|
420
|
+
files: ["generated.ts"], // Reads without ./
|
|
421
|
+
inputs: [],
|
|
422
|
+
expected_output: [],
|
|
423
|
+
}),
|
|
424
|
+
createTask({
|
|
425
|
+
id: "T02",
|
|
426
|
+
sequence: 1,
|
|
427
|
+
files: [],
|
|
428
|
+
inputs: [],
|
|
429
|
+
expected_output: ["./generated.ts"], // Creates with ./
|
|
430
|
+
}),
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
434
|
+
assert.equal(results.length, 1, "Should detect ordering violation despite ./ on creator");
|
|
435
|
+
assert.ok(results[0].message.includes("sequence violation"));
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("no false positive when correctly ordered with mixed paths", () => {
|
|
439
|
+
const tasks = [
|
|
440
|
+
createTask({
|
|
441
|
+
id: "T01",
|
|
442
|
+
sequence: 0,
|
|
443
|
+
files: [],
|
|
444
|
+
inputs: [],
|
|
445
|
+
expected_output: ["./src/api.ts"],
|
|
446
|
+
}),
|
|
447
|
+
createTask({
|
|
448
|
+
id: "T02",
|
|
449
|
+
sequence: 1,
|
|
450
|
+
files: ["src/api.ts"], // Same file, different notation
|
|
451
|
+
inputs: [],
|
|
452
|
+
expected_output: [],
|
|
453
|
+
}),
|
|
454
|
+
];
|
|
455
|
+
|
|
456
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
457
|
+
assert.deepEqual(results, [], "Should pass - T02 reads file that T01 already created");
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// ─── Task Ordering Tests ─────────────────────────────────────────────────────
|
|
462
|
+
|
|
463
|
+
describe("checkTaskOrdering", () => {
|
|
464
|
+
test("passes when tasks are correctly ordered", () => {
|
|
465
|
+
const tasks = [
|
|
466
|
+
createTask({
|
|
467
|
+
id: "T01",
|
|
468
|
+
sequence: 0,
|
|
469
|
+
files: [],
|
|
470
|
+
inputs: [],
|
|
471
|
+
expected_output: ["api.ts"],
|
|
472
|
+
}),
|
|
473
|
+
createTask({
|
|
474
|
+
id: "T02",
|
|
475
|
+
sequence: 1,
|
|
476
|
+
files: ["api.ts"],
|
|
477
|
+
inputs: [],
|
|
478
|
+
expected_output: [],
|
|
479
|
+
}),
|
|
480
|
+
];
|
|
481
|
+
|
|
482
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
483
|
+
assert.deepEqual(results, []);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("fails when task reads file created by later task", () => {
|
|
487
|
+
const tasks = [
|
|
488
|
+
createTask({
|
|
489
|
+
id: "T01",
|
|
490
|
+
sequence: 0,
|
|
491
|
+
files: ["generated.ts"], // Reads file that doesn't exist yet
|
|
492
|
+
inputs: [],
|
|
493
|
+
expected_output: [],
|
|
494
|
+
}),
|
|
495
|
+
createTask({
|
|
496
|
+
id: "T02",
|
|
497
|
+
sequence: 1,
|
|
498
|
+
files: [],
|
|
499
|
+
inputs: [],
|
|
500
|
+
expected_output: ["generated.ts"], // Creates the file
|
|
501
|
+
}),
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
505
|
+
assert.equal(results.length, 1);
|
|
506
|
+
assert.equal(results[0].category, "file");
|
|
507
|
+
assert.equal(results[0].passed, false);
|
|
508
|
+
assert.equal(results[0].blocking, true);
|
|
509
|
+
assert.ok(results[0].message.includes("T01"));
|
|
510
|
+
assert.ok(results[0].message.includes("T02"));
|
|
511
|
+
assert.ok(results[0].message.includes("sequence violation"));
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test("detects ordering violation in inputs array", () => {
|
|
515
|
+
const tasks = [
|
|
516
|
+
createTask({
|
|
517
|
+
id: "T01",
|
|
518
|
+
sequence: 0,
|
|
519
|
+
files: [],
|
|
520
|
+
inputs: ["schema.json"],
|
|
521
|
+
expected_output: [],
|
|
522
|
+
}),
|
|
523
|
+
createTask({
|
|
524
|
+
id: "T02",
|
|
525
|
+
sequence: 1,
|
|
526
|
+
files: [],
|
|
527
|
+
inputs: [],
|
|
528
|
+
expected_output: ["schema.json"],
|
|
529
|
+
}),
|
|
530
|
+
];
|
|
531
|
+
|
|
532
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
533
|
+
assert.equal(results.length, 1);
|
|
534
|
+
assert.ok(results[0].message.includes("schema.json"));
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
test("handles multiple ordering violations", () => {
|
|
538
|
+
const tasks = [
|
|
539
|
+
createTask({
|
|
540
|
+
id: "T01",
|
|
541
|
+
sequence: 0,
|
|
542
|
+
files: ["a.ts", "b.ts"],
|
|
543
|
+
inputs: [],
|
|
544
|
+
expected_output: [],
|
|
545
|
+
}),
|
|
546
|
+
createTask({
|
|
547
|
+
id: "T02",
|
|
548
|
+
sequence: 1,
|
|
549
|
+
files: [],
|
|
550
|
+
inputs: [],
|
|
551
|
+
expected_output: ["a.ts"],
|
|
552
|
+
}),
|
|
553
|
+
createTask({
|
|
554
|
+
id: "T03",
|
|
555
|
+
sequence: 2,
|
|
556
|
+
files: [],
|
|
557
|
+
inputs: [],
|
|
558
|
+
expected_output: ["b.ts"],
|
|
559
|
+
}),
|
|
560
|
+
];
|
|
561
|
+
|
|
562
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
563
|
+
assert.equal(results.length, 2);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
test("passes when no dependencies between tasks", () => {
|
|
567
|
+
const tasks = [
|
|
568
|
+
createTask({
|
|
569
|
+
id: "T01",
|
|
570
|
+
sequence: 0,
|
|
571
|
+
files: [],
|
|
572
|
+
inputs: [],
|
|
573
|
+
expected_output: ["a.ts"],
|
|
574
|
+
}),
|
|
575
|
+
createTask({
|
|
576
|
+
id: "T02",
|
|
577
|
+
sequence: 1,
|
|
578
|
+
files: [],
|
|
579
|
+
inputs: [],
|
|
580
|
+
expected_output: ["b.ts"],
|
|
581
|
+
}),
|
|
582
|
+
];
|
|
583
|
+
|
|
584
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
585
|
+
assert.deepEqual(results, []);
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// ─── Interface Contract Tests ────────────────────────────────────────────────
|
|
590
|
+
|
|
591
|
+
describe("checkInterfaceContracts", () => {
|
|
592
|
+
test("passes when function signatures match", () => {
|
|
593
|
+
const tasks = [
|
|
594
|
+
createTask({
|
|
595
|
+
id: "T01",
|
|
596
|
+
description: `
|
|
597
|
+
\`\`\`typescript
|
|
598
|
+
function processData(input: string): boolean
|
|
599
|
+
\`\`\`
|
|
600
|
+
`,
|
|
601
|
+
}),
|
|
602
|
+
createTask({
|
|
603
|
+
id: "T02",
|
|
604
|
+
description: `
|
|
605
|
+
\`\`\`typescript
|
|
606
|
+
function processData(input: string): boolean
|
|
607
|
+
\`\`\`
|
|
608
|
+
`,
|
|
609
|
+
}),
|
|
610
|
+
];
|
|
611
|
+
|
|
612
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
613
|
+
assert.deepEqual(results, []);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
test("warns on parameter mismatch (non-blocking)", () => {
|
|
617
|
+
const tasks = [
|
|
618
|
+
createTask({
|
|
619
|
+
id: "T01",
|
|
620
|
+
description: `
|
|
621
|
+
\`\`\`typescript
|
|
622
|
+
function saveUser(name: string): void
|
|
623
|
+
\`\`\`
|
|
624
|
+
`,
|
|
625
|
+
}),
|
|
626
|
+
createTask({
|
|
627
|
+
id: "T02",
|
|
628
|
+
description: `
|
|
629
|
+
\`\`\`typescript
|
|
630
|
+
function saveUser(name: string, email: string): void
|
|
631
|
+
\`\`\`
|
|
632
|
+
`,
|
|
633
|
+
}),
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
637
|
+
assert.equal(results.length, 1);
|
|
638
|
+
assert.equal(results[0].category, "schema");
|
|
639
|
+
assert.equal(results[0].target, "saveUser");
|
|
640
|
+
assert.equal(results[0].passed, true); // Warning, not failure
|
|
641
|
+
assert.equal(results[0].blocking, false);
|
|
642
|
+
assert.ok(results[0].message.includes("different parameters"));
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
test("warns on return type mismatch (non-blocking)", () => {
|
|
646
|
+
const tasks = [
|
|
647
|
+
createTask({
|
|
648
|
+
id: "T01",
|
|
649
|
+
description: `
|
|
650
|
+
\`\`\`typescript
|
|
651
|
+
function getData(): string
|
|
652
|
+
\`\`\`
|
|
653
|
+
`,
|
|
654
|
+
}),
|
|
655
|
+
createTask({
|
|
656
|
+
id: "T02",
|
|
657
|
+
description: `
|
|
658
|
+
\`\`\`typescript
|
|
659
|
+
function getData(): number
|
|
660
|
+
\`\`\`
|
|
661
|
+
`,
|
|
662
|
+
}),
|
|
663
|
+
];
|
|
664
|
+
|
|
665
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
666
|
+
assert.equal(results.length, 1);
|
|
667
|
+
assert.ok(results[0].message.includes("different return types"));
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test("handles export function syntax", () => {
|
|
671
|
+
const tasks = [
|
|
672
|
+
createTask({
|
|
673
|
+
id: "T01",
|
|
674
|
+
description: `
|
|
675
|
+
\`\`\`typescript
|
|
676
|
+
export function validate(data: object): boolean
|
|
677
|
+
\`\`\`
|
|
678
|
+
`,
|
|
679
|
+
}),
|
|
680
|
+
createTask({
|
|
681
|
+
id: "T02",
|
|
682
|
+
description: `
|
|
683
|
+
\`\`\`typescript
|
|
684
|
+
export function validate(data: string): boolean
|
|
685
|
+
\`\`\`
|
|
686
|
+
`,
|
|
687
|
+
}),
|
|
688
|
+
];
|
|
689
|
+
|
|
690
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
691
|
+
assert.equal(results.length, 1);
|
|
692
|
+
assert.ok(results[0].message.includes("validate"));
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test("handles async function syntax", () => {
|
|
696
|
+
const tasks = [
|
|
697
|
+
createTask({
|
|
698
|
+
id: "T01",
|
|
699
|
+
description: `
|
|
700
|
+
\`\`\`typescript
|
|
701
|
+
export async function fetchData(): Promise<string>
|
|
702
|
+
\`\`\`
|
|
703
|
+
`,
|
|
704
|
+
}),
|
|
705
|
+
createTask({
|
|
706
|
+
id: "T02",
|
|
707
|
+
description: `
|
|
708
|
+
\`\`\`typescript
|
|
709
|
+
export async function fetchData(): Promise<number>
|
|
710
|
+
\`\`\`
|
|
711
|
+
`,
|
|
712
|
+
}),
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
716
|
+
assert.equal(results.length, 1);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
test("handles const arrow function syntax", () => {
|
|
720
|
+
const tasks = [
|
|
721
|
+
createTask({
|
|
722
|
+
id: "T01",
|
|
723
|
+
description: `
|
|
724
|
+
\`\`\`typescript
|
|
725
|
+
const handler = (req: Request): Response =>
|
|
726
|
+
\`\`\`
|
|
727
|
+
`,
|
|
728
|
+
}),
|
|
729
|
+
createTask({
|
|
730
|
+
id: "T02",
|
|
731
|
+
description: `
|
|
732
|
+
\`\`\`typescript
|
|
733
|
+
const handler = (req: Request, res: Response): void =>
|
|
734
|
+
\`\`\`
|
|
735
|
+
`,
|
|
736
|
+
}),
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
740
|
+
// Should have 2 results: parameter mismatch AND return type mismatch
|
|
741
|
+
assert.equal(results.length, 2);
|
|
742
|
+
assert.ok(results.some((r) => r.message.includes("handler")));
|
|
743
|
+
assert.ok(results.some((r) => r.message.includes("parameters")));
|
|
744
|
+
assert.ok(results.some((r) => r.message.includes("return types")));
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
test("passes when no code blocks present", () => {
|
|
748
|
+
const tasks = [
|
|
749
|
+
createTask({
|
|
750
|
+
id: "T01",
|
|
751
|
+
description: "Just some text without code blocks",
|
|
752
|
+
}),
|
|
753
|
+
];
|
|
754
|
+
|
|
755
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
756
|
+
assert.deepEqual(results, []);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
test("handles multiple mismatches for same function", () => {
|
|
760
|
+
const tasks = [
|
|
761
|
+
createTask({
|
|
762
|
+
id: "T01",
|
|
763
|
+
description: `
|
|
764
|
+
\`\`\`typescript
|
|
765
|
+
function process(a: string): string
|
|
766
|
+
\`\`\`
|
|
767
|
+
`,
|
|
768
|
+
}),
|
|
769
|
+
createTask({
|
|
770
|
+
id: "T02",
|
|
771
|
+
description: `
|
|
772
|
+
\`\`\`typescript
|
|
773
|
+
function process(a: number): number
|
|
774
|
+
\`\`\`
|
|
775
|
+
`,
|
|
776
|
+
}),
|
|
777
|
+
];
|
|
778
|
+
|
|
779
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
780
|
+
// Should have both parameter and return type mismatches
|
|
781
|
+
assert.equal(results.length, 2);
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
// ─── runPreExecutionChecks Integration Tests ─────────────────────────────────
|
|
786
|
+
|
|
787
|
+
describe("runPreExecutionChecks", () => {
|
|
788
|
+
let tempDir: string;
|
|
789
|
+
|
|
790
|
+
test("returns pass status when all checks pass", async () => {
|
|
791
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
792
|
+
mkdirSync(tempDir, { recursive: true });
|
|
793
|
+
writeFileSync(join(tempDir, "existing.ts"), "// content");
|
|
794
|
+
|
|
795
|
+
try {
|
|
796
|
+
const tasks = [
|
|
797
|
+
createTask({
|
|
798
|
+
id: "T01",
|
|
799
|
+
files: ["existing.ts"],
|
|
800
|
+
inputs: [],
|
|
801
|
+
expected_output: ["output.ts"],
|
|
802
|
+
}),
|
|
803
|
+
createTask({
|
|
804
|
+
id: "T02",
|
|
805
|
+
files: ["output.ts"],
|
|
806
|
+
inputs: [],
|
|
807
|
+
expected_output: [],
|
|
808
|
+
}),
|
|
809
|
+
];
|
|
810
|
+
|
|
811
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
812
|
+
assert.equal(result.status, "pass");
|
|
813
|
+
assert.equal(result.checks.length, 0);
|
|
814
|
+
assert.ok(result.durationMs >= 0);
|
|
815
|
+
} finally {
|
|
816
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
test("returns fail status when blocking failure exists", async () => {
|
|
821
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
822
|
+
mkdirSync(tempDir, { recursive: true });
|
|
823
|
+
|
|
824
|
+
try {
|
|
825
|
+
const tasks = [
|
|
826
|
+
createTask({
|
|
827
|
+
id: "T01",
|
|
828
|
+
files: ["nonexistent.ts"],
|
|
829
|
+
inputs: [],
|
|
830
|
+
expected_output: [],
|
|
831
|
+
}),
|
|
832
|
+
];
|
|
833
|
+
|
|
834
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
835
|
+
assert.equal(result.status, "fail");
|
|
836
|
+
assert.ok(result.checks.length > 0);
|
|
837
|
+
assert.ok(result.checks.some((c) => c.blocking === true));
|
|
838
|
+
} finally {
|
|
839
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
test("returns warn status for non-blocking issues", async () => {
|
|
844
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
845
|
+
mkdirSync(tempDir, { recursive: true });
|
|
846
|
+
|
|
847
|
+
try {
|
|
848
|
+
// Create tasks with only interface contract warnings
|
|
849
|
+
const tasks = [
|
|
850
|
+
createTask({
|
|
851
|
+
id: "T01",
|
|
852
|
+
files: [],
|
|
853
|
+
inputs: [],
|
|
854
|
+
expected_output: [],
|
|
855
|
+
description: `
|
|
856
|
+
\`\`\`typescript
|
|
857
|
+
function foo(a: string): void
|
|
858
|
+
\`\`\`
|
|
859
|
+
`,
|
|
860
|
+
}),
|
|
861
|
+
createTask({
|
|
862
|
+
id: "T02",
|
|
863
|
+
files: [],
|
|
864
|
+
inputs: [],
|
|
865
|
+
expected_output: [],
|
|
866
|
+
description: `
|
|
867
|
+
\`\`\`typescript
|
|
868
|
+
function foo(a: number): void
|
|
869
|
+
\`\`\`
|
|
870
|
+
`,
|
|
871
|
+
}),
|
|
872
|
+
];
|
|
873
|
+
|
|
874
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
875
|
+
assert.equal(result.status, "warn");
|
|
876
|
+
assert.ok(result.checks.some((c) => c.blocking === false));
|
|
877
|
+
} finally {
|
|
878
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
test("combines results from all check types", async () => {
|
|
883
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
884
|
+
mkdirSync(tempDir, { recursive: true });
|
|
885
|
+
|
|
886
|
+
try {
|
|
887
|
+
const tasks = [
|
|
888
|
+
createTask({
|
|
889
|
+
id: "T01",
|
|
890
|
+
sequence: 0,
|
|
891
|
+
files: ["will-be-created.ts"], // Ordering violation
|
|
892
|
+
inputs: ["missing.ts"], // Missing file
|
|
893
|
+
expected_output: [],
|
|
894
|
+
description: `
|
|
895
|
+
\`\`\`typescript
|
|
896
|
+
function check(a: string): void
|
|
897
|
+
\`\`\`
|
|
898
|
+
`,
|
|
899
|
+
}),
|
|
900
|
+
createTask({
|
|
901
|
+
id: "T02",
|
|
902
|
+
sequence: 1,
|
|
903
|
+
files: [],
|
|
904
|
+
inputs: [],
|
|
905
|
+
expected_output: ["will-be-created.ts"],
|
|
906
|
+
description: `
|
|
907
|
+
\`\`\`typescript
|
|
908
|
+
function check(a: number): void
|
|
909
|
+
\`\`\`
|
|
910
|
+
`,
|
|
911
|
+
}),
|
|
912
|
+
];
|
|
913
|
+
|
|
914
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
915
|
+
assert.equal(result.status, "fail");
|
|
916
|
+
|
|
917
|
+
// Should have multiple types of issues
|
|
918
|
+
const categories = new Set(result.checks.map((c) => c.category));
|
|
919
|
+
assert.ok(categories.has("file")); // From consistency and ordering
|
|
920
|
+
assert.ok(categories.has("schema")); // From interface check
|
|
921
|
+
} finally {
|
|
922
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
test("reports duration in milliseconds", async () => {
|
|
927
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
928
|
+
mkdirSync(tempDir, { recursive: true });
|
|
929
|
+
|
|
930
|
+
try {
|
|
931
|
+
const tasks = [createTask({ id: "T01" })];
|
|
932
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
933
|
+
|
|
934
|
+
assert.ok(typeof result.durationMs === "number");
|
|
935
|
+
assert.ok(result.durationMs >= 0);
|
|
936
|
+
} finally {
|
|
937
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
test("handles empty task array", async () => {
|
|
942
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
943
|
+
mkdirSync(tempDir, { recursive: true });
|
|
944
|
+
|
|
945
|
+
try {
|
|
946
|
+
const result = await runPreExecutionChecks([], tempDir);
|
|
947
|
+
assert.equal(result.status, "pass");
|
|
948
|
+
assert.deepEqual(result.checks, []);
|
|
949
|
+
} finally {
|
|
950
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
// ─── PreExecutionResult Type Tests ───────────────────────────────────────────
|
|
956
|
+
|
|
957
|
+
describe("PreExecutionResult type", () => {
|
|
958
|
+
test("status is one of pass, warn, fail", async () => {
|
|
959
|
+
const tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
960
|
+
mkdirSync(tempDir, { recursive: true });
|
|
961
|
+
|
|
962
|
+
try {
|
|
963
|
+
const tasks = [createTask({ id: "T01" })];
|
|
964
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
965
|
+
|
|
966
|
+
assert.ok(["pass", "warn", "fail"].includes(result.status));
|
|
967
|
+
} finally {
|
|
968
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
test("checks array matches PreExecutionCheckJSON schema", async () => {
|
|
973
|
+
const tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
974
|
+
mkdirSync(tempDir, { recursive: true });
|
|
975
|
+
|
|
976
|
+
try {
|
|
977
|
+
const tasks = [
|
|
978
|
+
createTask({
|
|
979
|
+
id: "T01",
|
|
980
|
+
files: ["missing.ts"],
|
|
981
|
+
}),
|
|
982
|
+
];
|
|
983
|
+
|
|
984
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
985
|
+
|
|
986
|
+
for (const check of result.checks) {
|
|
987
|
+
assert.ok(["package", "file", "tool", "endpoint", "schema"].includes(check.category));
|
|
988
|
+
assert.ok(typeof check.target === "string");
|
|
989
|
+
assert.ok(typeof check.passed === "boolean");
|
|
990
|
+
assert.ok(typeof check.message === "string");
|
|
991
|
+
if (check.blocking !== undefined) {
|
|
992
|
+
assert.ok(typeof check.blocking === "boolean");
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
} finally {
|
|
996
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
});
|