gsd-pi 2.82.0-dev.c22380fc3 → 2.82.0-dev.dfbc5f58f
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 +4 -3
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +10 -1
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
- package/dist/resources/extensions/cmux/index.js +5 -0
- package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
- package/dist/resources/extensions/gsd/auto/loop.js +5 -5
- package/dist/resources/extensions/gsd/auto/orchestrator.js +11 -0
- package/dist/resources/extensions/gsd/auto/phases.js +8 -1
- package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
- package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +233 -127
- package/dist/resources/extensions/gsd/auto-prompts.js +2 -2
- package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
- package/dist/resources/extensions/gsd/auto-start.js +85 -12
- package/dist/resources/extensions/gsd/auto-verification.js +28 -22
- package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +30 -3
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
- package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +37 -0
- package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
- package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
- package/dist/resources/extensions/gsd/doctor.js +2 -28
- package/dist/resources/extensions/gsd/export-html.js +27 -425
- package/dist/resources/extensions/gsd/git-service.js +39 -1
- package/dist/resources/extensions/gsd/gsd-db.js +1 -0
- package/dist/resources/extensions/gsd/guided-flow.js +6 -0
- package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
- package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
- package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
- package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
- package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
- package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
- package/dist/resources/extensions/gsd/status-guards.js +4 -0
- package/dist/resources/extensions/gsd/templates/plan.md +8 -5
- package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
- package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
- package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
- package/dist/resources/extensions/gsd/validation.js +23 -1
- package/dist/resources/extensions/gsd/verification-gate.js +68 -7
- package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +33 -8
- package/dist/resources/extensions/shared/html-shell.js +388 -0
- package/dist/resources/extensions/subagent/index.js +448 -78
- package/dist/resources/extensions/subagent/launch.js +77 -0
- package/dist/resources/extensions/subagent/run-store.js +148 -0
- package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
- package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
- package/dist/resources/extensions/visual-brief/index.js +5 -0
- package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
- package/dist/resources/extensions/visual-brief/prompts.js +140 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- 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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
- 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 +4 -5
- 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 +2 -5
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -7
- 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 +4 -7
- 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 +4 -5
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
- package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
- package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
- package/package.json +4 -4
- package/packages/mcp-server/src/workflow-tools.test.ts +1 -1
- package/packages/native/tsconfig.json +2 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +5 -6
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
- package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
- package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
- package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
- package/packages/pi-ai/src/providers/simple-options.ts +5 -6
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/GSD-WORKFLOW.md +10 -1
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
- package/src/resources/extensions/cmux/index.ts +6 -0
- package/src/resources/extensions/gsd/auto/contracts.ts +14 -6
- package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
- package/src/resources/extensions/gsd/auto/loop.ts +8 -5
- package/src/resources/extensions/gsd/auto/orchestrator.ts +11 -0
- package/src/resources/extensions/gsd/auto/phases.ts +7 -1
- package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
- package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +266 -139
- package/src/resources/extensions/gsd/auto-prompts.ts +2 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
- package/src/resources/extensions/gsd/auto-start.ts +92 -9
- package/src/resources/extensions/gsd/auto-verification.ts +36 -34
- package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
- package/src/resources/extensions/gsd/auto.ts +32 -3
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
- package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
- package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
- package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +40 -0
- package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
- package/src/resources/extensions/gsd/doctor.ts +2 -27
- package/src/resources/extensions/gsd/export-html.ts +27 -427
- package/src/resources/extensions/gsd/git-service.ts +45 -1
- package/src/resources/extensions/gsd/gsd-db.ts +3 -0
- package/src/resources/extensions/gsd/guided-flow.ts +6 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
- package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
- package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
- package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
- package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
- package/src/resources/extensions/gsd/status-guards.ts +5 -0
- package/src/resources/extensions/gsd/templates/plan.md +8 -5
- package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
- package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +80 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
- package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
- package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +103 -1
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
- package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
- package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
- package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
- package/src/resources/extensions/gsd/types.ts +1 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
- package/src/resources/extensions/gsd/validation.ts +23 -1
- package/src/resources/extensions/gsd/verification-gate.ts +78 -6
- package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +41 -8
- package/src/resources/extensions/shared/html-shell.ts +412 -0
- package/src/resources/extensions/subagent/index.ts +567 -103
- package/src/resources/extensions/subagent/launch.ts +131 -0
- package/src/resources/extensions/subagent/run-store.ts +218 -0
- package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
- package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
- package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
- package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
- package/src/resources/extensions/visual-brief/index.ts +8 -0
- package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
- package/src/resources/extensions/visual-brief/prompts.ts +183 -0
- package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
- package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
- package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
- package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
- package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → q0WYuDVbHeFFYbdd-fei2}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → q0WYuDVbHeFFYbdd-fei2}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
@@ -10,7 +10,7 @@ import { runDispatch, runPreDispatch } from "../auto/phases.ts";
|
|
|
10
10
|
import { AutoSession } from "../auto/session.ts";
|
|
11
11
|
import { resolveUnitSupervisionTimeouts } from "../auto-timers.ts";
|
|
12
12
|
import { bootstrapAutoSession } from "../auto-start.ts";
|
|
13
|
-
import { postUnitPreVerification } from "../auto-post-unit.ts";
|
|
13
|
+
import { postUnitPostVerification, postUnitPreVerification } from "../auto-post-unit.ts";
|
|
14
14
|
import { resolveDispatch, setResearchProjectPromptBuilderForTest } from "../auto-dispatch.ts";
|
|
15
15
|
import { resolveExpectedArtifactPath, verifyExpectedArtifact, writeBlockerPlaceholder } from "../auto-recovery.ts";
|
|
16
16
|
import { finalizeProjectResearchTimeout } from "../project-research-policy.ts";
|
|
@@ -272,6 +272,7 @@ test("deep project setup: bootstrap can start auto-mode without an active milest
|
|
|
272
272
|
{
|
|
273
273
|
shouldUseWorktreeIsolation: () => false,
|
|
274
274
|
registerSigtermHandler: () => {},
|
|
275
|
+
registerAutoWorkerForSession: () => {},
|
|
275
276
|
lockBase: () => base,
|
|
276
277
|
buildLifecycle: () => ({
|
|
277
278
|
adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
|
|
@@ -386,6 +387,7 @@ test("deep project setup: bootstrap continues queued M002 without milestone cont
|
|
|
386
387
|
{
|
|
387
388
|
shouldUseWorktreeIsolation: () => false,
|
|
388
389
|
registerSigtermHandler: () => {},
|
|
390
|
+
registerAutoWorkerForSession: () => {},
|
|
389
391
|
lockBase: () => base,
|
|
390
392
|
buildLifecycle: () => ({
|
|
391
393
|
adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
|
|
@@ -1587,6 +1589,61 @@ test("deep project setup: discuss-milestone question failure pauses instead of a
|
|
|
1587
1589
|
}
|
|
1588
1590
|
});
|
|
1589
1591
|
|
|
1592
|
+
test("verified task git closeout failure retries and continues auto-mode", async () => {
|
|
1593
|
+
const base = makeBase();
|
|
1594
|
+
try {
|
|
1595
|
+
execFileSync("git", ["init"], { cwd: base, stdio: "ignore" });
|
|
1596
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: base, stdio: "ignore" });
|
|
1597
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: base, stdio: "ignore" });
|
|
1598
|
+
const hookPath = join(base, ".git", "hooks", "pre-commit");
|
|
1599
|
+
writeFileSync(
|
|
1600
|
+
hookPath,
|
|
1601
|
+
[
|
|
1602
|
+
"#!/bin/sh",
|
|
1603
|
+
"count_file=.git/pre-commit-count",
|
|
1604
|
+
"count=0",
|
|
1605
|
+
"if [ -f \"$count_file\" ]; then count=$(cat \"$count_file\"); fi",
|
|
1606
|
+
"count=$((count + 1))",
|
|
1607
|
+
"printf \"%s\" \"$count\" > \"$count_file\"",
|
|
1608
|
+
"echo blocked by test hook >&2",
|
|
1609
|
+
"exit 1",
|
|
1610
|
+
].join("\n"),
|
|
1611
|
+
);
|
|
1612
|
+
chmodSync(hookPath, 0o755);
|
|
1613
|
+
writeFileSync(join(base, "work.txt"), "changed\n");
|
|
1614
|
+
|
|
1615
|
+
const s = new AutoSession();
|
|
1616
|
+
s.active = true;
|
|
1617
|
+
s.basePath = base;
|
|
1618
|
+
s.originalBasePath = base;
|
|
1619
|
+
s.currentUnit = { type: "execute-task", id: "M001/S01/T01", startedAt: Date.now() };
|
|
1620
|
+
|
|
1621
|
+
let pauseCalled = false;
|
|
1622
|
+
const notifications: Array<{ message: string; severity?: string }> = [];
|
|
1623
|
+
const result = await postUnitPostVerification({
|
|
1624
|
+
s,
|
|
1625
|
+
ctx: { ui: { notify: (message: string, severity?: string) => notifications.push({ message, severity }) } } as any,
|
|
1626
|
+
pi: {} as any,
|
|
1627
|
+
buildSnapshotOpts: () => ({}) as any,
|
|
1628
|
+
lockBase: () => base,
|
|
1629
|
+
stopAuto: async () => {},
|
|
1630
|
+
pauseAuto: async () => { pauseCalled = true; },
|
|
1631
|
+
updateProgressWidget: () => {},
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
assert.equal(result, "continue");
|
|
1635
|
+
assert.equal(pauseCalled, false);
|
|
1636
|
+
assert.equal(s.lastGitActionStatus, "failed");
|
|
1637
|
+
assert.equal(readFileSync(join(base, ".git", "pre-commit-count"), "utf-8"), "3");
|
|
1638
|
+
assert.ok(
|
|
1639
|
+
notifications.some((entry) => entry.severity === "warning" && entry.message.includes("Git commit failed")),
|
|
1640
|
+
"verified task git closeout failure should warn instead of stopping auto-mode",
|
|
1641
|
+
);
|
|
1642
|
+
} finally {
|
|
1643
|
+
rmSync(base, { recursive: true, force: true });
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1590
1647
|
test("deep project setup: approval wait wins over deterministic write-gate placeholder", async () => {
|
|
1591
1648
|
const base = makeBase();
|
|
1592
1649
|
try {
|
|
@@ -118,3 +118,42 @@ describe("complete phase dispatch guard (#5683)", () => {
|
|
|
118
118
|
assert.equal(result?.reason, "All milestones complete.");
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
|
+
|
|
122
|
+
describe("complete milestone context recovery guard (#5831)", () => {
|
|
123
|
+
let base = "";
|
|
124
|
+
const executionEntryRule = DISPATCH_RULES.find(
|
|
125
|
+
(candidate) => candidate.name === "execution-entry phase (no context) → discuss-milestone",
|
|
126
|
+
);
|
|
127
|
+
const prePlanningRule = DISPATCH_RULES.find(
|
|
128
|
+
(candidate) => candidate.name === "pre-planning (no context) → discuss-milestone",
|
|
129
|
+
);
|
|
130
|
+
assert.ok(executionEntryRule, "execution-entry missing-context rule should exist");
|
|
131
|
+
assert.ok(prePlanningRule, "pre-planning missing-context rule should exist");
|
|
132
|
+
|
|
133
|
+
afterEach(() => {
|
|
134
|
+
if (base) rmSync(base, { recursive: true, force: true });
|
|
135
|
+
base = "";
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("does not discuss a complete execution-entry milestone with no CONTEXT file", async () => {
|
|
139
|
+
base = makeBase();
|
|
140
|
+
const ctx = buildDispatchCtx(base);
|
|
141
|
+
ctx.state.registry = [{ id: "M001", title: "Milestone One", status: "complete" }];
|
|
142
|
+
ctx.state.phase = "completing-milestone";
|
|
143
|
+
|
|
144
|
+
const result = await executionEntryRule.match(ctx);
|
|
145
|
+
|
|
146
|
+
assert.equal(result, null);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("does not discuss a complete pre-planning milestone with no CONTEXT file", async () => {
|
|
150
|
+
base = makeBase();
|
|
151
|
+
const ctx = buildDispatchCtx(base);
|
|
152
|
+
ctx.state.registry = [{ id: "M001", title: "Milestone One", status: "complete" }];
|
|
153
|
+
ctx.state.phase = "pre-planning";
|
|
154
|
+
|
|
155
|
+
const result = await prePlanningRule.match(ctx);
|
|
156
|
+
|
|
157
|
+
assert.equal(result, null);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -47,6 +47,33 @@ test("dispatch guard blocks when prior milestone has incomplete slices", (t) =>
|
|
|
47
47
|
);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
test("dispatch guard skips prior DB parked or deferred milestones without marker files", (t) => {
|
|
51
|
+
const repo = setupRepo();
|
|
52
|
+
t.after(() => teardownRepo(repo));
|
|
53
|
+
|
|
54
|
+
mkdirSync(join(repo, ".gsd", "milestones", "M001"), { recursive: true });
|
|
55
|
+
mkdirSync(join(repo, ".gsd", "milestones", "M002"), { recursive: true });
|
|
56
|
+
mkdirSync(join(repo, ".gsd", "milestones", "M003"), { recursive: true });
|
|
57
|
+
|
|
58
|
+
insertMilestone({ id: "M001", title: "Parked", status: "parked" });
|
|
59
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Incomplete", status: "pending", depends: [], sequence: 1 });
|
|
60
|
+
|
|
61
|
+
insertMilestone({ id: "M002", title: "Deferred", status: "deferred" });
|
|
62
|
+
insertSlice({ id: "S01", milestoneId: "M002", title: "Incomplete", status: "pending", depends: [], sequence: 1 });
|
|
63
|
+
|
|
64
|
+
insertMilestone({ id: "M003", title: "Current", status: "active" });
|
|
65
|
+
insertSlice({ id: "S01", milestoneId: "M003", title: "First", status: "pending", depends: [], sequence: 1 });
|
|
66
|
+
|
|
67
|
+
writeFileSync(join(repo, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# M001\n");
|
|
68
|
+
writeFileSync(join(repo, ".gsd", "milestones", "M002", "M002-ROADMAP.md"), "# M002\n");
|
|
69
|
+
writeFileSync(join(repo, ".gsd", "milestones", "M003", "M003-ROADMAP.md"), "# M003\n");
|
|
70
|
+
|
|
71
|
+
assert.equal(
|
|
72
|
+
getPriorSliceCompletionBlocker(repo, "main", "plan-slice", "M003/S01"),
|
|
73
|
+
null,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
50
77
|
test("dispatch guard blocks later slice in same milestone when earlier incomplete", (t) => {
|
|
51
78
|
const repo = setupRepo();
|
|
52
79
|
t.after(() => teardownRepo(repo));
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Tests for verification evidence cross-reference mismatch policy.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import { crossReferenceEvidence } from "../safety/evidence-cross-ref.ts";
|
|
8
|
+
import type { EvidenceEntry } from "../safety/evidence-collector.ts";
|
|
9
|
+
|
|
10
|
+
test("claims of passing verification become errors when recorded bash evidence failed", () => {
|
|
11
|
+
const mismatches = crossReferenceEvidence(
|
|
12
|
+
[{ command: "npm test", exitCode: 0, verdict: "passed" }],
|
|
13
|
+
[
|
|
14
|
+
{
|
|
15
|
+
kind: "bash",
|
|
16
|
+
toolCallId: "call-1",
|
|
17
|
+
command: "npm test",
|
|
18
|
+
exitCode: 1,
|
|
19
|
+
outputSnippet: "failed",
|
|
20
|
+
timestamp: Date.now(),
|
|
21
|
+
},
|
|
22
|
+
] as EvidenceEntry[],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
assert.equal(mismatches.length, 1);
|
|
26
|
+
assert.equal(mismatches[0].severity, "error");
|
|
27
|
+
assert.match(mismatches[0].reason, /Claimed exitCode=0/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("missing recorded bash evidence remains a warning", () => {
|
|
31
|
+
const mismatches = crossReferenceEvidence(
|
|
32
|
+
[{ command: "npm test", exitCode: 0, verdict: "passed" }],
|
|
33
|
+
[],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
assert.equal(mismatches.length, 1);
|
|
37
|
+
assert.equal(mismatches[0].severity, "warning");
|
|
38
|
+
});
|
|
@@ -141,6 +141,14 @@ test("Feature 1: executive summary paragraph is rendered", () => {
|
|
|
141
141
|
assert.ok(html.includes("$2.50 spent"), "should contain cost");
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
test("report uses the shared GSD HTML shell", () => {
|
|
145
|
+
const html = generateHtmlReport(mockData(), mockOpts());
|
|
146
|
+
assert.ok(html.includes('<span class="logo">GSD</span>'), "should render shared shell logo");
|
|
147
|
+
assert.ok(html.includes('<span class="kind-chip">Report</span>'), "should render report kind chip");
|
|
148
|
+
assert.ok(html.includes('<nav class="toc" aria-label="Report sections">'), "should render shared shell TOC");
|
|
149
|
+
assert.ok(html.includes('<main>'), "should render content inside shared shell main");
|
|
150
|
+
});
|
|
151
|
+
|
|
144
152
|
test("Feature 1: executive summary includes budget context when set", () => {
|
|
145
153
|
const data = mockData({ health: { ...mockData().health, budgetCeiling: 10.00 } });
|
|
146
154
|
const html = generateHtmlReport(data, mockOpts());
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
test("guided milestone discussion callsites pass workingDirectory to loadPrompt", () => {
|
|
10
|
+
const source = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
|
|
11
|
+
const calls = [...source.matchAll(/loadPrompt\("guided-discuss-milestone",\s*\{([\s\S]*?)\}\)/g)];
|
|
12
|
+
|
|
13
|
+
assert.equal(calls.length, 6, "all guided-flow guided-discuss-milestone callsites should be covered");
|
|
14
|
+
for (const call of calls) {
|
|
15
|
+
assert.match(
|
|
16
|
+
call[1] ?? "",
|
|
17
|
+
/\bworkingDirectory:\s*basePath\b/,
|
|
18
|
+
"guided-discuss-milestone prompts need workingDirectory so template validation does not crash",
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
@@ -92,6 +92,11 @@ test("resolveModelId: returns undefined for unknown model", () => {
|
|
|
92
92
|
assert.equal(match, undefined);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
test("resolveModelId: returns undefined for missing model ID", () => {
|
|
96
|
+
const match = resolveModelId(undefined, AVAILABLE_MODELS, "anthropic");
|
|
97
|
+
assert.equal(match, undefined);
|
|
98
|
+
});
|
|
99
|
+
|
|
95
100
|
test("resolveModelId: returns undefined for unknown provider/model combo", () => {
|
|
96
101
|
const match = resolveModelId("fakeprovider/fake-model", AVAILABLE_MODELS, undefined);
|
|
97
102
|
assert.equal(match, undefined);
|
|
@@ -9,11 +9,11 @@ import { isInfrastructureError, INFRA_ERROR_CODES } from "../auto/infra-errors.j
|
|
|
9
9
|
test("INFRA_ERROR_CODES contains the expected codes", () => {
|
|
10
10
|
for (const code of [
|
|
11
11
|
"ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE", "ENFILE",
|
|
12
|
-
"EAGAIN", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
|
|
12
|
+
"EAGAIN", "ENOBUFS", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
|
|
13
13
|
]) {
|
|
14
14
|
assert.ok(INFRA_ERROR_CODES.has(code), `missing ${code}`);
|
|
15
15
|
}
|
|
16
|
-
assert.equal(INFRA_ERROR_CODES.size,
|
|
16
|
+
assert.equal(INFRA_ERROR_CODES.size, 11, "unexpected extra codes");
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
// ── isInfrastructureError: code property detection ───────────────────────────
|
|
@@ -5,6 +5,8 @@ import test, { describe } from "node:test";
|
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
INFRA_ERROR_CODES,
|
|
9
|
+
isInfrastructureError,
|
|
8
10
|
isTransientCooldownError,
|
|
9
11
|
getCooldownRetryAfterMs,
|
|
10
12
|
MAX_COOLDOWN_RETRIES,
|
|
@@ -13,6 +15,13 @@ import {
|
|
|
13
15
|
|
|
14
16
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
15
17
|
|
|
18
|
+
describe("infra error classification", () => {
|
|
19
|
+
test("ENOBUFS is treated as infrastructure exhaustion", () => {
|
|
20
|
+
assert.equal(INFRA_ERROR_CODES.has("ENOBUFS"), true);
|
|
21
|
+
assert.equal(isInfrastructureError(Object.assign(new Error("spawnSync git ENOBUFS"), { code: "ENOBUFS" })), "ENOBUFS");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
16
25
|
describe("infra-errors cooldown constants", () => {
|
|
17
26
|
test("COOLDOWN_FALLBACK_WAIT_MS is a positive number greater than the 30s rate-limit backoff", () => {
|
|
18
27
|
assert.ok(typeof COOLDOWN_FALLBACK_WAIT_MS === "number");
|
|
@@ -446,6 +446,26 @@ node_modules/
|
|
|
446
446
|
const strandedIssues = detect.issues.filter(i => i.code === "stranded_lock_directory");
|
|
447
447
|
assert.deepStrictEqual(strandedIssues.length, 0, "live lock holder: stranded_lock_directory NOT detected");
|
|
448
448
|
});
|
|
449
|
+
|
|
450
|
+
test('stranded_lock_directory still reports when worker lookup fails', async () => {
|
|
451
|
+
const dir = createMinimalProject();
|
|
452
|
+
cleanups.push(dir);
|
|
453
|
+
|
|
454
|
+
const lockDir = join(dir, ".gsd.lock");
|
|
455
|
+
mkdirSync(lockDir, { recursive: true });
|
|
456
|
+
const { openDatabase, _getAdapter, closeDatabase } = await import("../../gsd-db.ts");
|
|
457
|
+
openDatabase(join(dir, ".gsd", "gsd.db"));
|
|
458
|
+
const db = _getAdapter()!;
|
|
459
|
+
db.exec("DROP TABLE workers");
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
const detect = await runGSDDoctor(dir);
|
|
463
|
+
const strandedIssues = detect.issues.filter(i => i.code === "stranded_lock_directory");
|
|
464
|
+
assert.ok(strandedIssues.length > 0, "reports stranded lock directory even when active worker lookup fails");
|
|
465
|
+
} finally {
|
|
466
|
+
closeDatabase();
|
|
467
|
+
}
|
|
468
|
+
});
|
|
449
469
|
} else {
|
|
450
470
|
}
|
|
451
471
|
|
|
@@ -5,7 +5,7 @@ import assert from 'node:assert/strict';
|
|
|
5
5
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, symlinkSync, readFileSync } from "node:fs";
|
|
6
6
|
import { join, dirname } from "node:path";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
|
-
import { execSync } from "node:child_process";
|
|
8
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
inferCommitType,
|
|
@@ -371,6 +371,18 @@ describe('git-service', async () => {
|
|
|
371
371
|
return dir;
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
function gitRun(args: string[], cwd: string): string {
|
|
375
|
+
return execFileSync("git", args, {
|
|
376
|
+
cwd,
|
|
377
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
378
|
+
encoding: "utf-8",
|
|
379
|
+
env: {
|
|
380
|
+
...process.env,
|
|
381
|
+
GIT_ALLOW_PROTOCOL: "file",
|
|
382
|
+
},
|
|
383
|
+
}).trim();
|
|
384
|
+
}
|
|
385
|
+
|
|
374
386
|
// ─── GitServiceImpl: smart staging ─────────────────────────────────────
|
|
375
387
|
|
|
376
388
|
test('GitServiceImpl: smart staging', () => {
|
|
@@ -413,6 +425,96 @@ describe('git-service', async () => {
|
|
|
413
425
|
rmSync(repo, { recursive: true, force: true });
|
|
414
426
|
});
|
|
415
427
|
|
|
428
|
+
test('GitServiceImpl: task autoCommit skips keyFiles inside submodules', () => {
|
|
429
|
+
const repo = initTempRepo();
|
|
430
|
+
const subSrc = mkdtempSync(join(tmpdir(), "gsd-git-submodule-src-"));
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
gitRun(["init", "-b", "main"], subSrc);
|
|
434
|
+
gitRun(["config", "user.name", "Pi Test"], subSrc);
|
|
435
|
+
gitRun(["config", "user.email", "pi@example.com"], subSrc);
|
|
436
|
+
createFile(subSrc, "tracked.txt", "initial\n");
|
|
437
|
+
gitRun(["add", "-A"], subSrc);
|
|
438
|
+
gitRun(["commit", "-m", "init submodule"], subSrc);
|
|
439
|
+
|
|
440
|
+
gitRun(["-c", "protocol.file.allow=always", "submodule", "add", `file://${subSrc}`, "sub"], repo);
|
|
441
|
+
gitRun(["commit", "-m", "add submodule"], repo);
|
|
442
|
+
|
|
443
|
+
createFile(repo, "sub/copied.txt", "copied from source\n");
|
|
444
|
+
createFile(repo, "src/feature.ts", "export const feature = true;\n");
|
|
445
|
+
createFile(repo, "src/unrelated.ts", "export const unrelated = true;\n");
|
|
446
|
+
|
|
447
|
+
const svc = new GitServiceImpl(repo);
|
|
448
|
+
const taskContext: TaskCommitContext = {
|
|
449
|
+
taskId: "S01/T01",
|
|
450
|
+
taskDisplayId: "T01",
|
|
451
|
+
taskTitle: "fix submodule staging",
|
|
452
|
+
milestoneId: "M001",
|
|
453
|
+
milestoneTitle: "Submodule auto commit",
|
|
454
|
+
sliceId: "S01",
|
|
455
|
+
sliceTitle: "Commit scoped files",
|
|
456
|
+
oneLiner: "Fixed auto commit when key files include submodule paths",
|
|
457
|
+
keyFiles: ["sub/copied.txt", "src/feature.ts"],
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const result = svc.autoCommit("execute-task", "M001/S01/T01", [], taskContext);
|
|
461
|
+
|
|
462
|
+
assert.ok(result !== null, "autoCommit should commit non-submodule changes");
|
|
463
|
+
const committed = gitRun(["show", "--name-only", "--format=", "HEAD"], repo);
|
|
464
|
+
assert.ok(committed.includes("src/feature.ts"), "non-submodule keyFile is committed");
|
|
465
|
+
assert.ok(!committed.includes("sub/copied.txt"), "submodule inner keyFile is not pathspec-staged");
|
|
466
|
+
assert.ok(!committed.includes("src/unrelated.ts"), "scoped staging does not fall back to smartStage");
|
|
467
|
+
} finally {
|
|
468
|
+
rmSync(repo, { recursive: true, force: true });
|
|
469
|
+
rmSync(subSrc, { recursive: true, force: true });
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test('GitServiceImpl: all keyFiles inside submodules falls back to smartStage', () => {
|
|
474
|
+
const repo = initTempRepo();
|
|
475
|
+
const subSrc = mkdtempSync(join(tmpdir(), "gsd-git-all-submodule-src-"));
|
|
476
|
+
|
|
477
|
+
try {
|
|
478
|
+
gitRun(["init", "-b", "main"], subSrc);
|
|
479
|
+
gitRun(["config", "user.name", "Pi Test"], subSrc);
|
|
480
|
+
gitRun(["config", "user.email", "pi@example.com"], subSrc);
|
|
481
|
+
createFile(subSrc, "tracked.txt", "initial\n");
|
|
482
|
+
gitRun(["add", "-A"], subSrc);
|
|
483
|
+
gitRun(["commit", "-m", "init submodule"], subSrc);
|
|
484
|
+
|
|
485
|
+
gitRun(["-c", "protocol.file.allow=always", "submodule", "add", `file://${subSrc}`, "sub"], repo);
|
|
486
|
+
gitRun(["commit", "-m", "add submodule"], repo);
|
|
487
|
+
|
|
488
|
+
createFile(repo, "sub/file1.txt", "inside submodule\n");
|
|
489
|
+
createFile(repo, "sub/file2.txt", "also inside\n");
|
|
490
|
+
createFile(repo, "src/real.ts", "export const real = true;\n");
|
|
491
|
+
|
|
492
|
+
const svc = new GitServiceImpl(repo);
|
|
493
|
+
const taskContext: TaskCommitContext = {
|
|
494
|
+
taskId: "S01/T02",
|
|
495
|
+
taskDisplayId: "T02",
|
|
496
|
+
taskTitle: "all keyFiles inside submodule",
|
|
497
|
+
milestoneId: "M001",
|
|
498
|
+
milestoneTitle: "Submodule auto commit",
|
|
499
|
+
sliceId: "S01",
|
|
500
|
+
sliceTitle: "Commit scoped files",
|
|
501
|
+
oneLiner: "Fell back when all key files are inside submodules",
|
|
502
|
+
keyFiles: ["sub", "sub/file1.txt", "sub/file2.txt"],
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
const result = svc.autoCommit("execute-task", "M001/S01/T02", [], taskContext);
|
|
506
|
+
|
|
507
|
+
assert.ok(result !== null, "autoCommit falls back to smartStage when all keyFiles are filtered");
|
|
508
|
+
const committed = gitRun(["show", "--name-only", "--format=", "HEAD"], repo);
|
|
509
|
+
assert.ok(!committed.includes("sub/file1.txt"), "submodule keyFile is not committed");
|
|
510
|
+
assert.ok(!committed.includes("sub/file2.txt"), "submodule keyFile is not committed");
|
|
511
|
+
assert.ok(committed.includes("src/real.ts"), "smartStage fallback commits other dirty files");
|
|
512
|
+
} finally {
|
|
513
|
+
rmSync(repo, { recursive: true, force: true });
|
|
514
|
+
rmSync(subSrc, { recursive: true, force: true });
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
|
|
416
518
|
// ─── GitServiceImpl: smart staging excludes tracked runtime files ──────
|
|
417
519
|
|
|
418
520
|
test('GitServiceImpl: smart staging excludes tracked runtime files', () => {
|
package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts
CHANGED
|
@@ -195,6 +195,11 @@ describe("infrastructure error detection", () => {
|
|
|
195
195
|
assert.equal(isInfrastructureError(err), "EAGAIN");
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
+
test("ENOBUFS (no buffer space available) is detected", () => {
|
|
199
|
+
const err = Object.assign(new Error("spawnSync git ENOBUFS"), { code: "ENOBUFS" });
|
|
200
|
+
assert.equal(isInfrastructureError(err), "ENOBUFS");
|
|
201
|
+
});
|
|
202
|
+
|
|
198
203
|
test("SQLite WAL corruption is detected via message scan", () => {
|
|
199
204
|
const err = new Error("database disk image is malformed");
|
|
200
205
|
assert.equal(isInfrastructureError(err), "SQLITE_CORRUPT");
|
|
@@ -223,7 +228,7 @@ describe("infrastructure error detection", () => {
|
|
|
223
228
|
test("all INFRA_ERROR_CODES are covered", () => {
|
|
224
229
|
const expectedCodes = [
|
|
225
230
|
"ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE",
|
|
226
|
-
"ENFILE", "EAGAIN", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
|
|
231
|
+
"ENFILE", "EAGAIN", "ENOBUFS", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
|
|
227
232
|
];
|
|
228
233
|
for (const code of expectedCodes) {
|
|
229
234
|
assert.ok(INFRA_ERROR_CODES.has(code), `${code} should be in INFRA_ERROR_CODES`);
|
|
@@ -41,6 +41,15 @@ const SAMPLE_ROADMAP = `# Project Roadmap
|
|
|
41
41
|
- [ ] 31 — Notifications
|
|
42
42
|
`;
|
|
43
43
|
|
|
44
|
+
const SAMPLE_VERSION_PREFIX_ROADMAP = `# Project Roadmap
|
|
45
|
+
|
|
46
|
+
## Phases
|
|
47
|
+
|
|
48
|
+
- ✅ **v1.0 MVP** — Phases 1-6 (shipped 2026-02-24)
|
|
49
|
+
- ✅ **v1.1 Onboarding** — Phases 7-9 (shipped 2026-03-01)
|
|
50
|
+
- 🚧 **v1.8 Production** — Phases 44-53
|
|
51
|
+
`;
|
|
52
|
+
|
|
44
53
|
const SAMPLE_PROJECT = `# My Project
|
|
45
54
|
|
|
46
55
|
A sample project for testing the migration parser.
|
|
@@ -246,6 +255,21 @@ test('parseOldRoadmap: flat format', () => {
|
|
|
246
255
|
assert.deepStrictEqual(roadmap.phases[1].done, false, 'flat roadmap: second phase not done');
|
|
247
256
|
});
|
|
248
257
|
|
|
258
|
+
test('parseOldRoadmap: emoji version-prefix phase ranges', () => {
|
|
259
|
+
const roadmap = parseOldRoadmap(SAMPLE_VERSION_PREFIX_ROADMAP);
|
|
260
|
+
assert.deepStrictEqual(roadmap.milestones.length, 0, 'version roadmap: no milestone sections');
|
|
261
|
+
assert.deepStrictEqual(roadmap.phases.length, 3, 'version roadmap: 3 phase ranges');
|
|
262
|
+
assert.deepStrictEqual(roadmap.phases[0].number, 1, 'version roadmap: first range starts at phase 1');
|
|
263
|
+
assert.deepStrictEqual(roadmap.phases[0].title, 'MVP', 'version roadmap: first title');
|
|
264
|
+
assert.deepStrictEqual(roadmap.phases[0].done, true, 'version roadmap: first range done');
|
|
265
|
+
assert.deepStrictEqual(roadmap.phases[1].number, 7, 'version roadmap: second range starts at phase 7');
|
|
266
|
+
assert.deepStrictEqual(roadmap.phases[1].title, 'Onboarding', 'version roadmap: second title');
|
|
267
|
+
assert.deepStrictEqual(roadmap.phases[1].done, true, 'version roadmap: second range done');
|
|
268
|
+
assert.deepStrictEqual(roadmap.phases[2].number, 44, 'version roadmap: third range starts at phase 44');
|
|
269
|
+
assert.deepStrictEqual(roadmap.phases[2].title, 'Production', 'version roadmap: third title');
|
|
270
|
+
assert.deepStrictEqual(roadmap.phases[2].done, false, 'version roadmap: third range in progress');
|
|
271
|
+
});
|
|
272
|
+
|
|
249
273
|
test('parseOldRoadmap: milestone-sectioned with <details>', () => {
|
|
250
274
|
const roadmap = parseOldRoadmap(SAMPLE_MILESTONE_SECTIONED_ROADMAP);
|
|
251
275
|
assert.ok(roadmap.milestones.length >= 2, 'ms roadmap: has milestone sections');
|
|
@@ -387,4 +411,3 @@ test('parseOldProject', () => {
|
|
|
387
411
|
const project = parseOldProject(SAMPLE_PROJECT);
|
|
388
412
|
assert.deepStrictEqual(project, SAMPLE_PROJECT, 'project: returns raw content');
|
|
389
413
|
});
|
|
390
|
-
|
|
@@ -13,17 +13,19 @@
|
|
|
13
13
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
14
14
|
import assert from "node:assert/strict";
|
|
15
15
|
import { chmodSync, existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
16
|
+
import { delimiter, join } from "node:path";
|
|
17
17
|
import { tmpdir } from "node:os";
|
|
18
18
|
import { execFileSync } from "node:child_process";
|
|
19
19
|
import {
|
|
20
20
|
assertWorktreeMaterialized,
|
|
21
21
|
nativeBranchDelete,
|
|
22
22
|
nativeCommit,
|
|
23
|
+
nativeGetCurrentBranch,
|
|
23
24
|
nativeIsRepo,
|
|
24
25
|
nativeResetHard,
|
|
25
26
|
nativeWorktreeAdd,
|
|
26
27
|
} from "../native-git-bridge.js";
|
|
28
|
+
import { GIT_NO_PROMPT_ENV } from "../git-constants.js";
|
|
27
29
|
|
|
28
30
|
// Note: prior static-analysis tests that scanned native-git-bridge.ts for
|
|
29
31
|
// the raw shell-spawn pattern were removed under #4827 — the integration
|
|
@@ -78,6 +80,55 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
|
|
|
78
80
|
assert.equal(subject, "test: regression commit #4180");
|
|
79
81
|
});
|
|
80
82
|
|
|
83
|
+
test("nativeCommit retries once after transient ENOBUFS from git", (t) => {
|
|
84
|
+
const bin = mkdtempSync(join(tmpdir(), "ngb-enobufs-bin-"));
|
|
85
|
+
t.after(() => rmSync(bin, { recursive: true, force: true }));
|
|
86
|
+
|
|
87
|
+
const realGit = execFileSync("git", ["--exec-path"], { encoding: "utf-8" }).trim();
|
|
88
|
+
const attempts = join(bin, "attempts.txt");
|
|
89
|
+
const fakeGit = join(bin, "fake-git.cjs");
|
|
90
|
+
writeFileSync(fakeGit, `
|
|
91
|
+
const { appendFileSync, readFileSync } = require("node:fs");
|
|
92
|
+
const { spawnSync } = require("node:child_process");
|
|
93
|
+
const attempts = ${JSON.stringify(attempts)};
|
|
94
|
+
const realGit = ${JSON.stringify(join(realGit, process.platform === "win32" ? "git.exe" : "git"))};
|
|
95
|
+
appendFileSync(attempts, "1");
|
|
96
|
+
if (process.argv[2] === "commit" && readFileSync(attempts, "utf-8").length === 1) {
|
|
97
|
+
console.error("spawnSync git ENOBUFS");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const result = spawnSync(realGit, process.argv.slice(2), { stdio: "inherit" });
|
|
101
|
+
process.exit(result.status ?? 1);
|
|
102
|
+
`, "utf-8");
|
|
103
|
+
|
|
104
|
+
if (process.platform === "win32") {
|
|
105
|
+
writeFileSync(join(bin, "git.cmd"), `@echo off\r\nnode "${fakeGit}" %*\r\n`, "utf-8");
|
|
106
|
+
} else {
|
|
107
|
+
const shim = join(bin, "git");
|
|
108
|
+
writeFileSync(shim, `#!/bin/sh\nexec node "${fakeGit}" "$@"\n`, "utf-8");
|
|
109
|
+
chmodSync(shim, 0o755);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
writeFileSync(join(repo, "file.txt"), "retry commit\n");
|
|
113
|
+
git(["add", "."], repo);
|
|
114
|
+
|
|
115
|
+
const originalPath = process.env.PATH ?? "";
|
|
116
|
+
const gitEnv = GIT_NO_PROMPT_ENV as NodeJS.ProcessEnv;
|
|
117
|
+
const originalGitEnvPath = gitEnv.PATH;
|
|
118
|
+
try {
|
|
119
|
+
process.env.PATH = `${bin}${delimiter}${originalPath}`;
|
|
120
|
+
gitEnv.PATH = process.env.PATH;
|
|
121
|
+
const result = nativeCommit(repo, "test: retry ENOBUFS commit");
|
|
122
|
+
assert.ok(result !== null, "commit should succeed after retry");
|
|
123
|
+
} finally {
|
|
124
|
+
process.env.PATH = originalPath;
|
|
125
|
+
gitEnv.PATH = originalGitEnvPath;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
assert.equal(readFileSync(attempts, "utf-8").length, 2);
|
|
129
|
+
assert.equal(git(["log", "-1", "--format=%s"], repo), "test: retry ENOBUFS commit");
|
|
130
|
+
});
|
|
131
|
+
|
|
81
132
|
test("nativeCommit runs commit hooks", () => {
|
|
82
133
|
const hookPath = join(repo, ".git", "hooks", "commit-msg");
|
|
83
134
|
const marker = join(repo, "hook-ran.txt");
|
|
@@ -119,7 +170,17 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
|
|
|
119
170
|
test("nativeBranchDelete throws when git cannot delete the branch", () => {
|
|
120
171
|
assert.throws(
|
|
121
172
|
() => nativeBranchDelete(repo, "does-not-exist"),
|
|
122
|
-
/
|
|
173
|
+
/git branch -D does-not-exist failed[\s\S]*does-not-exist/,
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("nativeGetCurrentBranch preserves git stderr in fallback errors", (t) => {
|
|
178
|
+
const dir = mkdtempSync(join(tmpdir(), "ngb-stderr-notrepo-"));
|
|
179
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
180
|
+
|
|
181
|
+
assert.throws(
|
|
182
|
+
() => nativeGetCurrentBranch(dir),
|
|
183
|
+
/git branch --show-current failed[\s\S]*(not a git repository|not a git repo|fatal:)/,
|
|
123
184
|
);
|
|
124
185
|
});
|
|
125
186
|
|