gsd-pi 2.78.1-dev.e9d88a536 → 2.78.1-dev.eccf86e27
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 +5 -7
- package/dist/help-text.js +1 -1
- package/dist/resource-loader.js +6 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +41 -5
- package/dist/resources/extensions/gsd/auto/loop.js +235 -36
- package/dist/resources/extensions/gsd/auto/phases.js +14 -7
- package/dist/resources/extensions/gsd/auto/session.js +36 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +49 -4
- package/dist/resources/extensions/gsd/auto-post-unit.js +26 -12
- package/dist/resources/extensions/gsd/auto-worktree.js +185 -201
- package/dist/resources/extensions/gsd/auto.js +139 -49
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +26 -20
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
- package/dist/resources/extensions/gsd/crash-recovery.js +160 -47
- package/dist/resources/extensions/gsd/db/auto-workers.js +227 -0
- package/dist/resources/extensions/gsd/db/command-queue.js +105 -0
- package/dist/resources/extensions/gsd/db/milestone-leases.js +210 -0
- package/dist/resources/extensions/gsd/db/runtime-kv.js +91 -0
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +322 -0
- package/dist/resources/extensions/gsd/db-writer.js +96 -16
- package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
- package/dist/resources/extensions/gsd/docs/COORDINATION.md +42 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +4 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +22 -6
- package/dist/resources/extensions/gsd/doctor.js +12 -2
- package/dist/resources/extensions/gsd/gsd-db.js +355 -3
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +116 -26
- package/dist/resources/extensions/gsd/interrupted-session.js +18 -15
- package/dist/resources/extensions/gsd/metrics.js +287 -1
- package/dist/resources/extensions/gsd/paths.js +79 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/dist/resources/extensions/gsd/state.js +21 -6
- package/dist/resources/extensions/gsd/templates/project.md +10 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/workspace.js +59 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +79 -2
- package/dist/resources/extensions/gsd/write-intercept.js +3 -3
- 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 +14 -14
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/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.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/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 +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +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/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/README.md +2 -11
- package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +28 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +94 -4
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +226 -0
- package/packages/mcp-server/src/remote-questions.test.ts +103 -0
- package/packages/mcp-server/src/remote-questions.ts +35 -0
- package/packages/mcp-server/src/server.ts +129 -6
- package/packages/mcp-server/src/workflow-tools.ts +1 -1
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +37 -5
- package/src/resources/extensions/gsd/auto/loop.ts +263 -41
- package/src/resources/extensions/gsd/auto/phases.ts +15 -7
- package/src/resources/extensions/gsd/auto/session.ts +40 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +63 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +27 -12
- package/src/resources/extensions/gsd/auto-worktree.ts +218 -225
- package/src/resources/extensions/gsd/auto.ts +166 -43
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +26 -21
- package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
- package/src/resources/extensions/gsd/crash-recovery.ts +177 -43
- package/src/resources/extensions/gsd/db/auto-workers.ts +273 -0
- package/src/resources/extensions/gsd/db/command-queue.ts +149 -0
- package/src/resources/extensions/gsd/db/milestone-leases.ts +274 -0
- package/src/resources/extensions/gsd/db/runtime-kv.ts +127 -0
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +446 -0
- package/src/resources/extensions/gsd/db-writer.ts +113 -17
- package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
- package/src/resources/extensions/gsd/docs/COORDINATION.md +42 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +4 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +24 -6
- package/src/resources/extensions/gsd/doctor.ts +10 -2
- package/src/resources/extensions/gsd/gsd-db.ts +354 -3
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +152 -26
- package/src/resources/extensions/gsd/interrupted-session.ts +19 -12
- package/src/resources/extensions/gsd/metrics.ts +321 -1
- package/src/resources/extensions/gsd/paths.ts +67 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/src/resources/extensions/gsd/state.ts +44 -6
- package/src/resources/extensions/gsd/templates/project.md +10 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
- package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/auto-loop-symlink-worktree.test.ts +190 -0
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
- package/src/resources/extensions/gsd/tests/auto-workers.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/command-queue.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +169 -59
- package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
- package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/detect-stuck-respects-retry.test.ts +173 -0
- package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
- package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
- package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -12
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +24 -10
- package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +35 -23
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +369 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +72 -25
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +72 -25
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
- package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
- package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
- package/src/resources/extensions/gsd/tests/parallel-milestone-isolation.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +3 -17
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +138 -16
- package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/runtime-kv.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +133 -28
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workspace.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +94 -71
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/workspace.ts +95 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +78 -2
- package/src/resources/extensions/gsd/write-intercept.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +0 -213
- package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +0 -87
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +0 -159
- /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_ssgManifest.js +0 -0
|
@@ -8,6 +8,13 @@ Your goal is **not** to center the discussion on tech stack trivia, naming conve
|
|
|
8
8
|
|
|
9
9
|
## Interview Protocol
|
|
10
10
|
|
|
11
|
+
### Read project shape
|
|
12
|
+
|
|
13
|
+
Before your first question round, read `.gsd/PROJECT.md` and look for `## Project Shape` → `**Complexity:**`. The verdict is either **`simple`** or **`complex`** (default to `complex` if the section is missing or unclear).
|
|
14
|
+
|
|
15
|
+
- **`simple`** — favor 1–2 plain-text rounds, write the slice context fast. Skip parallel-research investigation.
|
|
16
|
+
- **`complex`** — full investigation with structured 3–4-option questions.
|
|
17
|
+
|
|
11
18
|
### Before your first question round
|
|
12
19
|
|
|
13
20
|
Do a lightweight targeted investigation so your questions are grounded in reality:
|
|
@@ -24,7 +31,7 @@ Do **not** go deep — just enough that your questions reflect what's actually t
|
|
|
24
31
|
|
|
25
32
|
**Never fabricate or simulate user input.** Never generate fake transcript markers like `[User]`, `[Human]`, or `User:`. Ask one question round, then wait for the user's actual response before continuing.
|
|
26
33
|
|
|
27
|
-
**If `{{structuredQuestionsAvailable}}` is `true`:** Ask **1–3 questions per round** using `ask_user_questions`. **Call `ask_user_questions` exactly once per turn — never make multiple calls with the same or overlapping questions. Wait for the user's response before asking the next round.**
|
|
34
|
+
**If `{{structuredQuestionsAvailable}}` is `true`:** Ask **1–3 questions per round** using `ask_user_questions`. In **`complex`** mode, each multi-choice question MUST present **3 or 4 concrete, researched options** plus a final **"Other — let me discuss"** option; options must be grounded in the investigation above (codebase signals, library docs, prior `.gsd/` artifacts), not generic placeholders. In **`simple`** mode, 2 options is fine. Binary wrap-up gates are exempt from the 3-or-4 rule. **Call `ask_user_questions` exactly once per turn — never make multiple calls with the same or overlapping questions. Wait for the user's response before asking the next round.**
|
|
28
35
|
**If `{{structuredQuestionsAvailable}}` is `false`:** Ask **1–3 questions per round** in plain text. Number them and wait for the user's response before asking the next round.
|
|
29
36
|
Keep each question focused on one of:
|
|
30
37
|
- **UX and user-facing behaviour** — what does the user see, click, trigger, or experience?
|
|
@@ -208,10 +208,17 @@ export async function getActiveMilestoneId(basePath) {
|
|
|
208
208
|
* Legacy filesystem parsing is available only through an explicit opt-in for
|
|
209
209
|
* tests/recovery flows; runtime must not silently infer state from markdown.
|
|
210
210
|
*/
|
|
211
|
-
export async function deriveState(basePath) {
|
|
212
|
-
//
|
|
211
|
+
export async function deriveState(basePath, opts) {
|
|
212
|
+
// Use the canonical project root (when provided) as the cache key so that
|
|
213
|
+
// two calls with different basePath strings (e.g. worktree path vs project
|
|
214
|
+
// root) but the same canonical .gsd/ share a single cache entry. The same
|
|
215
|
+
// key is used for both the lookup AND the write below — keying lookup on
|
|
216
|
+
// canonical-root while writing on basePath would silently return stale
|
|
217
|
+
// results across path-form alternation.
|
|
218
|
+
const cacheKey = opts?.projectRootForReads ?? basePath;
|
|
219
|
+
// Return cached result if within the TTL window for the same cacheKey
|
|
213
220
|
if (_stateCache &&
|
|
214
|
-
_stateCache.basePath ===
|
|
221
|
+
_stateCache.basePath === cacheKey &&
|
|
215
222
|
Date.now() - _stateCache.timestamp < CACHE_TTL_MS) {
|
|
216
223
|
return _stateCache.result;
|
|
217
224
|
}
|
|
@@ -230,7 +237,7 @@ export async function deriveState(basePath) {
|
|
|
230
237
|
if (wasDbOpenAttempted()) {
|
|
231
238
|
logWarning("state", "DB unavailable — using explicit legacy filesystem state derivation");
|
|
232
239
|
}
|
|
233
|
-
result = await _deriveStateImpl(basePath);
|
|
240
|
+
result = await _deriveStateImpl(basePath, opts);
|
|
234
241
|
_telemetry.markdownDeriveCount++;
|
|
235
242
|
}
|
|
236
243
|
else {
|
|
@@ -252,7 +259,7 @@ export async function deriveState(basePath) {
|
|
|
252
259
|
}
|
|
253
260
|
stopTimer({ phase: result.phase, milestone: result.activeMilestone?.id });
|
|
254
261
|
debugCount("deriveStateCalls");
|
|
255
|
-
_stateCache = { basePath, result, timestamp: Date.now() };
|
|
262
|
+
_stateCache = { basePath: cacheKey, result, timestamp: Date.now() };
|
|
256
263
|
return result;
|
|
257
264
|
}
|
|
258
265
|
/**
|
|
@@ -689,7 +696,15 @@ export async function deriveStateFromDb(basePath) {
|
|
|
689
696
|
// LEGACY: Filesystem-based state derivation for unmigrated projects.
|
|
690
697
|
// DB-backed projects use deriveStateFromDb() above. Target: extract to
|
|
691
698
|
// state-legacy.ts when all projects are DB-backed.
|
|
692
|
-
export async function _deriveStateImpl(basePath) {
|
|
699
|
+
export async function _deriveStateImpl(basePath, opts) {
|
|
700
|
+
// When the caller supplies a canonical project root for reads (e.g.
|
|
701
|
+
// s.canonicalProjectRoot from auto-mode), route all artifact reads through
|
|
702
|
+
// it. This prevents the worktree-local empty `.gsd/` from being consulted
|
|
703
|
+
// when the canonical state lives at the project root (or via a `.gsd`
|
|
704
|
+
// symlink into the external state dir).
|
|
705
|
+
if (opts?.projectRootForReads) {
|
|
706
|
+
basePath = opts.projectRootForReads;
|
|
707
|
+
}
|
|
693
708
|
const diskIds = findMilestoneIds(basePath);
|
|
694
709
|
const customOrder = loadQueueOrder(basePath);
|
|
695
710
|
const milestoneIds = sortByQueueOrder(diskIds, customOrder);
|
|
@@ -11,6 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
{{theOneThingThatMustWorkEvenIfEverythingElseIsCut}}
|
|
13
13
|
|
|
14
|
+
## Project Shape
|
|
15
|
+
|
|
16
|
+
<!-- Drives questioning depth in downstream stages. `simple` → short plain-text
|
|
17
|
+
rounds, fast PROJECT/CONTEXT/REQUIREMENTS writes. `complex` → researched
|
|
18
|
+
3–4-option questions with an "Other — let me discuss" hatch.
|
|
19
|
+
Default to `complex` when uncertain. -->
|
|
20
|
+
|
|
21
|
+
- **Complexity:** {{simple | complex}}
|
|
22
|
+
- **Why:** {{one-line rationale citing the signals that decided it}}
|
|
23
|
+
|
|
14
24
|
## Current State
|
|
15
25
|
|
|
16
26
|
{{whatHasBeenBuiltSoFar — what works, what exists, what's deployed}}
|
|
@@ -292,9 +292,9 @@ export function getRequiredWorkflowToolsForAutoUnit(unitType) {
|
|
|
292
292
|
case "execute-task":
|
|
293
293
|
case "execute-task-simple":
|
|
294
294
|
case "reactive-execute":
|
|
295
|
-
return ["
|
|
295
|
+
return ["gsd_task_complete"];
|
|
296
296
|
case "complete-slice":
|
|
297
|
-
return ["
|
|
297
|
+
return ["gsd_slice_complete"];
|
|
298
298
|
case "replan-slice":
|
|
299
299
|
return ["gsd_replan_slice"];
|
|
300
300
|
case "reassess-roadmap":
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// GSD-2 + Workspace handle: single source of truth for path resolution per milestone
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { resolveGsdPathContract, normalizeRealPath } from "./paths.js";
|
|
4
|
+
import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
|
|
5
|
+
function tryRealpath(p) {
|
|
6
|
+
return normalizeRealPath(p);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create an immutable GsdWorkspace handle from a raw base path.
|
|
10
|
+
* Resolves both the project root and (when applicable) the worktree root,
|
|
11
|
+
* normalizes them via realpath, and freezes the result.
|
|
12
|
+
*/
|
|
13
|
+
export function createWorkspace(rawBasePath) {
|
|
14
|
+
const resolvedBase = resolve(rawBasePath);
|
|
15
|
+
const isWorktree = isGsdWorktreePath(resolvedBase);
|
|
16
|
+
const projectRootRaw = resolveWorktreeProjectRoot(resolvedBase);
|
|
17
|
+
const projectRoot = tryRealpath(resolve(projectRootRaw));
|
|
18
|
+
const worktreeRoot = isWorktree ? tryRealpath(resolvedBase) : null;
|
|
19
|
+
// Derive a canonical base from the already-realpath-normalized paths so that
|
|
20
|
+
// resolveGsdPathContract always receives a canonical path. Using the raw
|
|
21
|
+
// resolvedBase here can produce a non-canonical projectGsd when the input
|
|
22
|
+
// path contains symlinks, causing contract.projectGsd to diverge from the
|
|
23
|
+
// realpath-normalized projectRoot / identityKey.
|
|
24
|
+
const canonicalBase = isWorktree ? (worktreeRoot ?? resolvedBase) : projectRoot;
|
|
25
|
+
const contract = Object.freeze(resolveGsdPathContract(canonicalBase));
|
|
26
|
+
const identityKey = tryRealpath(projectRoot);
|
|
27
|
+
const mode = isWorktree ? "worktree" : "project";
|
|
28
|
+
const workspace = Object.freeze({
|
|
29
|
+
projectRoot,
|
|
30
|
+
worktreeRoot,
|
|
31
|
+
mode,
|
|
32
|
+
contract,
|
|
33
|
+
identityKey,
|
|
34
|
+
lockRoot: projectRoot,
|
|
35
|
+
});
|
|
36
|
+
return workspace;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Bind a milestoneId to a workspace, producing an immutable MilestoneScope
|
|
40
|
+
* with path-returning closures that resolve via the authoritative projectGsd.
|
|
41
|
+
*
|
|
42
|
+
* All milestone-content paths route to contract.projectGsd (canonical),
|
|
43
|
+
* since that is the authoritative source of truth regardless of worktree mode.
|
|
44
|
+
*/
|
|
45
|
+
export function scopeMilestone(workspace, milestoneId) {
|
|
46
|
+
const { contract } = workspace;
|
|
47
|
+
const gsd = contract.projectGsd;
|
|
48
|
+
const scope = Object.freeze({
|
|
49
|
+
workspace,
|
|
50
|
+
milestoneId,
|
|
51
|
+
contextFile: () => join(gsd, "milestones", milestoneId, `${milestoneId}-CONTEXT.md`),
|
|
52
|
+
roadmapFile: () => join(gsd, "milestones", milestoneId, `${milestoneId}-ROADMAP.md`),
|
|
53
|
+
stateFile: () => join(gsd, "STATE.md"),
|
|
54
|
+
dbPath: () => contract.projectDb,
|
|
55
|
+
milestoneDir: () => join(gsd, "milestones", milestoneId),
|
|
56
|
+
metaJson: () => join(gsd, `${milestoneId}-META.json`),
|
|
57
|
+
});
|
|
58
|
+
return scope;
|
|
59
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD-2 — WorktreeResolver: encapsulates worktree path state and merge/exit lifecycle.
|
|
1
2
|
/**
|
|
2
3
|
* WorktreeResolver — encapsulates worktree path state and merge/exit lifecycle.
|
|
3
4
|
*
|
|
@@ -20,7 +21,20 @@ import { emitJournalEvent } from "./journal.js";
|
|
|
20
21
|
import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js";
|
|
21
22
|
import { getCollapseCadence, getMilestoneResquash, resquashMilestoneOnMain } from "./slice-cadence.js";
|
|
22
23
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
23
|
-
import { resolveWorktreeProjectRoot } from "./worktree-root.js";
|
|
24
|
+
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
25
|
+
import { claimMilestoneLease, releaseMilestoneLease } from "./db/milestone-leases.js";
|
|
26
|
+
// ─── Path Comparison Helper ────────────────────────────────────────────────
|
|
27
|
+
/**
|
|
28
|
+
* Compare two paths for physical identity, tolerating trailing slashes,
|
|
29
|
+
* symlink differences, and case variations on case-insensitive volumes.
|
|
30
|
+
*
|
|
31
|
+
* Used in place of string `===` / `!==` wherever one operand may be
|
|
32
|
+
* realpath-normalised (e.g. from the workspace registry) and the other
|
|
33
|
+
* may not be (e.g. a raw caller-supplied basePath).
|
|
34
|
+
*/
|
|
35
|
+
function isSamePath(a, b) {
|
|
36
|
+
return normalizeWorktreePathForCompare(a) === normalizeWorktreePathForCompare(b);
|
|
37
|
+
}
|
|
24
38
|
// ─── Path Helpers ──────────────────────────────────────────────────────────
|
|
25
39
|
/**
|
|
26
40
|
* Resolve the project root from session path state.
|
|
@@ -98,6 +112,69 @@ export class WorktreeResolver {
|
|
|
98
112
|
});
|
|
99
113
|
return;
|
|
100
114
|
}
|
|
115
|
+
// Phase B: claim a milestone lease before any worktree mutation. Two
|
|
116
|
+
// workers cannot enter the same milestone concurrently. Best-effort:
|
|
117
|
+
// skip if no worker registered (single-worker fallback) or DB
|
|
118
|
+
// unavailable; reuse existing lease if we already hold it on this
|
|
119
|
+
// milestone (re-entry within the same session).
|
|
120
|
+
if (this.s.workerId) {
|
|
121
|
+
if (this.s.currentMilestoneId === milestoneId && this.s.milestoneLeaseToken !== null) {
|
|
122
|
+
// Already held — no-op, the heartbeat in loop.ts refreshes TTL.
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// If we held a different milestone, release it first so other
|
|
126
|
+
// workers don't have to wait for TTL.
|
|
127
|
+
if (this.s.currentMilestoneId && this.s.currentMilestoneId !== milestoneId && this.s.milestoneLeaseToken !== null) {
|
|
128
|
+
try {
|
|
129
|
+
releaseMilestoneLease(this.s.workerId, this.s.currentMilestoneId, this.s.milestoneLeaseToken);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
debugLog("WorktreeResolver", {
|
|
133
|
+
action: "enterMilestone",
|
|
134
|
+
milestoneId,
|
|
135
|
+
releasePriorLeaseError: err instanceof Error ? err.message : String(err),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
this.s.milestoneLeaseToken = null;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const claim = claimMilestoneLease(this.s.workerId, milestoneId);
|
|
142
|
+
if (claim.ok) {
|
|
143
|
+
this.s.milestoneLeaseToken = claim.token;
|
|
144
|
+
debugLog("WorktreeResolver", {
|
|
145
|
+
action: "enterMilestone",
|
|
146
|
+
milestoneId,
|
|
147
|
+
leaseAcquired: true,
|
|
148
|
+
fencingToken: claim.token,
|
|
149
|
+
expiresAt: claim.expiresAt,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Lease held by another worker — fail loud so the user can
|
|
154
|
+
// see the conflict instead of silently double-running.
|
|
155
|
+
const msg = `Milestone ${milestoneId} is held by worker ${claim.byWorker} until ${claim.expiresAt}.`;
|
|
156
|
+
debugLog("WorktreeResolver", {
|
|
157
|
+
action: "enterMilestone",
|
|
158
|
+
milestoneId,
|
|
159
|
+
leaseHeldByOther: claim.byWorker,
|
|
160
|
+
expiresAt: claim.expiresAt,
|
|
161
|
+
});
|
|
162
|
+
ctx.notify(`${msg} Another auto-mode worker is active. Stop it before entering ${milestoneId}.`, "error");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
// DB unavailable or other error — log and fall through to the
|
|
168
|
+
// pre-Phase-B single-worker behavior so a fresh project before
|
|
169
|
+
// DB init still works.
|
|
170
|
+
debugLog("WorktreeResolver", {
|
|
171
|
+
action: "enterMilestone",
|
|
172
|
+
milestoneId,
|
|
173
|
+
leaseError: err instanceof Error ? err.message : String(err),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
101
178
|
// Resolve the project root for worktree operations via shared helper.
|
|
102
179
|
// Handles the case where originalBasePath is falsy and basePath is itself
|
|
103
180
|
// a worktree path — prevents double-nested worktree paths (#3729).
|
|
@@ -454,7 +531,7 @@ export class WorktreeResolver {
|
|
|
454
531
|
// roadmap triggers bare teardown which deletes the branch and orphans all
|
|
455
532
|
// milestone commits (#1573).
|
|
456
533
|
let roadmapPath = this.deps.resolveMilestoneFile(originalBase, milestoneId, "ROADMAP");
|
|
457
|
-
if (!roadmapPath && this.s.basePath
|
|
534
|
+
if (!roadmapPath && !isSamePath(this.s.basePath, originalBase)) {
|
|
458
535
|
roadmapPath = this.deps.resolveMilestoneFile(this.s.basePath, milestoneId, "ROADMAP");
|
|
459
536
|
if (roadmapPath) {
|
|
460
537
|
debugLog("WorktreeResolver", {
|
|
@@ -85,9 +85,9 @@ function matchesBlockedPattern(path) {
|
|
|
85
85
|
* Directs the agent to use engine tool calls instead.
|
|
86
86
|
*/
|
|
87
87
|
export const BLOCKED_WRITE_ERROR = `Direct writes to .gsd/STATE.md and .gsd/gsd.db are blocked. Use engine tool calls instead:
|
|
88
|
-
- To complete a task: call
|
|
89
|
-
- To complete a slice: call
|
|
90
|
-
- To save a decision: call
|
|
88
|
+
- To complete a task: call gsd_task_complete(milestone_id, slice_id, task_id, summary)
|
|
89
|
+
- To complete a slice: call gsd_slice_complete(milestone_id, slice_id, summary, uat_result)
|
|
90
|
+
- To save a decision: call gsd_decision_save(scope, decision, choice, rationale)
|
|
91
91
|
- To start a task: call gsd_start_task(milestone_id, slice_id, task_id)
|
|
92
92
|
- To record verification: call gsd_record_verification(milestone_id, slice_id, task_id, evidence)
|
|
93
93
|
- To report a blocker: call gsd_report_blocker(milestone_id, slice_id, task_id, description)`;
|