gsd-pi 2.82.0-dev.3a3c6509d → 2.82.0-dev.57fd453e4

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.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto/loop.js +14 -1
  4. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  5. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +3 -0
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +12 -5
  7. package/dist/resources/extensions/gsd/auto.js +14 -7
  8. package/dist/resources/extensions/gsd/markdown-renderer.js +10 -8
  9. package/dist/resources/extensions/gsd/paths.js +4 -0
  10. package/dist/resources/extensions/gsd/templates/plan.md +1 -0
  11. package/dist/resources/extensions/gsd/templates/task-plan.md +6 -0
  12. package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -5
  13. package/dist/resources/extensions/ttsr/ttsr-manager.js +3 -1
  14. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  15. package/dist/web/standalone/.next/BUILD_ID +1 -1
  16. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  17. package/dist/web/standalone/.next/build-manifest.json +3 -3
  18. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  19. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  20. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/index.html +1 -1
  37. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  44. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  47. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  48. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  49. package/dist/web/standalone/.next/static/chunks/8359.65b24fac92188a6b.js +10 -0
  50. package/dist/web/standalone/.next/static/chunks/9441.ff70bb53f6835771.js +1 -0
  51. package/dist/web/standalone/.next/static/chunks/{webpack-9a4db269f9ed63ad.js → webpack-855d616060cb6e59.js} +1 -1
  52. package/package.json +1 -1
  53. package/src/resources/extensions/gsd/auto/loop.ts +14 -1
  54. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  55. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +5 -1
  56. package/src/resources/extensions/gsd/auto-post-unit.ts +13 -5
  57. package/src/resources/extensions/gsd/auto.ts +13 -7
  58. package/src/resources/extensions/gsd/markdown-renderer.ts +10 -8
  59. package/src/resources/extensions/gsd/paths.ts +5 -0
  60. package/src/resources/extensions/gsd/templates/plan.md +1 -0
  61. package/src/resources/extensions/gsd/templates/task-plan.md +6 -0
  62. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +110 -0
  63. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +6 -5
  64. package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +5 -2
  65. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +26 -1
  66. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +84 -0
  67. package/src/resources/extensions/gsd/tests/quality-gates.test.ts +6 -0
  68. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  69. package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -4
  70. package/src/resources/extensions/ttsr/ttsr-manager.ts +5 -1
  71. package/dist/web/standalone/.next/static/chunks/8359.7eb3bb8f8ecf4c01.js +0 -10
  72. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +0 -1
  73. /package/dist/web/standalone/.next/static/{O6femb9LLl3nlgsDaYwS- → ky6ieNHfZXB_oHPklwTJb}/_buildManifest.js +0 -0
  74. /package/dist/web/standalone/.next/static/{O6femb9LLl3nlgsDaYwS- → ky6ieNHfZXB_oHPklwTJb}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -350,7 +350,7 @@ This is what makes GSD different. Run it, walk away, come back to built software
350
350
 
351
351
  Auto mode is a state machine driven by the GSD database at the project root. It derives the next unit of work from authoritative SQLite state, creates a fresh agent session, injects a focused prompt with all relevant context pre-inlined, and lets the LLM execute. When the LLM finishes, auto mode persists the result to the database, refreshes markdown projections such as `STATE.md`, and dispatches the next unit.
352
352
 
353
- The database is authoritative for milestones, slices, tasks, requirements, summaries, and completion status. Durable decisions and project knowledge are stored in the `memories` table: decisions are `architecture` memories, and KNOWLEDGE patterns/lessons are `pattern`/`gotcha` memories. Markdown under `.gsd/` is a rendered projection for review, prompts, and git-friendly history; it is not a runtime fallback unless you explicitly run a recovery/import command. In worktree mode, project-root DB state remains authoritative and worktree markdown projections are not synced back as state.
353
+ The database is authoritative for milestones, slices, tasks, requirements, summaries, and completion status. Durable decisions and project knowledge are stored in the `memories` table: decisions are `architecture` memories, and KNOWLEDGE patterns/lessons are `pattern`/`gotcha` memories. Markdown under `.gsd/` is a rendered projection for review, prompts, and git-friendly history; it is not a runtime fallback unless you explicitly run a recovery/import command. In worktree mode, artifact/projection writes are rendered under the active worktree-local `.gsd/`, while the project-root DB remains authoritative runtime state.
354
354
 
355
355
  `KNOWLEDGE.md` is hybrid: rules remain file-canonical, while patterns and lessons are stored in the `memories` table and rendered back into `KNOWLEDGE.md` on the next session-start projection. Existing pattern and lesson rows are backfilled into memories before projection, so newly captured patterns and lessons may appear in memory-backed prompt context before the file view refreshes.
356
356
 
@@ -1 +1 @@
1
- b9c1b29c0681f84f
1
+ 8862dc8dc822437d
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Main auto-mode execution loop.
1
3
  /**
2
4
  * auto/loop.ts — Main auto-mode execution loop.
3
5
  *
@@ -789,11 +791,18 @@ export async function autoLoop(ctx, pi, s, deps, options) {
789
791
  unitId: iterData.unitId,
790
792
  });
791
793
  const finalizeReason = finalizeResult.action === "break" ? finalizeResult.reason : undefined;
794
+ const finalizeStatus = finalizeReason === "step-wizard"
795
+ ? "completed"
796
+ : finalizeResult.action === "next"
797
+ ? "completed"
798
+ : finalizeResult.action === "continue"
799
+ ? "retry"
800
+ : "stopped";
792
801
  journalReporter.emit("post-unit-finalize-end", {
793
802
  iteration,
794
803
  unitType: iterData.unitType,
795
804
  unitId: iterData.unitId,
796
- status: finalizeResult.action === "next" ? "completed" : finalizeResult.action === "continue" ? "retry" : "stopped",
805
+ status: finalizeStatus,
797
806
  action: finalizeResult.action,
798
807
  ...(finalizeReason ? { reason: finalizeReason } : {}),
799
808
  });
@@ -837,6 +846,10 @@ export async function autoLoop(ctx, pi, s, deps, options) {
837
846
  }) || dispatchSettled;
838
847
  completeIteration();
839
848
  finishTurn("completed");
849
+ if (finalizeDecision.action === "complete-and-break") {
850
+ s.preserveStepSurfaceAfterLoopExit = true;
851
+ break;
852
+ }
840
853
  }
841
854
  catch (loopErr) {
842
855
  // ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Mutable auto-mode session state container.
1
3
  /**
2
4
  * AutoSession — encapsulates all mutable auto-mode state into a single instance.
3
5
  *
@@ -26,6 +28,7 @@ export class AutoSession {
26
28
  active = false;
27
29
  paused = false;
28
30
  completionStopInProgress = false;
31
+ preserveStepSurfaceAfterLoopExit = false;
29
32
  stepMode = false;
30
33
  verbose = false;
31
34
  activeEngineId = null;
@@ -210,6 +213,7 @@ export class AutoSession {
210
213
  this.active = false;
211
214
  this.paused = false;
212
215
  this.completionStopInProgress = false;
216
+ this.preserveStepSurfaceAfterLoopExit = false;
213
217
  this.stepMode = false;
214
218
  this.verbose = false;
215
219
  this.activeEngineId = null;
@@ -66,6 +66,9 @@ export function decideEngineDispatch(input) {
66
66
  export function decideFinalizeResult(input) {
67
67
  if (input.action === "break") {
68
68
  const reason = input.reason ?? "unknown";
69
+ if (reason === "step-wizard") {
70
+ return { action: "complete-and-break" };
71
+ }
69
72
  return {
70
73
  action: "stop",
71
74
  failureClass: reason === "git-closeout-failure" ? "git" : "closeout",
@@ -28,7 +28,7 @@ import { regenerateIfMissing } from "./workflow-projections.js";
28
28
  import { WorktreeStateProjection } from "./worktree-state-projection.js";
29
29
  import { createWorkspace, scopeMilestone } from "./workspace.js";
30
30
  import { normalizeWorktreePathForCompare } from "./worktree-root.js";
31
- import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
31
+ import { isDbAvailable, getDbPath, refreshOpenDatabaseFromDisk, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
32
32
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
33
33
  import { consumeSignal } from "./session-status-io.js";
34
34
  import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
@@ -294,14 +294,14 @@ export function detectRogueFileWrites(unitType, unitId, basePath) {
294
294
  * looping indefinitely (#2007).
295
295
  */
296
296
  export const MAX_ARTIFACT_VERIFICATION_RETRIES = 3;
297
- export const STEP_COMPLETE_FALLBACK_MESSAGE = "Step complete. Run /clear, then /gsd to continue (or /gsd auto to run continuously).";
297
+ export const STEP_COMPLETE_FALLBACK_MESSAGE = "Step complete. Run /clear if you want a clean view, then /gsd next to continue one step (or /gsd auto to run continuously).";
298
298
  export function buildStepCompleteMessage(nextState) {
299
299
  if (nextState.phase === "complete") {
300
300
  return "Step complete — milestone finished. Run /gsd status to review, or start the next milestone.";
301
301
  }
302
302
  const next = describeNextUnit(nextState);
303
303
  return `Step complete. Next: ${next.label}\n`
304
- + `Run /clear, then /gsd to continue (or /gsd auto to run continuously).`;
304
+ + `Run /clear if you want a clean view, then /gsd next to continue one step (or /gsd auto to run continuously).`;
305
305
  }
306
306
  /**
307
307
  * Decide whether step mode should stop at the step wizard after a unit finishes.
@@ -553,6 +553,13 @@ export async function postUnitPreVerification(pctx, opts) {
553
553
  if (!opts?.skipSettleDelay) {
554
554
  await new Promise(r => setTimeout(r, 100));
555
555
  }
556
+ const dbPath = getDbPath();
557
+ if (isDbAvailable() && dbPath && dbPath !== ":memory:") {
558
+ const refreshed = refreshOpenDatabaseFromDisk();
559
+ if (!refreshed) {
560
+ logWarning("db", "post-unit database refresh failed; derived state may be stale");
561
+ }
562
+ }
556
563
  // Turn-level git action (commit | snapshot | status-only)
557
564
  if (s.currentUnit) {
558
565
  const unit = s.currentUnit;
@@ -1458,8 +1465,8 @@ export async function postUnitPostVerification(pctx) {
1458
1465
  }
1459
1466
  }
1460
1467
  // Step mode → show wizard instead of dispatch.
1461
- // Without this notify(), /gsd in step mode finishes a unit and silently
1462
- // exits the loop, leaving the user with no hint to /clear and /gsd again.
1468
+ // Without this notify(), /gsd next finishes a unit and silently exits the
1469
+ // loop, leaving the user with no next-step command.
1463
1470
  if (s.stepMode) {
1464
1471
  let phaseAfterUnit = null;
1465
1472
  try {
@@ -689,6 +689,8 @@ export async function rerootCommandSession(cmdCtx, workspaceRoot) {
689
689
  }
690
690
  }
691
691
  export async function cleanupAfterLoopExit(ctx) {
692
+ const preserveStepSurface = s.preserveStepSurfaceAfterLoopExit;
693
+ const preservePausedSurface = s.paused;
692
694
  s.currentUnit = null;
693
695
  s.active = false;
694
696
  deactivateGSD();
@@ -712,19 +714,24 @@ export async function cleanupAfterLoopExit(ctx) {
712
714
  // A transient provider-error pause intentionally leaves the paused badge
713
715
  // visible so the user still has a resumable auto-mode signal on screen.
714
716
  if (!s.paused) {
715
- ctx.ui.setStatus("gsd-auto", undefined);
716
- ctx.ui.setWidget("gsd-progress", undefined);
717
- if (s.completionStopInProgress) {
718
- s.completionStopInProgress = false;
717
+ if (preserveStepSurface) {
718
+ s.preserveStepSurfaceAfterLoopExit = false;
719
+ }
720
+ else {
721
+ ctx.ui.setStatus("gsd-auto", undefined);
722
+ ctx.ui.setWidget("gsd-progress", undefined);
723
+ if (s.completionStopInProgress) {
724
+ s.completionStopInProgress = false;
725
+ }
726
+ initHealthWidget(ctx);
719
727
  }
720
- initHealthWidget(ctx);
721
728
  }
722
729
  // ADR-016 phase 3 (#5693): the stop-path basePath restore + chdir routes
723
730
  // through `Lifecycle.restoreToProjectRoot()`, the sole owner of both
724
731
  // `s.basePath` mutation and the paired `process.chdir` for auto-loop
725
732
  // transitions. The verb assigns `s.basePath` before any throwable work, so
726
733
  // a thrown error still leaves basePath restored.
727
- if (s.originalBasePath) {
734
+ if (s.originalBasePath && !preserveStepSurface && !preservePausedSurface) {
728
735
  try {
729
736
  buildLifecycle().restoreToProjectRoot();
730
737
  }
@@ -732,7 +739,7 @@ export async function cleanupAfterLoopExit(ctx) {
732
739
  logWarning("engine", `restore project root failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
733
740
  }
734
741
  }
735
- if (s.originalBasePath && s.cmdCtx) {
742
+ if (s.originalBasePath && s.cmdCtx && !preserveStepSurface && !preservePausedSurface) {
736
743
  const result = await rerootCommandSession(s.cmdCtx, s.originalBasePath);
737
744
  if (result.status === "cancelled") {
738
745
  logWarning("engine", "post-loop session re-root was cancelled", { file: "auto.ts", basePath: s.originalBasePath });
@@ -14,7 +14,7 @@ import { isClosedStatus } from "./status-guards.js";
14
14
  import { join, relative } from "node:path";
15
15
  import { createRequire } from "node:module";
16
16
  import { getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getTask, getSlice, getArtifact, insertArtifact, getGateResults, } from "./gsd-db.js";
17
- import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, gsdRoot, buildTaskFileName, buildSliceFileName, } from "./paths.js";
17
+ import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, gsdProjectionRoot, gsdRoot, buildTaskFileName, buildSliceFileName, } from "./paths.js";
18
18
  import { saveFile, clearParseCache } from "./files.js";
19
19
  import { invalidateStateCache } from "./state.js";
20
20
  import { clearPathCache } from "./paths.js";
@@ -24,7 +24,11 @@ import { clearPathCache } from "./paths.js";
24
24
  * E.g. "/project/.gsd/milestones/M001/M001-ROADMAP.md" → "milestones/M001/M001-ROADMAP.md"
25
25
  */
26
26
  function toArtifactPath(absPath, basePath) {
27
- const root = gsdRoot(basePath);
27
+ const projectionRoot = gsdProjectionRoot(basePath);
28
+ const projectionRel = relative(projectionRoot, absPath);
29
+ const root = projectionRel && !projectionRel.startsWith("..") && !projectionRel.startsWith("/")
30
+ ? projectionRoot
31
+ : gsdRoot(basePath);
28
32
  const rel = relative(root, absPath);
29
33
  // Normalize to forward slashes for consistent DB keys
30
34
  return rel.replace(/\\/g, "/");
@@ -305,10 +309,9 @@ export async function renderPlanFromDb(basePath, milestoneId, sliceId) {
305
309
  if (tasks.length === 0) {
306
310
  throw new Error(`no tasks found for ${milestoneId}/${sliceId}`);
307
311
  }
308
- const slicePath = resolveSlicePath(basePath, milestoneId, sliceId)
309
- ?? join(gsdRoot(basePath), "milestones", milestoneId, "slices", sliceId);
310
- const absPath = resolveSliceFile(basePath, milestoneId, sliceId, "PLAN")
311
- ?? join(slicePath, `${sliceId}-PLAN.md`);
312
+ const slicePath = join(gsdProjectionRoot(basePath), "milestones", milestoneId, "slices", sliceId);
313
+ mkdirSync(slicePath, { recursive: true });
314
+ const absPath = join(slicePath, `${sliceId}-PLAN.md`);
312
315
  const artifactPath = toArtifactPath(absPath, basePath);
313
316
  const sliceGates = getGateResults(milestoneId, sliceId, "slice");
314
317
  const content = renderSlicePlanMarkdown(slice, tasks, sliceGates);
@@ -329,8 +332,7 @@ export async function renderTaskPlanFromDb(basePath, milestoneId, sliceId, taskI
329
332
  if (!task) {
330
333
  throw new Error(`task ${milestoneId}/${sliceId}/${taskId} not found`);
331
334
  }
332
- const tasksDir = resolveTasksDir(basePath, milestoneId, sliceId)
333
- ?? join(gsdRoot(basePath), "milestones", milestoneId, "slices", sliceId, "tasks");
335
+ const tasksDir = join(gsdProjectionRoot(basePath), "milestones", milestoneId, "slices", sliceId, "tasks");
334
336
  mkdirSync(tasksDir, { recursive: true });
335
337
  const absPath = join(tasksDir, buildTaskFileName(taskId, "PLAN"));
336
338
  const artifactPath = toArtifactPath(absPath, basePath);
@@ -320,6 +320,10 @@ export function resolveGsdPathContract(workRoot, originalProjectRoot) {
320
320
  isWorktree,
321
321
  };
322
322
  }
323
+ export function gsdProjectionRoot(basePath) {
324
+ const contract = resolveGsdPathContract(basePath);
325
+ return normalizeRealPath(contract.worktreeGsd ?? contract.projectGsd);
326
+ }
323
327
  /**
324
328
  * Invalidate the gsdRoot cache.
325
329
  * Use ONLY at session-reset boundaries: workspace switch, process exit, or
@@ -132,6 +132,7 @@
132
132
  Verify field rules:
133
133
  - MUST be a mechanically executable command: `npm test`, `grep -q "pattern" file`, `test -f path`
134
134
  - MUST NOT use shell pipes, redirects, semicolons, backticks, command substitution, or output trimming
135
+ - MUST NOT use inline `node -e` assertions for verification; put assertions in a real test file and run it with `node --test` or a package test script
135
136
  - For content/document tasks: verify file existence, section count, YAML validity, or word count
136
137
  NOT exact phrasing, specific formulas, or "zero TBD" aspirational criteria
137
138
  - If no command can verify the output, write: "Manual review — file exists and is non-empty"
@@ -57,6 +57,12 @@ skills_used:
57
57
  - {{howToVerifyThisTaskIsActuallyDone}}
58
58
  - {{commandToRun_OR_behaviorToCheck}}
59
59
 
60
+ ## Verify Rules
61
+
62
+ - Use a real executable check, not prose.
63
+ - If the check needs file-content assertions, write a `node:test` file and run it with `node --test` or a package test script.
64
+ - Do not use inline `node -e` assertions for verification.
65
+
60
66
  ## Observability Impact
61
67
 
62
68
  <!-- OMIT THIS SECTION ENTIRELY for simple tasks that don't touch runtime boundaries,
@@ -12,7 +12,7 @@ import { appendEvent } from "../workflow-events.js";
12
12
  import { logWarning } from "../workflow-logger.js";
13
13
  import { validatePlanningPathScope } from "../planning-path-scope.js";
14
14
  import { checkFilePathConsistency, checkTaskOrdering } from "../pre-execution-checks.js";
15
- import { buildTaskFileName, gsdRoot, resolveTasksDir } from "../paths.js";
15
+ import { buildTaskFileName, gsdProjectionRoot } from "../paths.js";
16
16
  function validateTasks(value) {
17
17
  if (!Array.isArray(value) || value.length === 0) {
18
18
  throw new Error("tasks must be a non-empty array");
@@ -241,14 +241,12 @@ export async function handlePlanSlice(rawParams, basePath) {
241
241
  return { error: guardError };
242
242
  }
243
243
  try {
244
- const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
244
+ const tasksDir = join(gsdProjectionRoot(basePath), "milestones", params.milestoneId, "slices", params.sliceId, "tasks");
245
245
  for (const taskId of omittedTaskIds) {
246
- if (!tasksDir)
247
- continue;
248
246
  const taskPlanPath = join(tasksDir, buildTaskFileName(taskId, "PLAN"));
249
247
  if (existsSync(taskPlanPath))
250
248
  rmSync(taskPlanPath, { force: true });
251
- const artifactPath = relative(gsdRoot(basePath), taskPlanPath).replace(/\\/g, "/");
249
+ const artifactPath = relative(gsdProjectionRoot(basePath), taskPlanPath).replace(/\\/g, "/");
252
250
  deleteArtifactByPath(artifactPath);
253
251
  }
254
252
  const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
@@ -43,6 +43,7 @@ const MAX_BUFFER_BYTES = 512 * 1024;
43
43
  * Prevents CPU spinning when deltas arrive faster than regex evaluation (#468).
44
44
  */
45
45
  const JS_FALLBACK_CHECK_INTERVAL_MS = 50;
46
+ const JS_FALLBACK_THROTTLE_MIN_BUFFER_BYTES = 4 * 1024;
46
47
  const DEFAULT_SCOPE = {
47
48
  allowText: true,
48
49
  allowThinking: false,
@@ -308,7 +309,8 @@ export class TtsrManager {
308
309
  // streams — regex on a growing buffer is O(rules × buffer_size) (#468).
309
310
  const now = Date.now();
310
311
  const lastCheck = this.#lastJsCheckAt.get(bufferKey) ?? 0;
311
- if (now - lastCheck < JS_FALLBACK_CHECK_INTERVAL_MS) {
312
+ if (nextBuffer.length >= JS_FALLBACK_THROTTLE_MIN_BUFFER_BYTES &&
313
+ now - lastCheck < JS_FALLBACK_CHECK_INTERVAL_MS) {
312
314
  stopTimer({ bufferSize: nextBuffer.length, throttled: true });
313
315
  return [];
314
316
  }