@exaudeus/workrail 3.66.0 → 3.68.0
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/application/services/compiler/template-registry.js +10 -1
- package/dist/application/validation.js +1 -1
- package/dist/cli/commands/worktrain-init.js +1 -1
- package/dist/console/standalone-console.js +4 -1
- package/dist/console-ui/assets/{index-BynU38Vu.js → index-CyzltI6D.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/coordinators/modes/full-pipeline.js +4 -4
- package/dist/coordinators/modes/implement-shared.js +5 -5
- package/dist/coordinators/modes/implement.js +4 -4
- package/dist/coordinators/pr-review.js +4 -4
- package/dist/daemon/workflow-runner.d.ts +1 -0
- package/dist/daemon/workflow-runner.js +1 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts +21 -2
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +48 -0
- package/dist/manifest.json +41 -41
- package/dist/mcp/handlers/v2-workflow.js +24 -7
- package/dist/mcp/output-schemas.d.ts +36 -0
- package/dist/mcp/output-schemas.js +11 -1
- package/dist/mcp/workflow-protocol-contracts.js +2 -2
- package/dist/v2/projections/session-metrics.d.ts +1 -1
- package/dist/v2/projections/session-metrics.js +16 -35
- package/dist/v2/usecases/console-routes.d.ts +2 -2
- package/docs/authoring-v2.md +4 -4
- package/docs/changelog-recent.md +3 -3
- package/docs/configuration.md +1 -1
- package/docs/design/adaptive-coordinator-context-candidates.md +1 -1
- package/docs/design/adaptive-coordinator-context.md +1 -1
- package/docs/design/adaptive-coordinator-routing-candidates.md +18 -18
- package/docs/design/adaptive-coordinator-routing-review.md +1 -1
- package/docs/design/adaptive-coordinator-routing.md +34 -34
- package/docs/design/agent-cascade-protocol.md +2 -2
- package/docs/design/console-daemon-separation-discovery.md +323 -0
- package/docs/design/context-assembly-design-candidates.md +1 -1
- package/docs/design/context-assembly-implementation-plan.md +1 -1
- package/docs/design/context-assembly-layer.md +2 -2
- package/docs/design/context-assembly-review-findings.md +1 -1
- package/docs/design/coordinator-access-audit.md +293 -0
- package/docs/design/coordinator-architecture-audit.md +62 -0
- package/docs/design/coordinator-error-handling-audit.md +240 -0
- package/docs/design/coordinator-testability-audit.md +426 -0
- package/docs/design/daemon-architecture-discovery.md +1 -1
- package/docs/design/daemon-console-separation-discovery.md +242 -0
- package/docs/design/daemon-memory-audit.md +203 -0
- package/docs/design/design-candidates-console-daemon-separation.md +256 -0
- package/docs/design/design-candidates-discovery-loop-fix.md +141 -0
- package/docs/design/design-review-findings-console-daemon-separation.md +106 -0
- package/docs/design/design-review-findings-discovery-loop-fix.md +81 -0
- package/docs/design/discovery-loop-fix-candidates.md +161 -0
- package/docs/design/discovery-loop-fix-design-review.md +106 -0
- package/docs/design/discovery-loop-fix-validation.md +258 -0
- package/docs/design/discovery-loop-investigation-A.md +188 -0
- package/docs/design/discovery-loop-investigation-B.md +287 -0
- package/docs/design/exploration-workflow-candidates.md +205 -0
- package/docs/design/exploration-workflow-design-review.md +166 -0
- package/docs/design/exploration-workflow-discovery.md +443 -0
- package/docs/design/ide-context-files-candidates.md +231 -0
- package/docs/design/ide-context-files-design-review.md +85 -0
- package/docs/design/ide-context-files.md +615 -0
- package/docs/design/implementation-plan-discovery-loop-fix.md +199 -0
- package/docs/design/implementation-plan-queue-poll-rotation.md +102 -0
- package/docs/design/in-process-http-audit.md +190 -0
- package/docs/design/layer3b-ghost-nodes-design-candidates.md +2 -2
- package/docs/design/loadSessionNotes-candidates.md +108 -0
- package/docs/design/loadSessionNotes-test-coverage-discovery.md +297 -0
- package/docs/design/loadSessionNotes-test-coverage-session4.md +209 -0
- package/docs/design/loadSessionNotes-test-coverage-v3.md +321 -0
- package/docs/design/probe-session-design-candidates.md +261 -0
- package/docs/design/probe-session-phase0.md +490 -0
- package/docs/design/routines-guide.md +7 -7
- package/docs/design/session-metrics-attribution-candidates.md +250 -0
- package/docs/design/session-metrics-attribution-design-review.md +115 -0
- package/docs/design/session-metrics-attribution-discovery.md +319 -0
- package/docs/design/session-metrics-candidates.md +227 -0
- package/docs/design/session-metrics-design-review.md +104 -0
- package/docs/design/session-metrics-discovery.md +454 -0
- package/docs/design/spawn-session-debug.md +202 -0
- package/docs/design/trigger-validator-candidates.md +214 -0
- package/docs/design/trigger-validator-review.md +109 -0
- package/docs/design/trigger-validator-shaping-phase0.md +239 -0
- package/docs/design/trigger-validator.md +454 -0
- package/docs/design/v2-core-design-locks.md +2 -2
- package/docs/design/workflow-extension-points.md +15 -15
- package/docs/design/workflow-id-validation-at-startup.md +1 -1
- package/docs/design/workflow-id-validation-implementation-plan.md +2 -2
- package/docs/design/workflow-trigger-lifecycle-audit.md +175 -0
- package/docs/design/worktrain-task-queue-candidates.md +5 -5
- package/docs/design/worktrain-task-queue.md +4 -4
- package/docs/discovery/coordinator-script-design.md +1 -1
- package/docs/discovery/coordinator-ux-discovery.md +3 -3
- package/docs/discovery/simulation-report.md +1 -1
- package/docs/discovery/workflow-modernization-discovery.md +326 -0
- package/docs/discovery/workflow-selection-for-discovery-tasks.md +33 -33
- package/docs/discovery/worktrain-status-briefing.md +1 -1
- package/docs/discovery/wr-discovery-goal-reframing.md +1 -1
- package/docs/docker.md +1 -1
- package/docs/ideas/backlog.md +227 -0
- package/docs/ideas/third-party-workflow-setup-design-thinking.md +1 -1
- package/docs/integrations/claude-code.md +5 -5
- package/docs/integrations/firebender.md +1 -1
- package/docs/plans/agentic-orchestration-roadmap.md +2 -2
- package/docs/plans/mr-review-workflow-redesign.md +9 -9
- package/docs/plans/ui-ux-workflow-design-candidates.md +4 -4
- package/docs/plans/ui-ux-workflow-discovery.md +2 -2
- package/docs/plans/workflow-categories-candidates.md +8 -8
- package/docs/plans/workflow-categories-discovery.md +4 -4
- package/docs/plans/workflow-modernization-design.md +430 -0
- package/docs/plans/workflow-staleness-detection-candidates.md +11 -11
- package/docs/plans/workflow-staleness-detection-review.md +4 -4
- package/docs/plans/workflow-staleness-detection.md +9 -9
- package/docs/plans/workrail-platform-vision.md +3 -3
- package/docs/reference/agent-context-cleaner-snippet.md +1 -1
- package/docs/reference/agent-context-guidance.md +4 -4
- package/docs/reference/context-optimization.md +2 -2
- package/docs/roadmap/now-next-later.md +2 -2
- package/docs/roadmap/open-work-inventory.md +16 -16
- package/docs/workflows.md +31 -31
- package/package.json +1 -1
- package/spec/workflow-tags.json +47 -47
- package/workflows/adaptive-ticket-creation.json +16 -16
- package/workflows/architecture-scalability-audit.json +22 -22
- package/workflows/bug-investigation.agentic.v2.json +3 -3
- package/workflows/classify-task-workflow.json +1 -1
- package/workflows/coding-task-workflow-agentic.json +6 -6
- package/workflows/cross-platform-code-conversion.v2.json +8 -8
- package/workflows/document-creation-workflow.json +8 -8
- package/workflows/documentation-update-workflow.json +8 -8
- package/workflows/intelligent-test-case-generation.json +2 -2
- package/workflows/learner-centered-course-workflow.json +2 -2
- package/workflows/mr-review-workflow.agentic.v2.json +4 -4
- package/workflows/personal-learning-materials-creation-branched.json +8 -8
- package/workflows/presentation-creation.json +5 -5
- package/workflows/production-readiness-audit.json +1 -1
- package/workflows/relocation-workflow-us.json +31 -31
- package/workflows/routines/context-gathering.json +1 -1
- package/workflows/routines/design-review.json +1 -1
- package/workflows/routines/execution-simulation.json +1 -1
- package/workflows/routines/feature-implementation.json +3 -3
- package/workflows/routines/final-verification.json +1 -1
- package/workflows/routines/hypothesis-challenge.json +1 -1
- package/workflows/routines/ideation.json +1 -1
- package/workflows/routines/parallel-work-partitioning.json +3 -3
- package/workflows/routines/philosophy-alignment.json +2 -2
- package/workflows/routines/plan-analysis.json +1 -1
- package/workflows/routines/plan-generation.json +1 -1
- package/workflows/routines/tension-driven-design.json +6 -6
- package/workflows/scoped-documentation-workflow.json +26 -26
- package/workflows/ui-ux-design-workflow.json +14 -14
- package/workflows/workflow-diagnose-environment.json +1 -1
- package/workflows/workflow-for-workflows.json +32 -77
- package/workflows/workflow-for-workflows.v2.json +0 -788
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Discovery Loop Investigation B: wr.discovery Infinite Re-run on Issue #393
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-21
|
|
4
|
+
**Issue:** GitHub #393 - "test(daemon): add coverage for loadSessionNotes failure paths"
|
|
5
|
+
**Symptom:** Autonomous pipeline repeatedly runs `wr.discovery` sessions on issue #393. Each session runs for ~30 minutes then stops with `outcome: timeout`. The pipeline never progresses to shaping or coding. The issue stays assigned to `worktrain-etienneb`. The queue keeps re-selecting it every poll cycle.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Timeline of What Actually Happened
|
|
10
|
+
|
|
11
|
+
All timestamps are UTC. Source: `~/.workrail/events/daemon/2026-04-21.jsonl` and `~/.workrail/queue-poll.jsonl`.
|
|
12
|
+
|
|
13
|
+
### 2026-04-20 19:10 - Loop begins
|
|
14
|
+
|
|
15
|
+
Issue #393 is assigned to `worktrain-etienneb` and has a `## Acceptance Criteria` checklist section in its body. `inferMaturity()` scores it as `specced`.
|
|
16
|
+
|
|
17
|
+
Queue poll starts selecting it every ~5 minutes:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
19:10:53 task_selected reason="has acceptance criteria or checklist"
|
|
21
|
+
19:15:48 task_selected ...
|
|
22
|
+
19:20:48 task_selected ...
|
|
23
|
+
... (continues hourly or more frequently through 2026-04-21)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Each selection dispatches `dispatchAdaptivePipeline(goal="test(daemon): ...", workspace=".../workrail")`. The goal contains no PR/MR reference and no dep-bump keyword, so `routeTask()` returns `FULL`. The FULL pipeline spawns `wr.discovery`.
|
|
27
|
+
|
|
28
|
+
### 2026-04-21 01:53 - First wr.discovery sessions start
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
01:53:42 session_started wr.discovery cd2f1c1f
|
|
32
|
+
01:55:30 session_started wr.discovery 0913d66e
|
|
33
|
+
01:57:11 session_started wr.discovery 03b13b82
|
|
34
|
+
01:58:53 session_started wr.discovery b586dba9
|
|
35
|
+
02:01:01 session_started wr.discovery 3846e2a5
|
|
36
|
+
02:02:44 session_started wr.discovery 2976d1db
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Multiple sessions are spawned near-simultaneously because the daemon restarted many times between 01:50 and 02:07 (15+ restarts logged). Each restart clears the in-memory `dispatchingIssues` set, so the idempotency guard is reset and the queue re-dispatches on the first poll cycle after each restart.
|
|
40
|
+
|
|
41
|
+
### 2026-04-21 05:24 onward - Single-session steady-state
|
|
42
|
+
|
|
43
|
+
From 05:24 onward, one `wr.discovery` session is spawned per hour (matching the queue poll interval). The pattern is:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
05:24:20 session_started wr.discovery 747b8b81
|
|
47
|
+
05:54:20 session_completed wr.discovery 747b8b81 outcome=timeout
|
|
48
|
+
|
|
49
|
+
06:24:15 session_started wr.discovery 063f67e3
|
|
50
|
+
06:54:15 session_completed wr.discovery 063f67e3 outcome=timeout
|
|
51
|
+
|
|
52
|
+
07:24:15 session_started wr.discovery c816e662
|
|
53
|
+
07:54:15 session_completed wr.discovery c816e662 outcome=timeout
|
|
54
|
+
... (continues through 13:24)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Every session runs for exactly 30 minutes, then times out. No shaping, coding, or review sessions are ever spawned. The issue remains open and assigned to `worktrain-etienneb` throughout.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## What Should Have Happened
|
|
62
|
+
|
|
63
|
+
1. Queue selects issue #393 (maturity=`specced`). **[Correct]**
|
|
64
|
+
2. `dispatchAdaptivePipeline` routes to FULL mode. **[Correct]**
|
|
65
|
+
3. FULL pipeline spawns `wr.discovery` with `DISCOVERY_TIMEOUT_MS = 55 minutes`. **[Correct - coordinator side]**
|
|
66
|
+
4. `wr.discovery` completes successfully within 55 minutes.
|
|
67
|
+
5. FULL pipeline reads the handoff artifact, spawns `wr.shaping`, then `wr.coding-task`.
|
|
68
|
+
6. Coding session opens a PR. PR review runs. PR merges.
|
|
69
|
+
7. Post-merge: issue #393 is closed (either automatically by GitHub when the PR closes it, or explicitly).
|
|
70
|
+
8. On the next queue poll, `pollGitHubQueueIssues()` fetches only open issues - #393 would no longer appear, ending the loop.
|
|
71
|
+
|
|
72
|
+
Instead:
|
|
73
|
+
- `wr.discovery` sessions time out at 30 minutes (not 55).
|
|
74
|
+
- The FULL pipeline escalates.
|
|
75
|
+
- No downstream phases run.
|
|
76
|
+
- The GitHub issue stays open and assigned.
|
|
77
|
+
- The queue re-selects the issue on every poll cycle indefinitely.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Root Causes
|
|
82
|
+
|
|
83
|
+
### Root Cause 1 (Primary): Session timeout mismatch - 30 min vs. 55 min
|
|
84
|
+
|
|
85
|
+
**File:** `src/daemon/workflow-runner.ts:83` and `src/trigger/trigger-listener.ts:436-498`
|
|
86
|
+
|
|
87
|
+
`DEFAULT_SESSION_TIMEOUT_MINUTES = 30` is the default applied to all sessions that do not receive an explicit `agentConfig.maxSessionMinutes` override.
|
|
88
|
+
|
|
89
|
+
When the coordinator calls `deps.spawnSession('wr.discovery', ...)`, the implementation in `trigger-listener.ts:492-498` calls `routerRef.dispatch({ workflowId, goal, workspacePath, context, _preAllocatedStartResponse })`. Note that **no `agentConfig` field is passed**. This means the spawned `wr.discovery` session runs under `DEFAULT_SESSION_TIMEOUT_MINUTES = 30`.
|
|
90
|
+
|
|
91
|
+
The coordinator then calls `deps.awaitSessions([discoveryHandle], DISCOVERY_TIMEOUT_MS)` where `DISCOVERY_TIMEOUT_MS = 55 * 60 * 1000` (55 minutes). But the session itself has already timed out at 30 minutes and returned `outcome: timeout` to the `awaitSessions` poller.
|
|
92
|
+
|
|
93
|
+
The mismatch:
|
|
94
|
+
|
|
95
|
+
| Layer | Timeout value | Where set |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| Session wall-clock (actual) | 30 min | `workflow-runner.ts:83` `DEFAULT_SESSION_TIMEOUT_MINUTES` |
|
|
98
|
+
| Coordinator await (expected) | 55 min | `adaptive-pipeline.ts:39` `DISCOVERY_TIMEOUT_MS` |
|
|
99
|
+
|
|
100
|
+
The coordinator's 55-minute budget is never consumed because the session dies first at 30 minutes. `awaitSessions` sees `outcome: timeout`, the FULL pipeline returns `{ kind: 'escalated', escalationReason: { phase: 'discovery', reason: 'discovery session timeout' } }`, and nothing more is done.
|
|
101
|
+
|
|
102
|
+
**Why the sessions time out:** The `wr.discovery` workflow has 22 steps. It needs to do landscape analysis, candidate generation, direction selection, and handoff preparation. 30 minutes is insufficient for this on a complex task, especially when the session is also attempting delegation via `spawn_agent` (which eats into the parent's wall-clock budget).
|
|
103
|
+
|
|
104
|
+
### Root Cause 2 (Primary): No post-escalation issue lifecycle management
|
|
105
|
+
|
|
106
|
+
**File:** `src/trigger/polling-scheduler.ts:616-624`
|
|
107
|
+
|
|
108
|
+
When `dispatchAdaptivePipeline` resolves (with any outcome including `escalated`), the only action taken is:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
void dispatchP
|
|
112
|
+
.then(() => {
|
|
113
|
+
this.dispatchingIssues.delete(top.issue.number); // only action
|
|
114
|
+
console.log(`[QueuePoll] in-flight-clear #${top.issue.number} reason=completed`);
|
|
115
|
+
})
|
|
116
|
+
.catch(() => {
|
|
117
|
+
this.dispatchingIssues.delete(top.issue.number); // only action
|
|
118
|
+
console.log(`[QueuePoll] in-flight-clear #${top.issue.number} reason=error`);
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The `PipelineOutcome` returned by the coordinator is **silently discarded**. There is no code that:
|
|
123
|
+
|
|
124
|
+
- Inspects `outcome.kind` (could be `merged`, `escalated`, or `dry_run`)
|
|
125
|
+
- Adds a `worktrain:failed` or `worktrain:in-progress` label to the GitHub issue on escalation
|
|
126
|
+
- Posts a comment explaining the failure
|
|
127
|
+
- Closes the issue if merged
|
|
128
|
+
- Persists the session ID to the `daemon-sessions/` directory (the sidecar idempotency store)
|
|
129
|
+
|
|
130
|
+
Without any of these, the queue's only guard against re-selecting a completed/escalated issue is:
|
|
131
|
+
1. **`worktrain:in-progress` label check** (H3 in `doPollGitHubQueue`) - never added
|
|
132
|
+
2. **`/sess_[a-z0-9]+/` in issue body** (H3) - never added
|
|
133
|
+
3. **`dispatchingIssues` in-memory set** - cleared on `dispatchAdaptivePipeline` completion and lost on every daemon restart
|
|
134
|
+
4. **`checkIdempotency()` sidecar scan** - scans `~/.workrail/daemon-sessions/`, but that directory is always empty (the coordinator never writes session files there)
|
|
135
|
+
5. **GitHub issue state = closed** - never triggered because the pipeline never reaches merge
|
|
136
|
+
|
|
137
|
+
All five guards fail. The issue is re-selected on every subsequent poll cycle.
|
|
138
|
+
|
|
139
|
+
### Root Cause 3 (Contributing): In-memory `dispatchingIssues` lost on daemon restart
|
|
140
|
+
|
|
141
|
+
**File:** `src/trigger/polling-scheduler.ts:113`, `private readonly dispatchingIssues = new Set<number>()`
|
|
142
|
+
|
|
143
|
+
The `dispatchingIssues` set is in-memory only. Each daemon restart clears it completely. The daemon restarted 15+ times between 01:50-02:07 and multiple times thereafter. Every restart causes the set to be re-initialized empty, so the first poll cycle after each restart re-dispatches #393 even if the previous pipeline is still in flight.
|
|
144
|
+
|
|
145
|
+
The queue poll log shows this clearly:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
01:10:39 task_skipped reason=active_session_in_process (set is live)
|
|
149
|
+
01:12:29 task_selected reason=... (daemon restarted, set cleared)
|
|
150
|
+
01:12:39 task_skipped reason=active_session_in_process (set re-populated)
|
|
151
|
+
01:16:06 task_selected reason=... (daemon restarted again)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The sidecar idempotency check (`checkIdempotency()` scanning `~/.workrail/daemon-sessions/`) was designed to handle cross-restart dedup, but it only works if the coordinator writes JSON files to that directory. It never does.
|
|
155
|
+
|
|
156
|
+
### Root Cause 4 (Contributing): `daemon-sessions/` directory never populated by coordinator
|
|
157
|
+
|
|
158
|
+
**File:** `src/trigger/adapters/github-queue-poller.ts:86`, `src/trigger/trigger-listener.ts:436-498`
|
|
159
|
+
|
|
160
|
+
`checkIdempotency()` scans `~/.workrail/daemon-sessions/` for JSON files containing `context.taskCandidate.issueNumber`. The directory is empty at time of investigation. The coordinator's `spawnSession()` implementation does not write any file to `daemon-sessions/`. The comment in `checkIdempotency()` says:
|
|
161
|
+
|
|
162
|
+
> "real session files written by persistTokens() contain only { continueToken, checkpointToken, ts } -- no context field. A file without taskCandidate cannot claim ownership of any issue."
|
|
163
|
+
|
|
164
|
+
So even if a file were written, it would be read as `clear` (not `active`) because it would have no `context.taskCandidate.issueNumber`. The sidecar idempotency check is effectively dead for coordinator-spawned sessions.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Evidence Summary
|
|
169
|
+
|
|
170
|
+
| Evidence | Source |
|
|
171
|
+
|---|---|
|
|
172
|
+
| 13 wr.discovery sessions completed with `outcome=timeout`, all at exactly ~30 min | `events/daemon/2026-04-21.jsonl` |
|
|
173
|
+
| `DEFAULT_SESSION_TIMEOUT_MINUTES = 30` | `src/daemon/workflow-runner.ts:83` |
|
|
174
|
+
| `DISCOVERY_TIMEOUT_MS = 55 * 60 * 1000` | `src/coordinators/adaptive-pipeline.ts:39` |
|
|
175
|
+
| `spawnSession()` passes no `agentConfig` to `routerRef.dispatch()` | `src/trigger/trigger-listener.ts:492-498` |
|
|
176
|
+
| 73 `task_selected` events for #393 across 19+ hours | `~/.workrail/queue-poll.jsonl` |
|
|
177
|
+
| 0 shaping or coding sessions spawned | `events/daemon/2026-04-21.jsonl` (no `session_started` for `wr.shaping` or `wr.coding-task` linked to #393) |
|
|
178
|
+
| `dispatchAdaptivePipeline` result never inspected | `src/trigger/polling-scheduler.ts:616-624` |
|
|
179
|
+
| `daemon-sessions/` directory is empty | `ls ~/.workrail/daemon-sessions/` |
|
|
180
|
+
| GitHub issue #393 remains `OPEN`, assigned to `worktrain-etienneb` | `gh issue view 393` |
|
|
181
|
+
| 15+ daemon restarts between 01:50-02:07 | `events/daemon/2026-04-21.jsonl` (daemon_started/stopped events) |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Recommended Fixes
|
|
186
|
+
|
|
187
|
+
### Fix 1 (Critical): Pass `agentConfig.maxSessionMinutes` in coordinator's `spawnSession()`
|
|
188
|
+
|
|
189
|
+
**File:** `src/trigger/trigger-listener.ts` (the `spawnSession` implementation, lines 492-498) or `src/coordinators/modes/full-pipeline.ts` (the call site).
|
|
190
|
+
|
|
191
|
+
The `spawnSession` signature already accepts a `context` parameter. The fix is to thread `agentConfig` through the spawn call so the child session gets the correct timeout budget.
|
|
192
|
+
|
|
193
|
+
**Option A:** Extend `CoordinatorDeps.spawnSession` to accept an optional `agentConfig` parameter:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// In pr-review.ts (CoordinatorDeps interface):
|
|
197
|
+
readonly spawnSession: (
|
|
198
|
+
workflowId: string,
|
|
199
|
+
goal: string,
|
|
200
|
+
workspace: string,
|
|
201
|
+
context?: Readonly<Record<string, unknown>>,
|
|
202
|
+
agentConfig?: { readonly maxSessionMinutes?: number; readonly maxTurns?: number },
|
|
203
|
+
) => Promise<Result<string | null, string>>;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Then in `full-pipeline.ts`, pass the coordinator's per-phase timeout:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const discoverySpawnResult = await deps.spawnSession(
|
|
210
|
+
'wr.discovery',
|
|
211
|
+
opts.goal,
|
|
212
|
+
opts.workspace,
|
|
213
|
+
undefined,
|
|
214
|
+
{ maxSessionMinutes: Math.ceil(DISCOVERY_TIMEOUT_MS / 60_000) }, // 55
|
|
215
|
+
);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
And in `trigger-listener.ts`, forward the agentConfig to the dispatch call:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
routerRef.dispatch({
|
|
222
|
+
workflowId,
|
|
223
|
+
goal,
|
|
224
|
+
workspacePath: workspace,
|
|
225
|
+
context,
|
|
226
|
+
agentConfig, // forward the injected agentConfig
|
|
227
|
+
_preAllocatedStartResponse: startResult.value.response,
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Option B (simpler, no signature change):** Set a higher default in `~/.workrail/config.json` or in the trigger definition's `agentConfig.maxSessionMinutes`. This avoids code changes but requires operator action and is less principled (the coordinator already has the right value; it should communicate it).
|
|
232
|
+
|
|
233
|
+
**Recommendation:** Option A. The coordinator knows the correct budget for each phase. Passing it explicitly is a tighter invariant than relying on a global default. The mismatch between `DEFAULT_SESSION_TIMEOUT_MINUTES=30` and `DISCOVERY_TIMEOUT_MS=55` is a latent bug that would also affect shaping (35 min > 30 min) and coding (65 min > 30 min) sessions.
|
|
234
|
+
|
|
235
|
+
### Fix 2 (Critical): Handle PipelineOutcome in the queue poller
|
|
236
|
+
|
|
237
|
+
**File:** `src/trigger/polling-scheduler.ts:616-624`
|
|
238
|
+
|
|
239
|
+
The `dispatchP.then/catch` blocks must inspect the outcome and take action when the pipeline escalates:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
void dispatchP
|
|
243
|
+
.then((outcome) => {
|
|
244
|
+
this.dispatchingIssues.delete(top.issue.number);
|
|
245
|
+
if (outcome.kind === 'escalated') {
|
|
246
|
+
// Add worktrain:failed label + comment to prevent re-selection
|
|
247
|
+
void this.markIssueEscalated(top.issue, outcome.escalationReason, source);
|
|
248
|
+
} else if (outcome.kind === 'merged') {
|
|
249
|
+
// Issue should be closed by the PR, but add a log entry
|
|
250
|
+
console.log(`[QueuePoll] pipeline merged for #${top.issue.number}`);
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
.catch((err) => {
|
|
254
|
+
this.dispatchingIssues.delete(top.issue.number);
|
|
255
|
+
void this.markIssueEscalated(top.issue, { phase: 'dispatch', reason: String(err) }, source);
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`markIssueEscalated()` should:
|
|
260
|
+
1. Add the `worktrain:failed` label to the issue (which is already checked by H3 as `active_session_or_in_progress` - or add a dedicated `worktrain:failed` label to `queueConfig.excludeLabels`)
|
|
261
|
+
2. Post a comment with the escalation reason for operator visibility
|
|
262
|
+
|
|
263
|
+
Alternatively, a simpler guard: add `worktrain:failed` to `queueConfig.excludeLabels` and have the poller add that label on escalation.
|
|
264
|
+
|
|
265
|
+
### Fix 3 (Important): Persist issue ownership to `daemon-sessions/` for cross-restart idempotency
|
|
266
|
+
|
|
267
|
+
**File:** `src/trigger/polling-scheduler.ts` (in `doPollGitHubQueue`) or `src/trigger/trigger-listener.ts` (in `spawnSession`).
|
|
268
|
+
|
|
269
|
+
Before calling `dispatchAdaptivePipeline`, write a sidecar file to `~/.workrail/daemon-sessions/<issueNumber>.json` with content `{ "context": { "taskCandidate": { "issueNumber": N } } }`. Remove this file in the `.then`/`.catch` completion handler.
|
|
270
|
+
|
|
271
|
+
This makes `checkIdempotency()` effective across daemon restarts. The existing logic in `github-queue-poller.ts:305-356` already handles this correctly - it just needs a file to read.
|
|
272
|
+
|
|
273
|
+
### Fix 4 (Secondary): Add a 30-second dedup TTL extension when the daemon restarts
|
|
274
|
+
|
|
275
|
+
**File:** `src/trigger/polling-scheduler.ts` (start of `doPollGitHubQueue`)
|
|
276
|
+
|
|
277
|
+
The 30-second dedup window in `TriggerRouter._recentAdaptiveDispatches` is lost on restart. A short startup delay (e.g., wait 60 seconds before the first post-restart poll cycle) would reduce the burst of duplicate dispatches during rapid restart sequences. This is a band-aid; Fix 3 is the correct structural solution.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Non-Causes (Ruled Out)
|
|
282
|
+
|
|
283
|
+
- **Queue filter not working:** The queue correctly filters to only open issues assigned to `worktrain-etienneb`. Issue #393 legitimately matches because it is open and assigned.
|
|
284
|
+
- **`inferMaturity()` incorrect:** #393 has `## Acceptance Criteria` with `- [ ]` checklist items. `specced` is the correct maturity.
|
|
285
|
+
- **`routeTask()` routing to wrong mode:** The goal "test(daemon): add coverage..." correctly routes to FULL (no PR reference, no dep-bump keywords, no pitch file).
|
|
286
|
+
- **`wr.discovery` workflow definition incorrect:** The workflow has 22 steps and is substantive. The issue is runtime timeout, not workflow structure.
|
|
287
|
+
- **Coordinator logic incorrect:** The FULL pipeline's escalation on discovery timeout is correct behavior. The bug is that escalation has no consequence on the queue side.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Design Candidates: exploration-workflow.json Gap
|
|
2
|
+
|
|
3
|
+
*Human-facing artifact only. Durable truth is in workflow step notes and context variables.*
|
|
4
|
+
|
|
5
|
+
*This document is raw investigative material for the main agent's synthesis — not a final decision.*
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem Understanding
|
|
10
|
+
|
|
11
|
+
### Core Tensions
|
|
12
|
+
|
|
13
|
+
**T1: Naming backward-compatibility vs. canonical taxonomy**
|
|
14
|
+
`start_workflow exploration-workflow` throws `WorkflowNotFoundError` (-32001, hard error). The storage layer (`file-workflow-storage.ts:254`) does exact-ID lookup via `sanitizeId()` — no fuzzy match, no redirect. `spec/workflow.schema.json` has `additionalProperties: false` and no `aliases` field — aliases cannot be added to workflow files without a schema change. The only in-bundle fix that requires no schema/engine change is adding a workflow file with `id: "exploration-workflow"`.
|
|
15
|
+
|
|
16
|
+
**T2: Planning doc currency vs. housekeeping cost**
|
|
17
|
+
Three planning docs (`docs/roadmap/now-next-later.md`, `docs/roadmap/open-work-inventory.md`, `docs/tickets/next-up.md`) list `exploration-workflow.json` as the #1 modernization priority — a workflow that was intentionally deleted 7 months ago. The docs were never updated because the consolidation (commit `a0ddaaac`) was not followed by a completion-update pass.
|
|
18
|
+
|
|
19
|
+
**T3: YAGNI vs. discoverability pain**
|
|
20
|
+
YAGNI says don't build a general alias mechanism for one case. But the naming gap causes a hard error that the trigger system (`trigger-listener.ts:293`) explicitly treats as serious — it validates workflow IDs at startup to prevent silent `workflow_not_found` failures. These pull in opposite directions.
|
|
21
|
+
|
|
22
|
+
### Likely Seam
|
|
23
|
+
|
|
24
|
+
Three simultaneous seams: (1) workflow bundle — `exploration-workflow.json` is gone, `wr.discovery.json` doesn't resolve to the old ID; (2) planning docs — three files with stale references; (3) schema — no alias support exists. The minimum-surface fix uses only seam (1). The architectural fix uses seam (3) and requires a storage layer change.
|
|
25
|
+
|
|
26
|
+
### What Makes This Hard
|
|
27
|
+
|
|
28
|
+
The naive fix (update planning docs) doesn't fix the hard error. The clean fix (engine aliases) requires touching the schema and storage layer. The middle path (stub file) is a patch that introduces a non-workflow workflow file into the bundle. A junior developer would update the docs and miss the live error path.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Philosophy Constraints
|
|
33
|
+
|
|
34
|
+
| Principle | Tension with this problem |
|
|
35
|
+
|---|---|
|
|
36
|
+
| **Structure earns its place** | A fix that prevents a real recurring failure mode earns its keep. A redirect stub and an alias mechanism both qualify. |
|
|
37
|
+
| **YAGNI with discipline** | Don't build a general alias mechanism for one confirmed rename. Stub is the YAGNI-compatible fix; engine alias is premature generalization. |
|
|
38
|
+
| **Architectural fixes over patches** | Alias mechanism is the architecturally correct fix. Stub is an explicit patch. This principle and YAGNI are in direct conflict here. |
|
|
39
|
+
| **Errors are data** | `WorkflowNotFoundError` is a hard -32001 error. Leaving it live is accepting a known broken path. |
|
|
40
|
+
| **Validate at boundaries, trust inside** | The trigger system validates workflow IDs at startup for this reason. The `start_workflow` call is a boundary — errors there should be informative, not hard failures with no guidance. |
|
|
41
|
+
|
|
42
|
+
**Resolution:** YAGNI wins over "architectural fixes over patches" when one case is confirmed and the general case is speculative. Stub (Candidate B) is the right call for current evidence. Architectural fix (Candidate C) is right when a second rename is confirmed.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Impact Surface
|
|
47
|
+
|
|
48
|
+
**What must stay consistent if the boundary changes:**
|
|
49
|
+
|
|
50
|
+
- `bundled-workflow-smoke.test.ts` auto-walks all files in `workflows/` — a stub will be picked up and smoke-tested. It must pass schema validation and produce a valid first-step response.
|
|
51
|
+
- `validate:registry` script checks all bundled workflows. The stub must have a valid `id`, `name`, `version`, and `steps[1+]`.
|
|
52
|
+
- `list_workflows` MCP tool will surface the stub alongside real workflows. The stub name must clearly signal deprecation to avoid confusion.
|
|
53
|
+
- `docs/roadmap/open-work-inventory.md` references `exploration-workflow.json` in the modernization list — must be updated consistently with `now-next-later.md` and `tickets/next-up.md` to avoid contradictory state.
|
|
54
|
+
- Any daemon session that reads the roadmap before starting work will no longer be misled into trying to modernize a deleted file.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Candidates
|
|
59
|
+
|
|
60
|
+
### Candidate A: Update planning docs only
|
|
61
|
+
|
|
62
|
+
**Summary:** Update 3 stale planning docs; open 2 new GitHub issues (naming gap, codebase archaeology). No workflow files or source changes.
|
|
63
|
+
|
|
64
|
+
**Tensions resolved / accepted:**
|
|
65
|
+
- Resolves: T2 (planning docs)
|
|
66
|
+
- Accepts: T1 (hard `WorkflowNotFoundError` stays live), T3 (discoverability pain deferred)
|
|
67
|
+
|
|
68
|
+
**Boundary:** `docs/roadmap/` and `docs/tickets/` only.
|
|
69
|
+
|
|
70
|
+
**Why that boundary:** Minimal. Matches the completion-update pattern from AGENTS.md.
|
|
71
|
+
|
|
72
|
+
**Failure mode:** New issues never prioritized. Hard error persists indefinitely. Agents that read trigger system pattern (`trigger-listener.ts:293`) hit `WorkflowNotFoundError` with no guidance.
|
|
73
|
+
|
|
74
|
+
**Repo pattern:** Directly follows AGENTS.md completion-update pattern.
|
|
75
|
+
|
|
76
|
+
**Gains / losses:** Gain: zero blast radius, fast. Loss: hard `WorkflowNotFoundError` stays live; no fix for the error path.
|
|
77
|
+
|
|
78
|
+
**Scope judgment: Too narrow.** Team's own philosophy treats `workflow_not_found` as a serious failure mode (trigger system, error-handler.ts). Leaving it live while calling the work "done" contradicts that.
|
|
79
|
+
|
|
80
|
+
**Philosophy fit:** Honors YAGNI. Conflicts with "structure earns its place" and "errors are data."
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### Candidate B: Thin redirect stub + planning doc updates + new codebase-archaeology issue
|
|
85
|
+
|
|
86
|
+
**Summary:** Create `workflows/exploration-workflow.json` as a single-step redirect stub. Update 3 planning docs. Open one GitHub issue for codebase-archaeology scope.
|
|
87
|
+
|
|
88
|
+
**Stub specification:**
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"id": "exploration-workflow",
|
|
92
|
+
"name": "Exploration Workflow [deprecated — use wr.discovery]",
|
|
93
|
+
"version": "3.0.0",
|
|
94
|
+
"description": "This workflow ID has moved to wr.discovery. Start a new session with start_workflow wr.discovery instead.",
|
|
95
|
+
"steps": [{
|
|
96
|
+
"id": "redirect",
|
|
97
|
+
"prompt": "This workflow has been renamed. Please start a new session using start_workflow with id 'wr.discovery'. That workflow is the canonical successor to the exploration and design-thinking workflows and covers all the same use cases."
|
|
98
|
+
}]
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Uses only schema-declared fields (`id`, `name`, `version`, `description`, `steps`). No new fields. `additionalProperties: false` is satisfied. Single step satisfies `steps: { minItems: 1 }`.
|
|
103
|
+
|
|
104
|
+
**Tensions resolved / accepted:**
|
|
105
|
+
- Resolves: T1 (hard error fixed — `exploration-workflow` resolves to stub, step tells user to use `wr.discovery`)
|
|
106
|
+
- Resolves: T2 (planning docs updated)
|
|
107
|
+
- Accepts: T3 partially (codebase-archaeology deferred, but explicitly tracked as new issue)
|
|
108
|
+
|
|
109
|
+
**Boundary:** Workflow file layer — the minimum-surface boundary. Storage layer does file-based dispatch; adding a file is the natural extension point for this layer.
|
|
110
|
+
|
|
111
|
+
**Why that boundary is best fit:** Zero engine, zero schema, zero source changes. The problem lives at the workflow-ID resolution boundary; the fix lives at the same boundary.
|
|
112
|
+
|
|
113
|
+
**Failure mode:** Stub appears in `list_workflows` output alongside real workflows. Users may be confused about two discovery-related entries. The stub's name and description must be explicit enough to avoid this. If `bundled-workflow-smoke.test.ts` runs the stub and checks for substantive output, it may fail quality gates.
|
|
114
|
+
|
|
115
|
+
**Repo pattern:** Adapts existing pattern (thin-purpose workflow files like `test-session-persistence.json` already exist in the bundle). Redirect-only is a new usage but not architecturally alien.
|
|
116
|
+
|
|
117
|
+
**Gains / losses:** Gain: hard `WorkflowNotFoundError` fixed immediately; zero source changes; stub is reversible in one commit when engine aliases ship. Loss: non-workflow file in the workflow bundle; potential `list_workflows` confusion; stub accumulates debt if not eventually replaced.
|
|
118
|
+
|
|
119
|
+
**Scope judgment: Best-fit.** Fixes the real failure mode (hard error) at the minimum surface. Remaining open question explicitly scoped.
|
|
120
|
+
|
|
121
|
+
**Philosophy fit:** Honors "structure earns its place" (prevents real recurring failure), YAGNI (minimal stub only), "validate at boundaries" (error caught at `start_workflow` boundary). Explicit conflict: "architectural fixes over patches" — the stub is a patch. Accepted tradeoff.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### Candidate C: Engine alias feature
|
|
126
|
+
|
|
127
|
+
**Summary:** Add `aliases?: readonly string[]` to `spec/workflow.schema.json`; extend `getWorkflowById` in `src/infrastructure/storage/file-workflow-storage.ts` to build an alias-to-canonical-id map during index construction and try secondary alias lookup on not-found; populate `wr.discovery.json` with `aliases: ["exploration-workflow", "design-thinking-workflow"]`. Update 3 planning docs.
|
|
128
|
+
|
|
129
|
+
**Feature specification:**
|
|
130
|
+
- Schema change: `"aliases": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }` added to `spec/workflow.schema.json` as an optional property.
|
|
131
|
+
- Storage change: In `getWorkflowIndex()` (`file-workflow-storage.ts`), two-pass index build: first pass builds primary-id index, second pass adds `aliasId → canonicalId` mappings. In `getWorkflowById`, when `index.get(safeId)` returns null, try `aliasIndex.get(safeId)` to get the canonical ID, then retry.
|
|
132
|
+
- Alias uniqueness enforcement: Schema validator rejects workflows where an alias collides with any other workflow's primary ID or alias.
|
|
133
|
+
- Workflow file change: `wr.discovery.json` declares `"aliases": ["exploration-workflow", "design-thinking-workflow"]`.
|
|
134
|
+
- No `src/v2/durable-core/` changes required — alias resolution happens at the storage/registry layer.
|
|
135
|
+
|
|
136
|
+
**Tensions resolved / accepted:**
|
|
137
|
+
- Resolves: T1 (naming hard error fixed architecturally)
|
|
138
|
+
- Resolves: T3 (YAGNI pressure partially — alias mechanism is general, handles future renames)
|
|
139
|
+
- Resolves: T2 (planning docs updated)
|
|
140
|
+
- Accepts: T3 partially — codebase-archaeology still a separate open question
|
|
141
|
+
|
|
142
|
+
**Boundary:** Schema + storage layer. Additive change to both. No protected `src/v2/durable-core/` code touched.
|
|
143
|
+
|
|
144
|
+
**Why that boundary:** Architecturally correct. The alias mechanism belongs at the workflow registry/storage layer, not as a workflow file. This is where ID resolution happens.
|
|
145
|
+
|
|
146
|
+
**Failure mode:** Alias cycles (workflow A aliases to workflow B which aliases back to A) cause infinite loops or index build errors. Duplicate aliases across workflows create ambiguous resolution. Secondary-lookup latency adds to every not-found call. All addressable with defensive implementation — but requires more engineering discipline.
|
|
147
|
+
|
|
148
|
+
**Repo pattern:** Departs from existing patterns. No alias mechanism exists today. New tests needed. Schema validator must be extended.
|
|
149
|
+
|
|
150
|
+
**Gains / losses:** Gain: architecturally correct; no stub debt; `wr.discovery` self-documents its predecessors; generalizes to future renames. Loss: substantially larger scope; schema + storage + tests; requires new validator logic; slower to ship.
|
|
151
|
+
|
|
152
|
+
**Scope judgment: Too broad** for current evidence. One confirmed rename. No second rename in the backlog. YAGNI says don't build for a case that isn't confirmed.
|
|
153
|
+
|
|
154
|
+
**Philosophy fit:** Honors "architectural fixes over patches." Honors "structure earns its place." Conflicts with YAGNI — general feature for one case.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Comparison and Recommendation
|
|
159
|
+
|
|
160
|
+
### Tensions vs. candidates matrix
|
|
161
|
+
|
|
162
|
+
| Tension | A | B | C |
|
|
163
|
+
|---|---|---|---|
|
|
164
|
+
| T1: Naming hard error | ✗ | ✓ | ✓ |
|
|
165
|
+
| T2: Planning doc currency | ✓ | ✓ | ✓ |
|
|
166
|
+
| T3: YAGNI vs. discoverability | ✓ | ~ | ✗ |
|
|
167
|
+
|
|
168
|
+
### Decision criteria verdict
|
|
169
|
+
|
|
170
|
+
| Criterion | A | B | C |
|
|
171
|
+
|---|---|---|---|
|
|
172
|
+
| Resolve roadmap item without losing intent | ✓ | ✓ | ✓ |
|
|
173
|
+
| Address naming/discoverability gap | ✗ | ✓ | ✓ |
|
|
174
|
+
| Scope new work distinctly | ~ | ✓ | ~ |
|
|
175
|
+
| Actionable without unavailable session analytics | ✓ | ✓ | ✓ |
|
|
176
|
+
|
|
177
|
+
### Recommendation: **Candidate B**
|
|
178
|
+
|
|
179
|
+
B is the only candidate that satisfies all 4 decision criteria. A is strictly dominated by B (same doc fix, doesn't fix hard error). C is premature generalization for one confirmed case.
|
|
180
|
+
|
|
181
|
+
**Core rationale:** One deleted workflow causing one hard error. Fix: one file. The storage layer's file-based dispatch makes "add a file" the natural, minimum extension point. Zero engine changes. The stub is reversible in one commit when Candidate C is eventually justified.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Self-Critique
|
|
186
|
+
|
|
187
|
+
**Strongest argument against Candidate B:** The stub is explicit technical debt. `list_workflows` will return two discovery-related entries and users may be confused. The stub's step prompt tells users to restart their session entirely — a friction point. "Architectural fixes over patches" is a stated first-class principle, and a stub is an explicit patch.
|
|
188
|
+
|
|
189
|
+
**Why Candidate A lost:** Strictly dominated by B. Same planning doc fix, doesn't fix the hard error. The additional work for B is one stub file. No scenario where A is better than B.
|
|
190
|
+
|
|
191
|
+
**What would justify Candidate C:** (1) A second workflow consolidation creates another `WorkflowNotFoundError`; OR (2) the project owner explicitly decides alias support is a product feature worth shipping; OR (3) the stub causes confirmed user confusion in `list_workflows` and a `hidden`/`deprecated` schema field would be needed anyway (at which point the schema is already being changed, so adding `aliases` is marginal cost).
|
|
192
|
+
|
|
193
|
+
**Assumption that invalidates Candidate B:** If `bundled-workflow-smoke.test.ts` enforces that all workflow files must produce substantive exploration outputs (not redirect-only prompts), the stub fails quality gates. The smoke test auto-walks all files in `workflows/`. If it checks output quality, not just schema validity, the stub fails. Risk assessment: low — the smoke test is a structural validation test, not an output-quality test — but verify before shipping.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Open Questions for the Main Agent
|
|
198
|
+
|
|
199
|
+
1. **Smoke test compatibility:** Does `tests/lifecycle/bundled-workflow-smoke.test.ts` check only schema validity and step reachability, or does it also assert on output quality? The stub passes the former; may fail the latter.
|
|
200
|
+
|
|
201
|
+
2. **list_workflows filtering:** Is there any existing mechanism to mark a workflow as deprecated or hidden from `list_workflows` output? If not, is the stub's explicit `[deprecated]` naming suffix sufficient?
|
|
202
|
+
|
|
203
|
+
3. **Project owner intent:** The consolidation commit (`a0ddaaac`) was AI-authored (Firebender co-author). The metaGuidance "canonical successor" claim was written by the same session. Does the project owner independently confirm that wr.discovery fully covers the exploration use case, or should a human review be requested before closing this item?
|
|
204
|
+
|
|
205
|
+
4. **Codebase-archaeology gap:** Is there evidence in session history that users invoke wr.discovery for "explore this codebase" tasks and find it mis-shaped? This would change the recommendation from "open a new issue" to "create `wr.explore.json` now."
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Design Review Findings: exploration-workflow.json Gap
|
|
2
|
+
|
|
3
|
+
*Human-facing artifact. Durable truth is in step notes and context variables.*
|
|
4
|
+
|
|
5
|
+
*Optimized for main-agent synthesis — concise and actionable.*
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Selected Direction
|
|
10
|
+
|
|
11
|
+
**Candidate B (hybrid variant):** Thin redirect stub at `workflows/exploration-workflow.json` + update `wr.discovery.json` description to name predecessors + update 3 planning docs + open 1 GitHub issue for codebase-archaeology scope.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Tradeoff Review
|
|
16
|
+
|
|
17
|
+
| Tradeoff | Status | Condition for failure |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Stub is a patch not an architectural fix | Acceptable | Escalate to engine alias (Candidate C) on second workflow rename |
|
|
20
|
+
| Stub appears in list_workflows alongside wr.discovery | Acceptable | Monitor for user confusion; fix by name if needed |
|
|
21
|
+
| Codebase-archaeology gap deferred to GitHub issue | Acceptable conditional | Stub prompt MUST explicitly distinguish problem-space exploration from codebase archaeology |
|
|
22
|
+
|
|
23
|
+
All three tradeoffs are acceptable under current conditions. No invariant is violated.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Failure Mode Review
|
|
28
|
+
|
|
29
|
+
| Failure Mode | Severity | Coverage | Missing Mitigation |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| Stub accumulates indefinitely, alias feature never ships | LOW-MEDIUM | Partially handled | Add technical debt note to stub `description`; GitHub issue should note Candidate C pivot condition |
|
|
32
|
+
| Stub prompt too vague, misleads agents about codebase-archaeology scope | **MEDIUM — HIGHEST RISK** | Handled only if prompt written as specified | None beyond correct prompt authoring |
|
|
33
|
+
| list_workflows confusion between stub and wr.discovery | LOW | Handled by stub name with `[deprecated]` | None required without evidence |
|
|
34
|
+
|
|
35
|
+
**Most dangerous:** FM2 (vague prompt). Only silent failure mode — agents won't error, they'll silently use wr.discovery for the wrong job. The required stub prompt is the mitigation; it is non-optional.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Runner-Up / Simpler Alternative Review
|
|
40
|
+
|
|
41
|
+
**Runner-up (Candidate C — engine alias):** One element worth borrowing: update `wr.discovery.json`'s description to name predecessor IDs. Zero schema changes. One sentence. Survives stub deletion. Adopted into the hybrid.
|
|
42
|
+
|
|
43
|
+
**Simpler variant (no stub file):** Fails decision criterion 2. `WorkflowNotFoundError` fires before any description is visible. The stub is required.
|
|
44
|
+
|
|
45
|
+
**Hybrid verdict:** Stub + wr.discovery description update is strictly better than stub alone. Adopted.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Philosophy Alignment
|
|
50
|
+
|
|
51
|
+
| Principle | Status |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Structure earns its place | ✓ Satisfied — stub prevents real recurring failure |
|
|
54
|
+
| YAGNI with discipline | ✓ Satisfied — no speculative abstractions |
|
|
55
|
+
| Errors are data | ✓ Satisfied — hard error path actively fixed |
|
|
56
|
+
| Observability as a constraint | ✓ Satisfied — three visibility improvements |
|
|
57
|
+
| Document "why" not "what" | ✓ Satisfied — stub prompt and description document rationale |
|
|
58
|
+
| Architectural fixes over patches | ~ Tension — stub is a patch; accepted because YAGNI counterbalances for one case |
|
|
59
|
+
| Make illegal states unrepresentable | ~ Informational — `deprecated: true` field would be cleaner but requires schema change; name-based convention is sufficient |
|
|
60
|
+
|
|
61
|
+
No principle is violated in a way that creates correctness or reliability risk.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Findings
|
|
66
|
+
|
|
67
|
+
### ORANGE: Stub spec was missing required `title` field on step
|
|
68
|
+
|
|
69
|
+
**Discovery:** `standardStep` schema requires `id` AND `title` (not just `id`). The spec in `design-candidates.md` omitted `title` on the step definition. A stub built from that spec would fail schema validation and the smoke test.
|
|
70
|
+
|
|
71
|
+
**Resolution:** Corrected spec (see Recommended Revisions). Add `"title": "This workflow has moved"` to the step. Verified against schema.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### ORANGE: Stub prompt content is non-optional — vague prompt creates silent misleading guidance
|
|
76
|
+
|
|
77
|
+
**Discovery:** If the stub's step prompt is simplified to "use wr.discovery instead," it encodes a false assumption: that wr.discovery covers codebase archaeology. This is the highest-risk failure mode.
|
|
78
|
+
|
|
79
|
+
**Resolution:** The stub's step prompt is a hard deliverable requirement. See Recommended Revisions for exact required wording.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### YELLOW: Candidate C pivot condition must be explicitly tracked
|
|
84
|
+
|
|
85
|
+
**Discovery:** The stub creates technical debt that should trigger Candidate C (engine alias feature) when a second workflow rename is confirmed. Without an explicit tracking mechanism, the debt persists invisibly.
|
|
86
|
+
|
|
87
|
+
**Resolution:** (1) Add a note to stub's `description` field. (2) Include Candidate C pivot condition in the GitHub issue for codebase-archaeology scope.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### YELLOW: wr.discovery description update not yet applied
|
|
92
|
+
|
|
93
|
+
**Discovery:** The hybrid design requires updating `wr.discovery.json`'s description to name predecessor IDs. This was added in the hybrid review but is not yet reflected in any existing doc.
|
|
94
|
+
|
|
95
|
+
**Resolution:** See Recommended Revisions.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Recommended Revisions
|
|
100
|
+
|
|
101
|
+
### Revision 1 — Corrected stub specification (REQUIRED)
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"id": "exploration-workflow",
|
|
106
|
+
"name": "Exploration Workflow [deprecated — use wr.discovery]",
|
|
107
|
+
"version": "3.0.0",
|
|
108
|
+
"description": "This workflow ID has moved. The exploration and design-thinking workflows were consolidated into wr.discovery (Discovery Workflow) in March 2026. Temporary redirect stub — replace with engine-level alias support if a second workflow rename creates another WorkflowNotFoundError.",
|
|
109
|
+
"steps": [
|
|
110
|
+
{
|
|
111
|
+
"id": "redirect",
|
|
112
|
+
"title": "This workflow has moved",
|
|
113
|
+
"prompt": "This workflow has been renamed and restructured as 'wr.discovery' (Discovery Workflow).\n\nFor problem-space exploration, decision-making, design thinking, or comparing approaches to an ambiguous problem: start a new session with start_workflow using id 'wr.discovery'.\n\nImportant distinction: if your goal is to systematically map or understand an unfamiliar codebase — tracing architecture, call stacks, data flows, or module structure — that is a different job from problem-space exploration. No dedicated bundled workflow covers it yet. Use your standard code investigation tools (Grep, Read, Bash) directly for that task."
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
All required fields satisfied: `id` (≥3 chars), `name` (≥1), `description` (≥1), `version`, `steps` (minItems: 1). Step has `id` and `title`. Prompt ≤8192 chars.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### Revision 2 — wr.discovery description update (REQUIRED for hybrid)
|
|
124
|
+
|
|
125
|
+
Current `description`: `"Use this to explore and think through a problem end-to-end. Moves between landscape exploration, problem framing, candidate generation, adversarial challenge, and uncertainty resolution."`
|
|
126
|
+
|
|
127
|
+
Proposed: `"Use this to explore and think through a problem end-to-end. Moves between landscape exploration, problem framing, candidate generation, adversarial challenge, and uncertainty resolution. This is the canonical successor to exploration-workflow and design-thinking-workflow — if you are looking for either of those by name, this is the right workflow."`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Revision 3 — Planning doc updates (REQUIRED)
|
|
132
|
+
|
|
133
|
+
Three files need updating:
|
|
134
|
+
|
|
135
|
+
**`docs/roadmap/now-next-later.md` (Next section):**
|
|
136
|
+
Remove: `exploration-workflow.json is the highest-priority candidate.`
|
|
137
|
+
Replace with: `exploration-workflow.json was superseded by wr.discovery (see open-work-inventory.md). The remaining open question is whether a codebase-archaeology workflow (wr.explore or equivalent) is warranted — tracked as a separate GitHub issue.`
|
|
138
|
+
|
|
139
|
+
**`docs/roadmap/open-work-inventory.md` (Legacy workflow modernization section):**
|
|
140
|
+
Update status to: complete for exploration-workflow (superseded by wr.discovery v3.2.0, redirect stub added). Open: mr-review-workflow.json, bug-investigation.json remain on the modernization list.
|
|
141
|
+
|
|
142
|
+
**`docs/tickets/next-up.md` (Ticket 2):**
|
|
143
|
+
Replace "Legacy workflow modernization -- exploration-workflow.json" with a note: superseded. Point to the new GitHub issue for codebase-archaeology scope.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### Revision 4 — New GitHub issue (REQUIRED)
|
|
148
|
+
|
|
149
|
+
Title: `feat(workflows): decide whether codebase-archaeology exploration warrants a new wr.explore workflow`
|
|
150
|
+
|
|
151
|
+
Body should include:
|
|
152
|
+
- Problem: wr.discovery is shaped for problem-space exploration and strategic decision-making. Systematic codebase mapping (architecture understanding, call stack tracing) is a different job with different tools.
|
|
153
|
+
- Evidence gap: no session data confirming this gap is real vs. hypothetical.
|
|
154
|
+
- Acceptance criteria: investigate whether wr.discovery is being used for codebase-archaeology tasks and producing mis-shaped outputs; if confirmed, create wr.explore.json with explicit scope.
|
|
155
|
+
- Pivot condition for Candidate C: if a second workflow rename creates another WorkflowNotFoundError, escalate this issue to include engine alias support (adds `aliases` field to schema and secondary lookup to file-workflow-storage.ts).
|
|
156
|
+
- Labels: `feature`, `next`
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Residual Concerns
|
|
161
|
+
|
|
162
|
+
1. **The consolidation commit was AI-authored (Firebender).** The metaGuidance "canonical successor" claim was written by an AI session, not explicitly confirmed by the project owner. Before closing the roadmap item, it would be good to have the project owner verify that wr.discovery fully covers their intended exploration use case. This is a human-gate, not a technical concern.
|
|
163
|
+
|
|
164
|
+
2. **Codebase-archaeology use case is unverified.** No session data is available to confirm whether this gap causes real-world pain. The GitHub issue captures it, but without evidence it may never be prioritized. Acceptable — it's explicitly tracked rather than silently ignored.
|
|
165
|
+
|
|
166
|
+
3. **Stub name contains square brackets.** `[deprecated — use wr.discovery]` in the workflow name is a convention, not a schema-enforced status. If the project later adds a `deprecated: boolean` field to the schema, this convention should be replaced. Low priority.
|