gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.5a170d0
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 +1 -1
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/onboarding.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +28 -10
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +64 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
- package/dist/resources/extensions/gsd/context-injector.js +74 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
- package/dist/resources/extensions/gsd/custom-verification.js +145 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
- package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
- package/dist/resources/extensions/gsd/engine-types.js +8 -0
- package/dist/resources/extensions/gsd/execution-policy.js +8 -0
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +10 -3
- package/dist/resources/extensions/gsd/git-service.js +5 -1
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +46 -2
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +138 -0
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/resources/skills/create-workflow/SKILL.md +103 -0
- package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
- 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 +1 -1
- 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/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- 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 +16 -16
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- 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/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/chunks/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +29 -7
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- 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 +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +30 -10
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +71 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
- package/src/resources/extensions/gsd/context-injector.ts +100 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
- package/src/resources/extensions/gsd/custom-verification.ts +180 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
- package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
- package/src/resources/extensions/gsd/engine-types.ts +71 -0
- package/src/resources/extensions/gsd/execution-policy.ts +43 -0
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +9 -3
- package/src/resources/extensions/gsd/git-service.ts +5 -4
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +47 -2
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +171 -0
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
- package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/src/resources/skills/create-workflow/SKILL.md +103 -0
- package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
chmodSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
emitJournalEvent,
|
|
17
|
+
queryJournal,
|
|
18
|
+
type JournalEntry,
|
|
19
|
+
} from "../journal.ts";
|
|
20
|
+
|
|
21
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
function makeTmpBase(): string {
|
|
24
|
+
const base = join(tmpdir(), `gsd-journal-test-${randomUUID()}`);
|
|
25
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
26
|
+
return base;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cleanup(base: string): void {
|
|
30
|
+
try {
|
|
31
|
+
rmSync(base, { recursive: true, force: true });
|
|
32
|
+
} catch {
|
|
33
|
+
/* */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeEntry(overrides: Partial<JournalEntry> = {}): JournalEntry {
|
|
38
|
+
return {
|
|
39
|
+
ts: "2025-03-21T10:00:00.000Z",
|
|
40
|
+
flowId: "flow-aaa",
|
|
41
|
+
seq: 0,
|
|
42
|
+
eventType: "iteration-start",
|
|
43
|
+
...overrides,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── emitJournalEvent ─────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
test("emitJournalEvent creates journal directory and JSONL file", () => {
|
|
50
|
+
const base = makeTmpBase();
|
|
51
|
+
try {
|
|
52
|
+
const entry = makeEntry();
|
|
53
|
+
emitJournalEvent(base, entry);
|
|
54
|
+
|
|
55
|
+
const filePath = join(base, ".gsd", "journal", "2025-03-21.jsonl");
|
|
56
|
+
assert.ok(existsSync(filePath), "JSONL file should exist");
|
|
57
|
+
|
|
58
|
+
const raw = readFileSync(filePath, "utf-8").trim();
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
assert.equal(parsed.ts, entry.ts);
|
|
61
|
+
assert.equal(parsed.flowId, entry.flowId);
|
|
62
|
+
assert.equal(parsed.seq, entry.seq);
|
|
63
|
+
assert.equal(parsed.eventType, entry.eventType);
|
|
64
|
+
} finally {
|
|
65
|
+
cleanup(base);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("emitJournalEvent appends multiple lines to the same file", () => {
|
|
70
|
+
const base = makeTmpBase();
|
|
71
|
+
try {
|
|
72
|
+
emitJournalEvent(base, makeEntry({ seq: 0 }));
|
|
73
|
+
emitJournalEvent(base, makeEntry({ seq: 1, eventType: "dispatch-match" }));
|
|
74
|
+
emitJournalEvent(base, makeEntry({ seq: 2, eventType: "unit-start" }));
|
|
75
|
+
|
|
76
|
+
const filePath = join(base, ".gsd", "journal", "2025-03-21.jsonl");
|
|
77
|
+
const lines = readFileSync(filePath, "utf-8").trim().split("\n");
|
|
78
|
+
assert.equal(lines.length, 3, "Should have 3 lines");
|
|
79
|
+
|
|
80
|
+
const parsed = lines.map(l => JSON.parse(l));
|
|
81
|
+
assert.equal(parsed[0].seq, 0);
|
|
82
|
+
assert.equal(parsed[1].seq, 1);
|
|
83
|
+
assert.equal(parsed[2].seq, 2);
|
|
84
|
+
assert.equal(parsed[1].eventType, "dispatch-match");
|
|
85
|
+
} finally {
|
|
86
|
+
cleanup(base);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("emitJournalEvent auto-creates nonexistent parent directory", () => {
|
|
91
|
+
const base = join(tmpdir(), `gsd-journal-test-${randomUUID()}`);
|
|
92
|
+
// Don't create .gsd/ — emitJournalEvent should handle it via mkdirSync recursive
|
|
93
|
+
try {
|
|
94
|
+
emitJournalEvent(base, makeEntry());
|
|
95
|
+
const filePath = join(base, ".gsd", "journal", "2025-03-21.jsonl");
|
|
96
|
+
assert.ok(existsSync(filePath), "File should exist even when parent dirs did not");
|
|
97
|
+
} finally {
|
|
98
|
+
cleanup(base);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("emitJournalEvent preserves optional fields (rule, causedBy, data)", () => {
|
|
103
|
+
const base = makeTmpBase();
|
|
104
|
+
try {
|
|
105
|
+
const entry = makeEntry({
|
|
106
|
+
rule: "my-dispatch-rule",
|
|
107
|
+
causedBy: { flowId: "flow-prior", seq: 3 },
|
|
108
|
+
data: { unitId: "M001/S01/T01", status: "ok" },
|
|
109
|
+
});
|
|
110
|
+
emitJournalEvent(base, entry);
|
|
111
|
+
|
|
112
|
+
const filePath = join(base, ".gsd", "journal", "2025-03-21.jsonl");
|
|
113
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf-8").trim());
|
|
114
|
+
assert.equal(parsed.rule, "my-dispatch-rule");
|
|
115
|
+
assert.deepEqual(parsed.causedBy, { flowId: "flow-prior", seq: 3 });
|
|
116
|
+
assert.equal(parsed.data.unitId, "M001/S01/T01");
|
|
117
|
+
assert.equal(parsed.data.status, "ok");
|
|
118
|
+
} finally {
|
|
119
|
+
cleanup(base);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("emitJournalEvent silently catches write errors (no throw)", () => {
|
|
124
|
+
// Use a path that can't be created — null bytes in path
|
|
125
|
+
assert.doesNotThrow(() => {
|
|
126
|
+
emitJournalEvent("/dev/null/impossible\0path", makeEntry());
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("emitJournalEvent silently catches read-only directory errors", () => {
|
|
131
|
+
const base = makeTmpBase();
|
|
132
|
+
const journalDir = join(base, ".gsd", "journal");
|
|
133
|
+
mkdirSync(journalDir, { recursive: true });
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// Make the journal directory read-only
|
|
137
|
+
chmodSync(journalDir, 0o444);
|
|
138
|
+
|
|
139
|
+
// Should not throw
|
|
140
|
+
assert.doesNotThrow(() => {
|
|
141
|
+
emitJournalEvent(base, makeEntry());
|
|
142
|
+
});
|
|
143
|
+
} finally {
|
|
144
|
+
// Restore permissions for cleanup
|
|
145
|
+
try {
|
|
146
|
+
chmodSync(journalDir, 0o755);
|
|
147
|
+
} catch {
|
|
148
|
+
/* */
|
|
149
|
+
}
|
|
150
|
+
cleanup(base);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// ─── Daily Rotation ───────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
test("daily rotation: events with different dates go to different files", () => {
|
|
157
|
+
const base = makeTmpBase();
|
|
158
|
+
try {
|
|
159
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-20T23:59:59.000Z" }));
|
|
160
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-21T00:00:01.000Z" }));
|
|
161
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-22T12:00:00.000Z" }));
|
|
162
|
+
|
|
163
|
+
const journalDir = join(base, ".gsd", "journal");
|
|
164
|
+
assert.ok(existsSync(join(journalDir, "2025-03-20.jsonl")));
|
|
165
|
+
assert.ok(existsSync(join(journalDir, "2025-03-21.jsonl")));
|
|
166
|
+
assert.ok(existsSync(join(journalDir, "2025-03-22.jsonl")));
|
|
167
|
+
|
|
168
|
+
// Verify each file has exactly one line
|
|
169
|
+
for (const date of ["2025-03-20", "2025-03-21", "2025-03-22"]) {
|
|
170
|
+
const lines = readFileSync(join(journalDir, `${date}.jsonl`), "utf-8")
|
|
171
|
+
.trim()
|
|
172
|
+
.split("\n");
|
|
173
|
+
assert.equal(lines.length, 1, `${date}.jsonl should have 1 line`);
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
cleanup(base);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ─── queryJournal ─────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
test("queryJournal returns all entries when no filters provided", () => {
|
|
183
|
+
const base = makeTmpBase();
|
|
184
|
+
try {
|
|
185
|
+
emitJournalEvent(base, makeEntry({ seq: 0 }));
|
|
186
|
+
emitJournalEvent(base, makeEntry({ seq: 1, eventType: "dispatch-match" }));
|
|
187
|
+
|
|
188
|
+
const results = queryJournal(base);
|
|
189
|
+
assert.equal(results.length, 2);
|
|
190
|
+
assert.equal(results[0].seq, 0);
|
|
191
|
+
assert.equal(results[1].seq, 1);
|
|
192
|
+
} finally {
|
|
193
|
+
cleanup(base);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("queryJournal filters by flowId", () => {
|
|
198
|
+
const base = makeTmpBase();
|
|
199
|
+
try {
|
|
200
|
+
emitJournalEvent(base, makeEntry({ flowId: "flow-aaa", seq: 0 }));
|
|
201
|
+
emitJournalEvent(base, makeEntry({ flowId: "flow-bbb", seq: 1 }));
|
|
202
|
+
emitJournalEvent(base, makeEntry({ flowId: "flow-aaa", seq: 2 }));
|
|
203
|
+
|
|
204
|
+
const results = queryJournal(base, { flowId: "flow-aaa" });
|
|
205
|
+
assert.equal(results.length, 2);
|
|
206
|
+
assert.ok(results.every(e => e.flowId === "flow-aaa"));
|
|
207
|
+
} finally {
|
|
208
|
+
cleanup(base);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("queryJournal filters by eventType", () => {
|
|
213
|
+
const base = makeTmpBase();
|
|
214
|
+
try {
|
|
215
|
+
emitJournalEvent(base, makeEntry({ eventType: "iteration-start", seq: 0 }));
|
|
216
|
+
emitJournalEvent(base, makeEntry({ eventType: "dispatch-match", seq: 1 }));
|
|
217
|
+
emitJournalEvent(base, makeEntry({ eventType: "unit-start", seq: 2 }));
|
|
218
|
+
emitJournalEvent(base, makeEntry({ eventType: "dispatch-match", seq: 3 }));
|
|
219
|
+
|
|
220
|
+
const results = queryJournal(base, { eventType: "dispatch-match" });
|
|
221
|
+
assert.equal(results.length, 2);
|
|
222
|
+
assert.ok(results.every(e => e.eventType === "dispatch-match"));
|
|
223
|
+
} finally {
|
|
224
|
+
cleanup(base);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("queryJournal filters by unitId (from data.unitId)", () => {
|
|
229
|
+
const base = makeTmpBase();
|
|
230
|
+
try {
|
|
231
|
+
emitJournalEvent(
|
|
232
|
+
base,
|
|
233
|
+
makeEntry({ seq: 0, data: { unitId: "M001/S01/T01" } }),
|
|
234
|
+
);
|
|
235
|
+
emitJournalEvent(
|
|
236
|
+
base,
|
|
237
|
+
makeEntry({ seq: 1, data: { unitId: "M001/S01/T02" } }),
|
|
238
|
+
);
|
|
239
|
+
emitJournalEvent(
|
|
240
|
+
base,
|
|
241
|
+
makeEntry({ seq: 2, data: { unitId: "M001/S01/T01" } }),
|
|
242
|
+
);
|
|
243
|
+
emitJournalEvent(base, makeEntry({ seq: 3 })); // no data
|
|
244
|
+
|
|
245
|
+
const results = queryJournal(base, { unitId: "M001/S01/T01" });
|
|
246
|
+
assert.equal(results.length, 2);
|
|
247
|
+
assert.ok(
|
|
248
|
+
results.every(
|
|
249
|
+
e => (e.data as Record<string, unknown>)?.unitId === "M001/S01/T01",
|
|
250
|
+
),
|
|
251
|
+
);
|
|
252
|
+
} finally {
|
|
253
|
+
cleanup(base);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("queryJournal filters by time range (after/before)", () => {
|
|
258
|
+
const base = makeTmpBase();
|
|
259
|
+
try {
|
|
260
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-20T08:00:00.000Z", seq: 0 }));
|
|
261
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-21T10:00:00.000Z", seq: 1 }));
|
|
262
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-21T15:00:00.000Z", seq: 2 }));
|
|
263
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-22T20:00:00.000Z", seq: 3 }));
|
|
264
|
+
|
|
265
|
+
// After only
|
|
266
|
+
const afterResults = queryJournal(base, { after: "2025-03-21T00:00:00.000Z" });
|
|
267
|
+
assert.equal(afterResults.length, 3, "3 entries on or after 2025-03-21");
|
|
268
|
+
|
|
269
|
+
// Before only
|
|
270
|
+
const beforeResults = queryJournal(base, { before: "2025-03-21T12:00:00.000Z" });
|
|
271
|
+
assert.equal(beforeResults.length, 2, "2 entries on or before noon on 03-21");
|
|
272
|
+
|
|
273
|
+
// Both after and before
|
|
274
|
+
const rangeResults = queryJournal(base, {
|
|
275
|
+
after: "2025-03-21T00:00:00.000Z",
|
|
276
|
+
before: "2025-03-21T23:59:59.000Z",
|
|
277
|
+
});
|
|
278
|
+
assert.equal(rangeResults.length, 2, "2 entries within 2025-03-21");
|
|
279
|
+
} finally {
|
|
280
|
+
cleanup(base);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("queryJournal combines multiple filters", () => {
|
|
285
|
+
const base = makeTmpBase();
|
|
286
|
+
try {
|
|
287
|
+
emitJournalEvent(
|
|
288
|
+
base,
|
|
289
|
+
makeEntry({ flowId: "flow-aaa", eventType: "unit-start", seq: 0 }),
|
|
290
|
+
);
|
|
291
|
+
emitJournalEvent(
|
|
292
|
+
base,
|
|
293
|
+
makeEntry({ flowId: "flow-aaa", eventType: "dispatch-match", seq: 1 }),
|
|
294
|
+
);
|
|
295
|
+
emitJournalEvent(
|
|
296
|
+
base,
|
|
297
|
+
makeEntry({ flowId: "flow-bbb", eventType: "unit-start", seq: 2 }),
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const results = queryJournal(base, {
|
|
301
|
+
flowId: "flow-aaa",
|
|
302
|
+
eventType: "unit-start",
|
|
303
|
+
});
|
|
304
|
+
assert.equal(results.length, 1);
|
|
305
|
+
assert.equal(results[0].flowId, "flow-aaa");
|
|
306
|
+
assert.equal(results[0].eventType, "unit-start");
|
|
307
|
+
} finally {
|
|
308
|
+
cleanup(base);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("queryJournal on nonexistent directory returns empty array", () => {
|
|
313
|
+
const base = join(tmpdir(), `gsd-journal-test-${randomUUID()}`);
|
|
314
|
+
// Don't create anything
|
|
315
|
+
try {
|
|
316
|
+
const results = queryJournal(base);
|
|
317
|
+
assert.deepEqual(results, []);
|
|
318
|
+
} finally {
|
|
319
|
+
cleanup(base);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("queryJournal skips malformed JSON lines gracefully", () => {
|
|
324
|
+
const base = makeTmpBase();
|
|
325
|
+
try {
|
|
326
|
+
const journalDir = join(base, ".gsd", "journal");
|
|
327
|
+
mkdirSync(journalDir, { recursive: true });
|
|
328
|
+
|
|
329
|
+
// Write a file with a mix of valid and invalid lines
|
|
330
|
+
const validEntry = JSON.stringify(makeEntry({ seq: 0 }));
|
|
331
|
+
const content = `${validEntry}\n{not valid json\n${JSON.stringify(makeEntry({ seq: 1 }))}\n`;
|
|
332
|
+
writeFileSync(join(journalDir, "2025-03-21.jsonl"), content);
|
|
333
|
+
|
|
334
|
+
const results = queryJournal(base);
|
|
335
|
+
assert.equal(results.length, 2, "Should skip the malformed line");
|
|
336
|
+
assert.equal(results[0].seq, 0);
|
|
337
|
+
assert.equal(results[1].seq, 1);
|
|
338
|
+
} finally {
|
|
339
|
+
cleanup(base);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("queryJournal reads across multiple daily files", () => {
|
|
344
|
+
const base = makeTmpBase();
|
|
345
|
+
try {
|
|
346
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-20T12:00:00.000Z", seq: 0 }));
|
|
347
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-21T12:00:00.000Z", seq: 1 }));
|
|
348
|
+
emitJournalEvent(base, makeEntry({ ts: "2025-03-22T12:00:00.000Z", seq: 2 }));
|
|
349
|
+
|
|
350
|
+
const results = queryJournal(base);
|
|
351
|
+
assert.equal(results.length, 3, "Should read from all 3 files");
|
|
352
|
+
// Files are sorted, so order should be chronological
|
|
353
|
+
assert.equal(results[0].ts, "2025-03-20T12:00:00.000Z");
|
|
354
|
+
assert.equal(results[1].ts, "2025-03-21T12:00:00.000Z");
|
|
355
|
+
assert.equal(results[2].ts, "2025-03-22T12:00:00.000Z");
|
|
356
|
+
} finally {
|
|
357
|
+
cleanup(base);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("queryJournal filters by rule", () => {
|
|
362
|
+
const base = makeTmpBase();
|
|
363
|
+
try {
|
|
364
|
+
emitJournalEvent(
|
|
365
|
+
base,
|
|
366
|
+
makeEntry({ seq: 0, eventType: "dispatch-match", rule: "dispatch-task" }),
|
|
367
|
+
);
|
|
368
|
+
emitJournalEvent(
|
|
369
|
+
base,
|
|
370
|
+
makeEntry({ seq: 1, eventType: "post-unit-hook", rule: "post-unit-hook" }),
|
|
371
|
+
);
|
|
372
|
+
emitJournalEvent(
|
|
373
|
+
base,
|
|
374
|
+
makeEntry({ seq: 2, eventType: "dispatch-match", rule: "dispatch-task" }),
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const results = queryJournal(base, { rule: "dispatch-task" });
|
|
378
|
+
assert.equal(results.length, 2, "Should return only dispatch-task entries");
|
|
379
|
+
assert.ok(
|
|
380
|
+
results.every(e => e.rule === "dispatch-task"),
|
|
381
|
+
"All results should have rule === 'dispatch-task'",
|
|
382
|
+
);
|
|
383
|
+
} finally {
|
|
384
|
+
cleanup(base);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
@@ -187,6 +187,36 @@ console.log('=== md-importer: malformed/empty rows skipped ===');
|
|
|
187
187
|
assertEq(decisions[1].id, 'D003', 'second valid row (skipping malformed)');
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
console.log('=== md-importer: made_by backward compatibility (old 7-column format) ===');
|
|
191
|
+
|
|
192
|
+
{
|
|
193
|
+
const decisions = parseDecisionsTable(DECISIONS_MD);
|
|
194
|
+
// Old format has no Made By column — should default to 'agent'
|
|
195
|
+
for (const d of decisions) {
|
|
196
|
+
assertEq(d.made_by, 'agent', `${d.id} made_by defaults to agent for legacy format`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('=== md-importer: made_by column parsing (new 8-column format) ===');
|
|
201
|
+
|
|
202
|
+
{
|
|
203
|
+
const newFormatMd = `# Decisions Register
|
|
204
|
+
|
|
205
|
+
| # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |
|
|
206
|
+
|---|------|-------|----------|--------|-----------|------------|---------|
|
|
207
|
+
| D001 | M001 | library | SQLite library | better-sqlite3 | Sync API | No | human |
|
|
208
|
+
| D002 | M001 | arch | DB location | .gsd/gsd.db | Derived state | No | agent |
|
|
209
|
+
| D003 | M002 | impl | Config format | JSON | Simple | Yes | collaborative |
|
|
210
|
+
| D004 | M002 | impl | Cache strategy | LRU | Predictable | No | bogus |
|
|
211
|
+
`;
|
|
212
|
+
const decisions = parseDecisionsTable(newFormatMd);
|
|
213
|
+
assertEq(decisions.length, 4, 'should parse 4 decisions with new format');
|
|
214
|
+
assertEq(decisions[0].made_by, 'human', 'D001 made_by = human');
|
|
215
|
+
assertEq(decisions[1].made_by, 'agent', 'D002 made_by = agent');
|
|
216
|
+
assertEq(decisions[2].made_by, 'collaborative', 'D003 made_by = collaborative');
|
|
217
|
+
assertEq(decisions[3].made_by, 'agent', 'D004 invalid made_by defaults to agent');
|
|
218
|
+
}
|
|
219
|
+
|
|
190
220
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
191
221
|
// md-importer: parseRequirementsSections
|
|
192
222
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -354,7 +384,7 @@ console.log('=== md-importer: schema v1→v2 migration ===');
|
|
|
354
384
|
openDatabase(':memory:');
|
|
355
385
|
const adapter = _getAdapter();
|
|
356
386
|
const version = adapter?.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
357
|
-
assertEq(version?.v,
|
|
387
|
+
assertEq(version?.v, 4, 'new DB should be at schema version 4');
|
|
358
388
|
|
|
359
389
|
// Artifacts table should exist
|
|
360
390
|
const tableCheck = adapter?.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='artifacts'").get();
|
|
@@ -335,9 +335,9 @@ console.log('\n=== memory-store: schema includes memories table ===');
|
|
|
335
335
|
const viewCount = adapter.prepare('SELECT count(*) as cnt FROM active_memories').get();
|
|
336
336
|
assertEq(viewCount?.['cnt'], 0, 'active_memories view should exist');
|
|
337
337
|
|
|
338
|
-
// Verify schema version is
|
|
338
|
+
// Verify schema version is 4
|
|
339
339
|
const version = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
340
|
-
assertEq(version?.['v'],
|
|
340
|
+
assertEq(version?.['v'], 4, 'schema version should be 4');
|
|
341
341
|
|
|
342
342
|
closeDatabase();
|
|
343
343
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// milestone-id-reservation — Verifies that preview IDs from guided-flow
|
|
2
|
-
// match the IDs claimed by
|
|
2
|
+
// match the IDs claimed by gsd_milestone_generate_id via the shared
|
|
3
3
|
// reservation mechanism in milestone-ids.ts.
|
|
4
4
|
//
|
|
5
5
|
// Regression test for #1569.
|
|
@@ -652,6 +652,116 @@ console.log('\n=== parsePlan: new-format task entries with Files and Verify subl
|
|
|
652
652
|
assertTrue(p.tasks[0].description.includes('Why: because we need typed plan entries'), 'Why line accumulates into description');
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
console.log('\n=== parsePlan: heading-style task entries (### T01 -- Title) ===');
|
|
656
|
+
{
|
|
657
|
+
const content = `# S11: Heading Style
|
|
658
|
+
|
|
659
|
+
**Goal:** Test heading-style task parsing.
|
|
660
|
+
**Demo:** Parser handles heading-style task entries.
|
|
661
|
+
|
|
662
|
+
## Tasks
|
|
663
|
+
|
|
664
|
+
### T01 -- Implement feature
|
|
665
|
+
|
|
666
|
+
- Why: the feature is needed
|
|
667
|
+
- Files: \`src/feature.ts\`
|
|
668
|
+
- Verify: npm test
|
|
669
|
+
|
|
670
|
+
### T02 -- Write tests \`est:1h\`
|
|
671
|
+
|
|
672
|
+
Some description for the second task.
|
|
673
|
+
`;
|
|
674
|
+
|
|
675
|
+
const p = parsePlan(content);
|
|
676
|
+
assertEq(p.tasks.length, 2, 'heading-style task count');
|
|
677
|
+
assertEq(p.tasks[0].id, 'T01', 'heading T01 id');
|
|
678
|
+
assertEq(p.tasks[0].title, 'Implement feature', 'heading T01 title');
|
|
679
|
+
assertEq(p.tasks[0].done, false, 'heading T01 not done (headings have no checkbox)');
|
|
680
|
+
assertEq(p.tasks[0].files![0], 'src/feature.ts', 'heading T01 files extracted');
|
|
681
|
+
assertEq(p.tasks[0].verify, 'npm test', 'heading T01 verify extracted');
|
|
682
|
+
assertEq(p.tasks[1].id, 'T02', 'heading T02 id');
|
|
683
|
+
assertEq(p.tasks[1].title, 'Write tests', 'heading T02 title');
|
|
684
|
+
assertEq(p.tasks[1].estimate, '1h', 'heading T02 estimate');
|
|
685
|
+
assertTrue(p.tasks[1].description.includes('Some description'), 'heading T02 description');
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
console.log('\n=== parsePlan: heading-style with colon separator (### T01: Title) ===');
|
|
689
|
+
{
|
|
690
|
+
const content = `# S12: Heading Colon Style
|
|
691
|
+
|
|
692
|
+
**Goal:** Test colon-separated heading tasks.
|
|
693
|
+
**Demo:** Parser handles colon separator.
|
|
694
|
+
|
|
695
|
+
## Tasks
|
|
696
|
+
|
|
697
|
+
### T01: Setup project
|
|
698
|
+
Basic project setup steps.
|
|
699
|
+
|
|
700
|
+
### T02: Add CI pipeline \`est:30m\`
|
|
701
|
+
Configure CI.
|
|
702
|
+
`;
|
|
703
|
+
|
|
704
|
+
const p = parsePlan(content);
|
|
705
|
+
assertEq(p.tasks.length, 2, 'colon heading task count');
|
|
706
|
+
assertEq(p.tasks[0].id, 'T01', 'colon heading T01 id');
|
|
707
|
+
assertEq(p.tasks[0].title, 'Setup project', 'colon heading T01 title');
|
|
708
|
+
assertEq(p.tasks[1].id, 'T02', 'colon heading T02 id');
|
|
709
|
+
assertEq(p.tasks[1].title, 'Add CI pipeline', 'colon heading T02 title');
|
|
710
|
+
assertEq(p.tasks[1].estimate, '30m', 'colon heading T02 estimate');
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
console.log('\n=== parsePlan: heading-style with em-dash separator (### T01 — Title) ===');
|
|
714
|
+
{
|
|
715
|
+
const content = `# S13: Em-Dash Style
|
|
716
|
+
|
|
717
|
+
**Goal:** Test em-dash separated heading tasks.
|
|
718
|
+
**Demo:** Parser handles em-dash separator.
|
|
719
|
+
|
|
720
|
+
## Tasks
|
|
721
|
+
|
|
722
|
+
### T01 — Build the widget
|
|
723
|
+
|
|
724
|
+
Widget description.
|
|
725
|
+
`;
|
|
726
|
+
|
|
727
|
+
const p = parsePlan(content);
|
|
728
|
+
assertEq(p.tasks.length, 1, 'em-dash heading task count');
|
|
729
|
+
assertEq(p.tasks[0].id, 'T01', 'em-dash heading T01 id');
|
|
730
|
+
assertEq(p.tasks[0].title, 'Build the widget', 'em-dash heading T01 title');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
console.log('\n=== parsePlan: mixed checkbox and heading-style tasks ===');
|
|
734
|
+
{
|
|
735
|
+
const content = `# S14: Mixed Format
|
|
736
|
+
|
|
737
|
+
**Goal:** Test mixed formats.
|
|
738
|
+
**Demo:** Parser handles both styles in one plan.
|
|
739
|
+
|
|
740
|
+
## Tasks
|
|
741
|
+
|
|
742
|
+
- [ ] **T01: Checkbox task** \`est:20m\`
|
|
743
|
+
A checkbox-style task.
|
|
744
|
+
|
|
745
|
+
### T02 -- Heading task \`est:15m\`
|
|
746
|
+
|
|
747
|
+
A heading-style task.
|
|
748
|
+
|
|
749
|
+
- [x] **T03: Done checkbox task** \`est:10m\`
|
|
750
|
+
Already completed.
|
|
751
|
+
`;
|
|
752
|
+
|
|
753
|
+
const p = parsePlan(content);
|
|
754
|
+
assertEq(p.tasks.length, 3, 'mixed format task count');
|
|
755
|
+
assertEq(p.tasks[0].id, 'T01', 'mixed T01 id');
|
|
756
|
+
assertEq(p.tasks[0].done, false, 'mixed T01 not done');
|
|
757
|
+
assertEq(p.tasks[1].id, 'T02', 'mixed T02 id');
|
|
758
|
+
assertEq(p.tasks[1].title, 'Heading task', 'mixed T02 title');
|
|
759
|
+
assertEq(p.tasks[1].estimate, '15m', 'mixed T02 estimate');
|
|
760
|
+
assertEq(p.tasks[1].done, false, 'mixed T02 not done (heading style)');
|
|
761
|
+
assertEq(p.tasks[2].id, 'T03', 'mixed T03 id');
|
|
762
|
+
assertEq(p.tasks[2].done, true, 'mixed T03 done');
|
|
763
|
+
}
|
|
764
|
+
|
|
655
765
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
656
766
|
// parseSummary tests
|
|
657
767
|
// ═══════════════════════════════════════════════════════════════════════════
|