gsd-pi 2.39.0 → 2.40.0-dev.4a93031
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resource-loader.js +66 -2
- package/dist/resources/extensions/async-jobs/index.js +10 -0
- package/dist/resources/extensions/get-secrets-from-user.js +1 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
- package/dist/resources/extensions/gsd/auto-loop.js +761 -673
- package/dist/resources/extensions/gsd/auto-post-unit.js +10 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +3 -3
- package/dist/resources/extensions/gsd/auto-start.js +6 -1
- package/dist/resources/extensions/gsd/auto.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
- package/dist/resources/extensions/gsd/commands/context.js +84 -0
- package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
- package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
- package/dist/resources/extensions/gsd/commands/index.js +11 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +8 -1190
- package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
- package/dist/resources/extensions/gsd/doctor.js +32 -2
- package/dist/resources/extensions/gsd/export-html.js +46 -0
- package/dist/resources/extensions/gsd/files.js +1 -1
- package/dist/resources/extensions/gsd/health-widget.js +1 -1
- package/dist/resources/extensions/gsd/index.js +4 -1115
- package/dist/resources/extensions/gsd/progress-score.js +20 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
- package/dist/resources/extensions/gsd/visualizer-data.js +26 -1
- package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
- package/dist/welcome-screen.d.ts +3 -2
- package/dist/welcome-screen.js +66 -22
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +107 -24
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +2 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +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 +15 -457
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/skills.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/index.ts +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
- package/src/resources/extensions/gsd/auto-loop.ts +1075 -921
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +3 -3
- package/src/resources/extensions/gsd/auto-start.ts +6 -1
- package/src/resources/extensions/gsd/auto.ts +13 -10
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
- package/src/resources/extensions/gsd/commands/context.ts +101 -0
- package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
- package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
- package/src/resources/extensions/gsd/commands/index.ts +14 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +10 -1329
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
- package/src/resources/extensions/gsd/doctor.ts +47 -3
- package/src/resources/extensions/gsd/export-html.ts +51 -0
- package/src/resources/extensions/gsd/files.ts +1 -1
- package/src/resources/extensions/gsd/health-widget.ts +2 -1
- package/src/resources/extensions/gsd/index.ts +12 -1314
- package/src/resources/extensions/gsd/progress-score.ts +23 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
- package/src/resources/extensions/gsd/visualizer-data.ts +51 -1
- package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
- /package/dist/resources/extensions/{env-utils.js → gsd/env-utils.js} +0 -0
- /package/src/resources/extensions/{env-utils.ts → gsd/env-utils.ts} +0 -0
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
getHealthTrend,
|
|
15
15
|
getConsecutiveErrorUnits,
|
|
16
16
|
getHealthHistory,
|
|
17
|
+
getLatestHealthIssues,
|
|
18
|
+
getLatestHealthFixes,
|
|
17
19
|
type HealthSnapshot,
|
|
18
20
|
} from "./doctor-proactive.js";
|
|
19
21
|
|
|
@@ -77,6 +79,27 @@ export function computeProgressScore(): ProgressScore {
|
|
|
77
79
|
signals.push({ kind: "neutral", label: "No health data yet" });
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
// Surface actual doctor issue details when degraded
|
|
83
|
+
if (level !== "green") {
|
|
84
|
+
const latestIssues = getLatestHealthIssues();
|
|
85
|
+
// Show up to 5 most relevant issues (errors first, then warnings)
|
|
86
|
+
const sorted = [...latestIssues].sort((a, b) => {
|
|
87
|
+
const rank = { error: 0, warning: 1, info: 2 };
|
|
88
|
+
return rank[a.severity] - rank[b.severity];
|
|
89
|
+
});
|
|
90
|
+
for (const issue of sorted.slice(0, 5)) {
|
|
91
|
+
signals.push({
|
|
92
|
+
kind: issue.severity === "error" ? "negative" : "neutral",
|
|
93
|
+
label: issue.message,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const latestFixes = getLatestHealthFixes();
|
|
98
|
+
for (const fix of latestFixes.slice(0, 3)) {
|
|
99
|
+
signals.push({ kind: "positive", label: `Fixed: ${fix}` });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
80
103
|
const summary = level === "green"
|
|
81
104
|
? "Progressing well"
|
|
82
105
|
: level === "yellow"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
You are
|
|
1
|
+
You are debugging GSD itself. The user is donating their tokens to help find bugs in GSD's source code. Your job is to trace from symptom to root cause in the actual source and produce a filing-ready GitHub issue with specific file:line references and a concrete fix suggestion.
|
|
2
2
|
|
|
3
3
|
## User's Problem
|
|
4
4
|
|
|
@@ -10,62 +10,137 @@ You are investigating a GSD auto-mode failure. The user has described their prob
|
|
|
10
10
|
|
|
11
11
|
## GSD Source Location
|
|
12
12
|
|
|
13
|
-
GSD extension source code is at: {{gsdSourceDir}}
|
|
14
|
-
Key files for understanding failures:
|
|
15
|
-
- auto.ts — unit dispatch loop, stuck detection, timeout recovery
|
|
16
|
-
- session-forensics.ts — trace extraction from activity logs
|
|
17
|
-
- auto-recovery.ts — artifact verification, skip logic
|
|
18
|
-
- crash-recovery.ts — crash lock lifecycle
|
|
19
|
-
- doctor.ts — state integrity checks
|
|
13
|
+
GSD extension source code is at: `{{gsdSourceDir}}`
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
### Source Map by Domain
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
| Domain | Files |
|
|
18
|
+
|--------|-------|
|
|
19
|
+
| **Auto-mode engine** | `auto.ts` `auto-loop.ts` `auto-dispatch.ts` `auto-start.ts` `auto-supervisor.ts` `auto-timers.ts` `auto-timeout-recovery.ts` `auto-unit-closeout.ts` `auto-post-unit.ts` `auto-verification.ts` `auto-recovery.ts` `auto-worktree.ts` `auto-worktree-sync.ts` `auto-model-selection.ts` `auto-budget.ts` `dispatch-guard.ts` |
|
|
20
|
+
| **State & persistence** | `state.ts` `types.ts` `files.ts` `paths.ts` `json-persistence.ts` `atomic-write.ts` |
|
|
21
|
+
| **Forensics & recovery** | `forensics.ts` `session-forensics.ts` `crash-recovery.ts` `session-lock.ts` |
|
|
22
|
+
| **Metrics & telemetry** | `metrics.ts` `skill-telemetry.ts` `token-counter.ts` |
|
|
23
|
+
| **Health & diagnostics** | `doctor.ts` `doctor-types.ts` `doctor-checks.ts` `doctor-format.ts` `doctor-environment.ts` |
|
|
24
|
+
| **Prompts & context** | `prompt-loader.ts` `prompt-cache-optimizer.ts` `context-budget.ts` |
|
|
25
|
+
| **Git & worktrees** | `git-service.ts` `worktree.ts` `worktree-manager.ts` `git-self-heal.ts` |
|
|
26
|
+
| **Commands** | `commands.ts` `commands-inspect.ts` `commands-maintenance.ts` |
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
### Runtime Path Reference
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
```
|
|
31
|
+
.gsd/
|
|
32
|
+
├── PROJECT.md, DECISIONS.md, QUEUE.md, STATE.md, REQUIREMENTS.md, OVERRIDES.md, KNOWLEDGE.md, RUNTIME.md
|
|
33
|
+
├── auto.lock — crash lock (JSON: pid, unitType, unitId, sessionFile)
|
|
34
|
+
├── metrics.json — token/cost ledger (units array with cost, tokens, duration)
|
|
35
|
+
├── completed-units.json — array of "type/id" strings
|
|
36
|
+
├── doctor-history.jsonl — doctor check history
|
|
37
|
+
├── activity/ — session activity logs (JSONL per unit)
|
|
38
|
+
│ └── {seq}-{unitType}-{unitId}.jsonl
|
|
39
|
+
├── runtime/
|
|
40
|
+
│ ├── paused-session.json — serialized session when auto pauses
|
|
41
|
+
│ └── headless-context.md — headless resume context
|
|
42
|
+
├── debug/ — debug logs
|
|
43
|
+
├── forensics/ — saved forensic reports
|
|
44
|
+
├── milestones/{ID}/ — milestone artifacts
|
|
45
|
+
│ ├── {ID}-ROADMAP.md, {ID}-RESEARCH.md, {ID}-CONTEXT.md, {ID}-SUMMARY.md
|
|
46
|
+
│ └── slices/{SID}/ — slice artifacts
|
|
47
|
+
│ ├── {SID}-PLAN.md, {SID}-RESEARCH.md, {SID}-UAT-RESULT.md, {SID}-SUMMARY.md
|
|
48
|
+
│ └── tasks/{TID}-PLAN.md, {TID}-SUMMARY.md
|
|
49
|
+
└── worktrees/{milestoneId}/ — per-milestone worktree with replicated .gsd/
|
|
50
|
+
```
|
|
28
51
|
|
|
29
|
-
|
|
30
|
-
- What happened (the failure sequence)
|
|
31
|
-
- Why it happened (root cause in GSD's logic)
|
|
32
|
-
- What the user can do to recover (immediate fix)
|
|
52
|
+
### Activity Log Format
|
|
33
53
|
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
- **Filename**: `{3-digit-seq}-{unitType}-{unitId}.jsonl`
|
|
55
|
+
- Each line is a JSON object with `type: "message"` and a `message` field
|
|
56
|
+
- `message.role: "assistant"` — contains `content[]` array:
|
|
57
|
+
- `type: "text"` entries hold the agent's reasoning
|
|
58
|
+
- `type: "toolCall"` entries hold tool invocations (`name`, `id`, `arguments`)
|
|
59
|
+
- `message.role: "toolResult"` — contains `toolCallId`, `toolName`, `isError`, `content`
|
|
60
|
+
- `usage` field on assistant messages: `input`, `output`, `cacheRead`, `cacheWrite`, `totalTokens`, `cost`
|
|
61
|
+
- **To trace a failure**: find the last activity log, search for `isError: true` tool results, then read the agent's reasoning text preceding that error
|
|
36
62
|
|
|
37
|
-
|
|
38
|
-
- Repository: gsd-build/gsd-2
|
|
39
|
-
- Labels: bug, auto-generated
|
|
40
|
-
- Title: concise description of the failure
|
|
41
|
-
- Body format:
|
|
42
|
-
```
|
|
43
|
-
## Problem
|
|
44
|
-
[1-2 sentence summary]
|
|
63
|
+
### Crash Lock Format (`auto.lock`)
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
- GSD version: [from report]
|
|
48
|
-
- Model: [from report]
|
|
49
|
-
- Unit: [type/id that failed]
|
|
65
|
+
JSON with fields: `pid`, `startedAt`, `unitType`, `unitId`, `unitStartedAt`, `completedUnits`, `sessionFile`
|
|
50
66
|
|
|
51
|
-
|
|
52
|
-
[What was happening when it failed — phase, milestone, slice]
|
|
67
|
+
A stale lock (PID is dead) means the previous auto-mode session crashed mid-unit.
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
[Key anomalies detected, error traces, relevant tool call sequences]
|
|
69
|
+
### Metrics Ledger Format (`metrics.json`)
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
```
|
|
72
|
+
{ version: 1, projectStartedAt: <ms>, units: [{ type, id, model, startedAt, finishedAt, tokens: { input, output, cacheRead, cacheWrite, total }, cost, toolCalls, assistantMessages, ... }] }
|
|
73
|
+
```
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
*Auto-generated by `/gsd forensics`*
|
|
62
|
-
```
|
|
75
|
+
A unit dispatched more than once (`type/id` appears multiple times) indicates a stuck loop — the unit completed but artifact verification failed.
|
|
63
76
|
|
|
64
|
-
|
|
65
|
-
- Replace all absolute paths with relative paths
|
|
66
|
-
- Remove any API keys, tokens, or credentials
|
|
67
|
-
- Remove any environment variable values
|
|
68
|
-
- Do not include file content (code written by the user)
|
|
69
|
-
- Only include GSD structural information (tool names, file names, error messages)
|
|
77
|
+
## Investigation Protocol
|
|
70
78
|
|
|
71
|
-
|
|
79
|
+
1. **Start with the pre-parsed forensic report** above. The anomaly section contains automated findings — treat these as leads, not conclusions.
|
|
80
|
+
|
|
81
|
+
2. **Form hypotheses** about which module and code path is responsible. Use the source map to identify candidate files.
|
|
82
|
+
|
|
83
|
+
3. **Read the actual GSD source code** at `{{gsdSourceDir}}` to confirm or deny each hypothesis. Do not guess what code does — read it.
|
|
84
|
+
|
|
85
|
+
4. **Trace the code path** from the entry point (usually `auto-loop.ts` dispatch or `auto-dispatch.ts`) through to the failure point. Follow function calls across files.
|
|
86
|
+
|
|
87
|
+
5. **Identify the specific file and line** where the bug lives. Determine what kind of defect it is:
|
|
88
|
+
- Missing edge case / unhandled condition
|
|
89
|
+
- Wrong boolean logic or comparison
|
|
90
|
+
- Race condition or ordering issue
|
|
91
|
+
- State corruption (e.g. completed-units.json out of sync with artifacts)
|
|
92
|
+
- Timeout / recovery logic not triggering correctly
|
|
93
|
+
|
|
94
|
+
6. **Clarify if needed.** Use ask_user_questions (max 2 questions) only if the report is genuinely insufficient. Do not ask questions you can answer from the data or source code.
|
|
95
|
+
|
|
96
|
+
## Output
|
|
97
|
+
|
|
98
|
+
Explain your findings:
|
|
99
|
+
- **What happened** — the failure sequence reconstructed from activity logs and anomalies
|
|
100
|
+
- **Why it happened** — root cause traced to specific code in GSD source, with `file:line` references
|
|
101
|
+
- **Code snippet** — the problematic code and what it should do instead
|
|
102
|
+
- **Recovery** — what the user can do right now to get unstuck
|
|
103
|
+
|
|
104
|
+
Then **offer GitHub issue creation**: "Would you like me to create a GitHub issue for this on gsd-build/gsd-2?"
|
|
105
|
+
|
|
106
|
+
If yes, create using `gh issue create` with this format:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
## Problem
|
|
110
|
+
[1-2 sentence summary]
|
|
111
|
+
|
|
112
|
+
## Root Cause
|
|
113
|
+
[Specific file:line in GSD source, with code snippet showing the bug]
|
|
114
|
+
|
|
115
|
+
## Expected Behavior
|
|
116
|
+
[What the code should do instead — concrete fix suggestion]
|
|
117
|
+
|
|
118
|
+
## Environment
|
|
119
|
+
- GSD version: [from report]
|
|
120
|
+
- Model: [from report]
|
|
121
|
+
- Unit: [type/id that failed]
|
|
122
|
+
|
|
123
|
+
## Reproduction Context
|
|
124
|
+
[Phase, milestone, slice, what was happening when it failed]
|
|
125
|
+
|
|
126
|
+
## Forensic Evidence
|
|
127
|
+
[Key anomalies, error traces, relevant tool call sequences from the report]
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
*Auto-generated by `/gsd forensics`*
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Repository:** gsd-build/gsd-2
|
|
134
|
+
**Labels:** bug, auto-generated
|
|
135
|
+
|
|
136
|
+
### Redaction Rules (CRITICAL)
|
|
137
|
+
|
|
138
|
+
Before creating the issue, you MUST:
|
|
139
|
+
- Replace all absolute paths with relative paths
|
|
140
|
+
- Remove any API keys, tokens, or credentials
|
|
141
|
+
- Remove any environment variable values
|
|
142
|
+
- Do not include user's project code — only GSD structural information (tool names, file names, error messages)
|
|
143
|
+
|
|
144
|
+
## Report Saved
|
|
145
|
+
|
|
146
|
+
Remind the user that the full forensic report was saved locally (the path will be in the notification).
|
|
@@ -962,21 +962,25 @@ test("auto.ts startAuto calls autoLoop (not dispatchNextUnit as first dispatch)"
|
|
|
962
962
|
);
|
|
963
963
|
});
|
|
964
964
|
|
|
965
|
-
test("
|
|
966
|
-
const
|
|
967
|
-
resolve(import.meta.dirname, "..", "
|
|
965
|
+
test("agent_end handler calls resolveAgentEnd (not handleAgentEnd)", () => {
|
|
966
|
+
const hooksSrc = readFileSync(
|
|
967
|
+
resolve(import.meta.dirname, "..", "bootstrap", "register-hooks.ts"),
|
|
968
|
+
"utf-8",
|
|
969
|
+
);
|
|
970
|
+
// Verify the agent_end hook is registered
|
|
971
|
+
const handlerIdx = hooksSrc.indexOf('pi.on("agent_end"');
|
|
972
|
+
assert.ok(handlerIdx > -1, "register-hooks.ts must have an agent_end handler");
|
|
973
|
+
|
|
974
|
+
const recoverySrc = readFileSync(
|
|
975
|
+
resolve(import.meta.dirname, "..", "bootstrap", "agent-end-recovery.ts"),
|
|
968
976
|
"utf-8",
|
|
969
977
|
);
|
|
970
|
-
// Find the agent_end handler success path
|
|
971
|
-
const handlerIdx = src.indexOf('pi.on("agent_end"');
|
|
972
|
-
assert.ok(handlerIdx > -1, "index.ts must have an agent_end handler");
|
|
973
|
-
const handlerBlock = src.slice(handlerIdx, handlerIdx + 10000);
|
|
974
978
|
assert.ok(
|
|
975
|
-
|
|
979
|
+
recoverySrc.includes("resolveAgentEnd(event)"),
|
|
976
980
|
"agent_end success path must call resolveAgentEnd(event) instead of handleAgentEnd(ctx, pi)",
|
|
977
981
|
);
|
|
978
982
|
assert.ok(
|
|
979
|
-
|
|
983
|
+
recoverySrc.includes("isSessionSwitchInFlight()"),
|
|
980
984
|
"agent_end handler must ignore session-switch agent_end events from cmdCtx.newSession()",
|
|
981
985
|
);
|
|
982
986
|
});
|
|
@@ -176,9 +176,9 @@ async function main(): Promise<void> {
|
|
|
176
176
|
|
|
177
177
|
recordHealthSnapshot(2, 3, 1);
|
|
178
178
|
const summary = formatHealthSummary();
|
|
179
|
-
assertTrue(summary.includes("
|
|
180
|
-
assertTrue(summary.includes("
|
|
181
|
-
assertTrue(summary.includes("
|
|
179
|
+
assertTrue(summary.includes("2 errors") && summary.includes("3 warnings"), "summary includes error/warning counts");
|
|
180
|
+
assertTrue(summary.includes("1 fix applied"), "summary includes fix count");
|
|
181
|
+
assertTrue(summary.includes("1 of 5 consecutive errors"), "summary includes error streak");
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
// ─── Pre-Dispatch Health Gate ─────────────────────────────────────
|
|
@@ -261,38 +261,38 @@ test("pauseAutoForProviderError falls back to indefinite pause when not rate lim
|
|
|
261
261
|
|
|
262
262
|
// ── Escalating backoff for transient errors (#1166) ─────────────────────────
|
|
263
263
|
|
|
264
|
-
test("
|
|
265
|
-
const
|
|
264
|
+
test("agent-end-recovery.ts tracks consecutive transient errors for escalating backoff", () => {
|
|
265
|
+
const src = readFileSync(join(__dirname, "..", "bootstrap", "agent-end-recovery.ts"), "utf-8");
|
|
266
266
|
|
|
267
267
|
assert.ok(
|
|
268
|
-
|
|
269
|
-
"
|
|
268
|
+
src.includes("consecutiveTransientErrors"),
|
|
269
|
+
"agent-end-recovery.ts must track consecutiveTransientErrors for escalating backoff (#1166)",
|
|
270
270
|
);
|
|
271
271
|
assert.ok(
|
|
272
|
-
|
|
273
|
-
"
|
|
272
|
+
src.includes("MAX_TRANSIENT_AUTO_RESUMES"),
|
|
273
|
+
"agent-end-recovery.ts must define MAX_TRANSIENT_AUTO_RESUMES to cap infinite retries (#1166)",
|
|
274
274
|
);
|
|
275
275
|
});
|
|
276
276
|
|
|
277
|
-
test("
|
|
278
|
-
const
|
|
277
|
+
test("agent-end-recovery.ts resets consecutive transient error counter on success", () => {
|
|
278
|
+
const src = readFileSync(join(__dirname, "..", "bootstrap", "agent-end-recovery.ts"), "utf-8");
|
|
279
279
|
|
|
280
|
-
// After successful
|
|
280
|
+
// After successful agent_end (before resolveAgentEnd), the counter must be reset.
|
|
281
281
|
// Use a regex across the success block so CRLF checkouts on Windows do not
|
|
282
282
|
// push the reset line outside a fixed substring window.
|
|
283
283
|
assert.ok(
|
|
284
|
-
/consecutiveTransientErrors\s*=\s*0\s*;[\s\S]{0,250}
|
|
285
|
-
"consecutive transient error counter must be reset on
|
|
284
|
+
/consecutiveTransientErrors\s*=\s*0\s*;[\s\S]{0,250}resolveAgentEnd/.test(src),
|
|
285
|
+
"consecutive transient error counter must be reset before resolveAgentEnd on the success path (#1166)",
|
|
286
286
|
);
|
|
287
287
|
});
|
|
288
288
|
|
|
289
|
-
test("
|
|
290
|
-
const
|
|
289
|
+
test("agent-end-recovery.ts applies escalating delay for repeated transient errors", () => {
|
|
290
|
+
const src = readFileSync(join(__dirname, "..", "bootstrap", "agent-end-recovery.ts"), "utf-8");
|
|
291
291
|
|
|
292
|
-
// Must contain the exponential backoff formula
|
|
292
|
+
// Must contain the exponential backoff formula (may span multiple lines)
|
|
293
293
|
assert.ok(
|
|
294
|
-
|
|
295
|
-
"
|
|
294
|
+
src.includes("2 ** Math.max(0, consecutiveTransientErrors"),
|
|
295
|
+
"agent-end-recovery.ts must escalate retryAfterMs exponentially for consecutive transient errors (#1166)",
|
|
296
296
|
);
|
|
297
297
|
});
|
|
298
298
|
|
|
@@ -60,17 +60,17 @@ test("buildSkillActivationBlock matches installed skills from task context", ()
|
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
test("buildSkillActivationBlock includes always_use_skills from preferences", () => {
|
|
63
|
+
test("buildSkillActivationBlock includes always_use_skills from preferences using exact Skill tool format", () => {
|
|
64
64
|
const base = makeTempBase();
|
|
65
65
|
try {
|
|
66
|
-
writeSkill(base, "testing", "Use for
|
|
66
|
+
writeSkill(base, "swift-testing", "Use for Swift Testing assertions and verification patterns.");
|
|
67
67
|
loadOnlyTestSkills(base);
|
|
68
68
|
|
|
69
69
|
const result = buildBlock(base, { taskTitle: "Unrelated task title" }, {
|
|
70
|
-
always_use_skills: ["testing"],
|
|
70
|
+
always_use_skills: ["swift-testing"],
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
assert.
|
|
73
|
+
assert.equal(result, "<skill_activation>Call Skill('swift-testing').</skill_activation>");
|
|
74
74
|
} finally {
|
|
75
75
|
cleanup(base);
|
|
76
76
|
}
|
|
@@ -422,25 +422,25 @@ assertTrue(
|
|
|
422
422
|
"overlay has 10 tab labels",
|
|
423
423
|
);
|
|
424
424
|
|
|
425
|
-
// Verify commands.ts integration
|
|
426
|
-
const
|
|
427
|
-
const
|
|
425
|
+
// Verify commands/handlers/core.ts integration
|
|
426
|
+
const coreHandlerPath = join(__dirname, "..", "commands", "handlers", "core.ts");
|
|
427
|
+
const coreHandlerSrc = readFileSync(coreHandlerPath, "utf-8");
|
|
428
428
|
|
|
429
|
-
console.log("\n=== commands.ts integration ===");
|
|
429
|
+
console.log("\n=== commands/handlers/core.ts integration ===");
|
|
430
430
|
|
|
431
431
|
assertTrue(
|
|
432
|
-
|
|
433
|
-
"
|
|
432
|
+
coreHandlerSrc.includes('"visualize"'),
|
|
433
|
+
"core.ts has visualize in subcommands array",
|
|
434
434
|
);
|
|
435
435
|
|
|
436
436
|
assertTrue(
|
|
437
|
-
|
|
438
|
-
"
|
|
437
|
+
coreHandlerSrc.includes("GSDVisualizerOverlay"),
|
|
438
|
+
"core.ts imports GSDVisualizerOverlay",
|
|
439
439
|
);
|
|
440
440
|
|
|
441
441
|
assertTrue(
|
|
442
|
-
|
|
443
|
-
"
|
|
442
|
+
coreHandlerSrc.includes("handleVisualize"),
|
|
443
|
+
"core.ts has handleVisualize handler",
|
|
444
444
|
);
|
|
445
445
|
|
|
446
446
|
report();
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Data loader for workflow visualizer overlay — aggregates state + metrics.
|
|
2
2
|
|
|
3
3
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
4
5
|
import { deriveState } from './state.js';
|
|
5
6
|
import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js';
|
|
6
7
|
import { findMilestoneIds } from './milestone-ids.js';
|
|
7
|
-
import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile } from './paths.js';
|
|
8
|
+
import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile, gsdRoot } from './paths.js';
|
|
8
9
|
import {
|
|
9
10
|
getLedger,
|
|
10
11
|
getProjectTotals,
|
|
@@ -21,6 +22,8 @@ import { loadEffectiveGSDPreferences } from './preferences.js';
|
|
|
21
22
|
import { runProviderChecks, type ProviderCheckResult } from './doctor-providers.js';
|
|
22
23
|
import { generateSkillHealthReport } from './skill-health.js';
|
|
23
24
|
import { runEnvironmentChecks, type EnvironmentCheckResult } from './doctor-environment.js';
|
|
25
|
+
import { computeProgressScore } from './progress-score.js';
|
|
26
|
+
import { getHealthHistory } from './doctor-proactive.js';
|
|
24
27
|
|
|
25
28
|
import type { Phase } from './types.js';
|
|
26
29
|
import type { CaptureEntry } from './captures.js';
|
|
@@ -161,6 +164,27 @@ export interface SkillSummaryInfo {
|
|
|
161
164
|
topIssue: string | null;
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
/** A single doctor history entry for visualizer display. */
|
|
168
|
+
export interface VisualizerDoctorEntry {
|
|
169
|
+
ts: string;
|
|
170
|
+
ok: boolean;
|
|
171
|
+
errors: number;
|
|
172
|
+
warnings: number;
|
|
173
|
+
fixes: number;
|
|
174
|
+
codes: string[];
|
|
175
|
+
issues?: Array<{ severity: string; code: string; message: string; unitId: string }>;
|
|
176
|
+
fixDescriptions?: string[];
|
|
177
|
+
scope?: string;
|
|
178
|
+
summary?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Current progress score snapshot for health display. */
|
|
182
|
+
export interface VisualizerProgressScore {
|
|
183
|
+
level: "green" | "yellow" | "red";
|
|
184
|
+
summary: string;
|
|
185
|
+
signals: Array<{ kind: "positive" | "negative" | "neutral"; label: string }>;
|
|
186
|
+
}
|
|
187
|
+
|
|
164
188
|
export interface HealthInfo {
|
|
165
189
|
budgetCeiling: number | undefined;
|
|
166
190
|
tokenProfile: string;
|
|
@@ -174,6 +198,10 @@ export interface HealthInfo {
|
|
|
174
198
|
providers: ProviderStatusSummary[];
|
|
175
199
|
skillSummary: SkillSummaryInfo;
|
|
176
200
|
environmentIssues: import("./doctor-environment.js").EnvironmentCheckResult[];
|
|
201
|
+
/** Persisted doctor run history (most recent first, up to 20 entries). */
|
|
202
|
+
doctorHistory?: VisualizerDoctorEntry[];
|
|
203
|
+
/** Current in-memory progress score (null if auto-mode not active). */
|
|
204
|
+
progressScore?: VisualizerProgressScore | null;
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
export interface VisualizerData {
|
|
@@ -608,6 +636,26 @@ function loadHealth(units: UnitMetrics[], totals: ProjectTotals | null, basePath
|
|
|
608
636
|
environmentIssues = runEnvironmentChecks(basePath).filter(r => r.status !== "ok");
|
|
609
637
|
} catch { /* non-fatal */ }
|
|
610
638
|
|
|
639
|
+
// Doctor run history — persisted across sessions (sync read to keep loadHealth sync)
|
|
640
|
+
let doctorHistory: VisualizerDoctorEntry[] = [];
|
|
641
|
+
try {
|
|
642
|
+
const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
|
|
643
|
+
if (existsSync(historyPath)) {
|
|
644
|
+
const lines = readFileSync(historyPath, "utf-8").split("\n").filter(l => l.trim());
|
|
645
|
+
doctorHistory = lines.slice(-20).reverse().map(l => JSON.parse(l) as VisualizerDoctorEntry);
|
|
646
|
+
}
|
|
647
|
+
} catch { /* non-fatal */ }
|
|
648
|
+
|
|
649
|
+
// Current progress score — only meaningful when auto-mode has health data
|
|
650
|
+
let progressScore: VisualizerProgressScore | null = null;
|
|
651
|
+
try {
|
|
652
|
+
const history = getHealthHistory();
|
|
653
|
+
if (history.length > 0) {
|
|
654
|
+
const score = computeProgressScore();
|
|
655
|
+
progressScore = { level: score.level, summary: score.summary, signals: score.signals };
|
|
656
|
+
}
|
|
657
|
+
} catch { /* non-fatal */ }
|
|
658
|
+
|
|
611
659
|
return {
|
|
612
660
|
budgetCeiling,
|
|
613
661
|
tokenProfile,
|
|
@@ -621,6 +669,8 @@ function loadHealth(units: UnitMetrics[], totals: ProjectTotals | null, basePath
|
|
|
621
669
|
providers,
|
|
622
670
|
skillSummary,
|
|
623
671
|
environmentIssues,
|
|
672
|
+
doctorHistory,
|
|
673
|
+
progressScore,
|
|
624
674
|
};
|
|
625
675
|
}
|
|
626
676
|
|
|
@@ -1150,6 +1150,64 @@ export function renderHealthView(
|
|
|
1150
1150
|
}
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
+
// Progress score section — current traffic light status
|
|
1154
|
+
if (health.progressScore) {
|
|
1155
|
+
lines.push("");
|
|
1156
|
+
lines.push(th.fg("accent", th.bold("Progress Score")));
|
|
1157
|
+
lines.push("");
|
|
1158
|
+
const ps = health.progressScore;
|
|
1159
|
+
const scoreColor = ps.level === "green" ? "success" : ps.level === "yellow" ? "warning" : "error";
|
|
1160
|
+
const scoreIcon = ps.level === "green" ? "●" : ps.level === "yellow" ? "◐" : "○";
|
|
1161
|
+
lines.push(` ${th.fg(scoreColor, scoreIcon)} ${th.fg(scoreColor, ps.summary)}`);
|
|
1162
|
+
for (const signal of ps.signals) {
|
|
1163
|
+
const prefix = signal.kind === "positive" ? th.fg("success", " ✓")
|
|
1164
|
+
: signal.kind === "negative" ? th.fg("error", " ✗")
|
|
1165
|
+
: th.fg("dim", " ·");
|
|
1166
|
+
lines.push(` ${prefix} ${th.fg("dim", signal.label)}`);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Doctor history section — persisted across sessions
|
|
1171
|
+
const doctorHistory = health.doctorHistory ?? [];
|
|
1172
|
+
if (doctorHistory.length > 0) {
|
|
1173
|
+
lines.push("");
|
|
1174
|
+
lines.push(th.fg("accent", th.bold("Doctor History")));
|
|
1175
|
+
lines.push("");
|
|
1176
|
+
|
|
1177
|
+
for (const entry of doctorHistory.slice(0, 10)) {
|
|
1178
|
+
const icon = entry.ok ? th.fg("success", "✓") : th.fg("error", "✗");
|
|
1179
|
+
const ts = entry.ts.replace("T", " ").slice(0, 19);
|
|
1180
|
+
const scopeTag = entry.scope ? th.fg("accent", ` [${entry.scope}]`) : "";
|
|
1181
|
+
// Prefer human-readable summary, fall back to counts
|
|
1182
|
+
const detail = entry.summary
|
|
1183
|
+
? th.fg("text", entry.summary)
|
|
1184
|
+
: th.fg("text", `${entry.errors} errors, ${entry.warnings} warnings, ${entry.fixes} fixes`);
|
|
1185
|
+
lines.push(` ${icon} ${th.fg("dim", ts)}${scopeTag} ${detail}`);
|
|
1186
|
+
|
|
1187
|
+
// Show issue details if available
|
|
1188
|
+
if (entry.issues && entry.issues.length > 0) {
|
|
1189
|
+
for (const issue of entry.issues.slice(0, 3)) {
|
|
1190
|
+
const issuePfx = issue.severity === "error" ? th.fg("error", " ✗") : th.fg("warning", " ⚠");
|
|
1191
|
+
lines.push(` ${issuePfx} ${th.fg("dim", truncateToWidth(issue.message, width - 12))}`);
|
|
1192
|
+
}
|
|
1193
|
+
if (entry.issues.length > 3) {
|
|
1194
|
+
lines.push(` ${th.fg("dim", `+${entry.issues.length - 3} more`)}`);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// Show fixes if available
|
|
1199
|
+
if (entry.fixDescriptions && entry.fixDescriptions.length > 0) {
|
|
1200
|
+
for (const fix of entry.fixDescriptions.slice(0, 2)) {
|
|
1201
|
+
lines.push(` ${th.fg("success", "↳")} ${th.fg("dim", truncateToWidth(fix, width - 12))}`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (doctorHistory.length > 10) {
|
|
1207
|
+
lines.push(` ${th.fg("dim", `...${doctorHistory.length - 10} older entries`)}`);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1153
1211
|
// Skills section
|
|
1154
1212
|
if (health.skillSummary?.total > 0) {
|
|
1155
1213
|
lines.push("");
|
|
File without changes
|
|
File without changes
|