@shenlee/devcrew 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/packages/adapters/src/index.js +147 -6
- package/dist/packages/core/src/active-run.js +13 -1
- package/dist/packages/core/src/config.js +87 -16
- package/dist/packages/core/src/index.js +1 -0
- package/dist/packages/core/src/lock.js +89 -0
- package/dist/packages/core/src/paths.js +3 -0
- package/dist/packages/core/src/store.js +10 -0
- package/dist/packages/core/src/version.js +1 -1
- package/dist/packages/core/src/workflow.js +20 -3
- package/dist/packages/orchestrator/src/index.js +100 -16
- package/dist/packages/service/src/tools.js +79 -18
- package/docs/claude-code.md +14 -1
- package/docs/codex.md +15 -2
- package/docs/quickstart.md +1 -1
- package/docs/superpowers/plans/2026-07-14-p0-remediation.md +213 -0
- package/docs/superpowers/plans/2026-07-14-reliability-recovery.md +208 -0
- package/docs/superpowers/plans/2026-07-14-structured-role-results.md +231 -0
- package/docs/superpowers/specs/2026-07-14-p0-remediation-design.md +52 -0
- package/docs/superpowers/specs/2026-07-14-reliability-recovery-design.md +92 -0
- package/docs/superpowers/specs/2026-07-14-structured-role-results-design.md +96 -0
- package/docs/workflow.md +11 -2
- package/package.json +1 -1
- package/packages/adapters/src/index.ts +168 -6
- package/packages/core/src/active-run.ts +13 -1
- package/packages/core/src/config.ts +96 -17
- package/packages/core/src/index.ts +1 -0
- package/packages/core/src/lock.ts +104 -0
- package/packages/core/src/paths.ts +4 -0
- package/packages/core/src/store.ts +12 -1
- package/packages/core/src/types.ts +47 -1
- package/packages/core/src/version.ts +1 -1
- package/packages/core/src/workflow.ts +22 -3
- package/packages/orchestrator/src/index.ts +109 -17
- package/packages/service/src/tools.ts +86 -16
- package/plugins/devcrew-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/devcrew-codex/.mcp.json +1 -1
- package/scripts/smoke-codex-plugin.mjs +1 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Configuration Integrity and Run Recovery Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Reject unsafe configuration, prevent concurrent MCP mutations from overwriting one another, and make interrupted runs explicitly abortable and recoverable.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A strict version-1 parser validates `.devcrew/config.json` and keeps artifact paths inside the requester repository. A repository-wide atomic directory lock wraps MCP mutations while read-only calls remain available. Aborting is a persisted terminal state that preserves audit data; recovery only clears a confirmed stale lock and retries cleanup of a terminal run's residual worktree.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, Node.js `fs/promises`, Node test runner, Git worktrees, MCP tool registry.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Validate configuration and artifact paths
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `packages/core/src/config.ts`
|
|
17
|
+
- Modify: `tests/core.test.ts`
|
|
18
|
+
|
|
19
|
+
- [ ] **Step 1: Write failing configuration tests**
|
|
20
|
+
|
|
21
|
+
Add table-driven tests that write `.devcrew/config.json`, then call
|
|
22
|
+
`startWorkflow`. Cover an unknown top-level key, an unknown workflow key, an
|
|
23
|
+
unknown gate, a duplicate gate, an empty command, an absolute
|
|
24
|
+
`artifactDirectory`, and `../outside`. Each test asserts the error identifies
|
|
25
|
+
the invalid field. Add a valid custom artifact directory assertion so the
|
|
26
|
+
existing supported path remains accepted.
|
|
27
|
+
|
|
28
|
+
- [ ] **Step 2: Verify RED**
|
|
29
|
+
|
|
30
|
+
Run:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node --import tsx --test tests/core.test.ts
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Expected: new invalid-config cases fail because `readConfig` currently merges
|
|
37
|
+
untyped JSON and `artifactDirectory` is only joined to `cwd`.
|
|
38
|
+
|
|
39
|
+
- [ ] **Step 3: Implement a strict version-1 parser**
|
|
40
|
+
|
|
41
|
+
In `config.ts`, parse JSON as `unknown`, require plain objects, reject keys
|
|
42
|
+
outside the documented schema, validate enum values from exported type
|
|
43
|
+
constants, and validate arrays as non-empty trimmed strings with no invalid
|
|
44
|
+
gate entries or duplicates. Resolve `workflow.artifactDirectory` against
|
|
45
|
+
`cwd`; reject absolute values and any resolved path whose relative path leaves
|
|
46
|
+
the repository. Return the normalized validated `DevCrewConfig`.
|
|
47
|
+
|
|
48
|
+
- [ ] **Step 4: Verify GREEN and commit**
|
|
49
|
+
|
|
50
|
+
Run:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node --import tsx --test tests/core.test.ts
|
|
54
|
+
git add packages/core/src/config.ts tests/core.test.ts
|
|
55
|
+
git commit -m "fix: validate DevCrew configuration strictly"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Expected: all core tests pass and invalid configuration fails before a run is
|
|
59
|
+
created.
|
|
60
|
+
|
|
61
|
+
### Task 2: Add explicit repository mutation locks
|
|
62
|
+
|
|
63
|
+
**Files:**
|
|
64
|
+
- Create: `packages/core/src/lock.ts`
|
|
65
|
+
- Modify: `packages/core/src/paths.ts`
|
|
66
|
+
- Modify: `packages/core/src/index.ts`
|
|
67
|
+
- Modify: `packages/service/src/tools.ts`
|
|
68
|
+
- Modify: `tests/core.test.ts`
|
|
69
|
+
- Modify: `tests/service.test.ts`
|
|
70
|
+
|
|
71
|
+
- [ ] **Step 1: Write failing lock tests**
|
|
72
|
+
|
|
73
|
+
Add core tests that hold a repository lock while a second acquisition rejects
|
|
74
|
+
with a busy error, then release the first lock and acquire again. Create a
|
|
75
|
+
lock directory with metadata containing a non-running pid and assert normal
|
|
76
|
+
acquisition still refuses it while explicit stale-lock recovery removes it.
|
|
77
|
+
Add a service test that holds the lock, verifies `devcrew_status` still works,
|
|
78
|
+
and verifies a mutating `devcrew_approve` returns a structured busy error.
|
|
79
|
+
|
|
80
|
+
- [ ] **Step 2: Verify RED**
|
|
81
|
+
|
|
82
|
+
Run:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node --import tsx --test tests/core.test.ts tests/service.test.ts
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Expected: imports fail because no repository lock API exists and MCP calls do
|
|
89
|
+
not serialize mutations.
|
|
90
|
+
|
|
91
|
+
- [ ] **Step 3: Implement lock primitives**
|
|
92
|
+
|
|
93
|
+
Add `repositoryLockPath`, then implement `withRepositoryLock(cwd, action)`
|
|
94
|
+
using atomic `mkdir`. Store `{ ownerId, pid, createdAt }` in `lock.json`,
|
|
95
|
+
remove only the held owner directory in `finally`, and reject existing locks
|
|
96
|
+
without waiting. Implement `recoverRepositoryLock(cwd)` to remove only a
|
|
97
|
+
missing/invalid/dead-local-process lock; it must reject a live owner and must
|
|
98
|
+
never run automatically.
|
|
99
|
+
|
|
100
|
+
- [ ] **Step 4: Wrap MCP mutation dispatch**
|
|
101
|
+
|
|
102
|
+
In `callDevCrewTool`, route start, answer, approve, reject, continue,
|
|
103
|
+
complete-execution, waive-verification, abort, and recovery cleanup through a
|
|
104
|
+
single lock wrapper. Resolve an omitted run id inside the lock. Keep status and
|
|
105
|
+
artifact calls unlocked. Ensure start creates its run and sets `active-run`
|
|
106
|
+
under one lock.
|
|
107
|
+
|
|
108
|
+
- [ ] **Step 5: Verify GREEN and commit**
|
|
109
|
+
|
|
110
|
+
Run:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
node --import tsx --test tests/core.test.ts tests/service.test.ts
|
|
114
|
+
git add packages/core/src/lock.ts packages/core/src/paths.ts packages/core/src/index.ts packages/service/src/tools.ts tests/core.test.ts tests/service.test.ts
|
|
115
|
+
git commit -m "fix: serialize DevCrew MCP mutations"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Expected: concurrent writes return a structured busy error and reads remain
|
|
119
|
+
available.
|
|
120
|
+
|
|
121
|
+
### Task 3: Persist terminal aborts and recover residual worktrees
|
|
122
|
+
|
|
123
|
+
**Files:**
|
|
124
|
+
- Modify: `packages/core/src/types.ts`
|
|
125
|
+
- Modify: `packages/core/src/active-run.ts`
|
|
126
|
+
- Modify: `packages/core/src/workflow.ts`
|
|
127
|
+
- Modify: `packages/orchestrator/src/index.ts`
|
|
128
|
+
- Modify: `packages/service/src/tools.ts`
|
|
129
|
+
- Modify: `docs/workflow.md`
|
|
130
|
+
- Modify: `tests/core.test.ts`
|
|
131
|
+
- Modify: `tests/orchestrator.test.ts`
|
|
132
|
+
- Modify: `tests/service.test.ts`
|
|
133
|
+
|
|
134
|
+
- [ ] **Step 1: Write failing abort and recovery tests**
|
|
135
|
+
|
|
136
|
+
Add a workflow test that aborts a nonterminal state with a reason, asserts
|
|
137
|
+
`status: "aborted"`, persisted reason/timestamp, and that continue/answer/
|
|
138
|
+
complete cannot resume it. Add an apply-worktree test that aborts after
|
|
139
|
+
execution, confirms the requester checkout is unchanged and the isolated
|
|
140
|
+
worktree is removed. Add a service test that `devcrew_abort` clears its active
|
|
141
|
+
run and that a repeated abort is idempotent. Add a recovery test for an
|
|
142
|
+
aborted state retaining a workspace reference: recovery cleans that worktree
|
|
143
|
+
and clears the reference without running a role.
|
|
144
|
+
|
|
145
|
+
- [ ] **Step 2: Verify RED**
|
|
146
|
+
|
|
147
|
+
Run:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
node --import tsx --test tests/core.test.ts tests/orchestrator.test.ts tests/service.test.ts
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Expected: tests fail because `aborted` is not a run status and no abort or
|
|
154
|
+
recovery tool exists.
|
|
155
|
+
|
|
156
|
+
- [ ] **Step 3: Implement terminal abort state**
|
|
157
|
+
|
|
158
|
+
Add `RunAbort` plus `RunStatus: "aborted"`; persist the first non-empty abort
|
|
159
|
+
reason and timestamp. Add `abortWorkflow` with terminal idempotency, and make
|
|
160
|
+
continuation return aborted runs unchanged. Add
|
|
161
|
+
`clearActiveRunIfMatches(cwd, runId)` so an abort never clears another run's
|
|
162
|
+
active pointer.
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 4: Implement orchestration and MCP lifecycle tools**
|
|
165
|
+
|
|
166
|
+
Add `abortOrchestratedWorkflow` to persist abort audit state before attempting
|
|
167
|
+
worktree cleanup. On cleanup success clear `executionWorkspace`; on failure
|
|
168
|
+
leave it for recovery. Add `recoverOrchestratedWorkflow` that accepts only
|
|
169
|
+
terminal runs and retries this cleanup. Register `devcrew_abort` and
|
|
170
|
+
`devcrew_recover`, with schemas and structured errors, and apply the lock
|
|
171
|
+
rules from Task 2. Update `docs/workflow.md` with abort/recover semantics.
|
|
172
|
+
|
|
173
|
+
- [ ] **Step 5: Verify GREEN and commit**
|
|
174
|
+
|
|
175
|
+
Run:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
node --import tsx --test tests/core.test.ts tests/orchestrator.test.ts tests/service.test.ts
|
|
179
|
+
git add packages/core/src/types.ts packages/core/src/active-run.ts packages/core/src/workflow.ts packages/orchestrator/src/index.ts packages/service/src/tools.ts docs/workflow.md tests/core.test.ts tests/orchestrator.test.ts tests/service.test.ts
|
|
180
|
+
git commit -m "feat: add DevCrew abort and recovery controls"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Expected: aborted runs retain audit evidence, cannot resume, clean their
|
|
184
|
+
worktrees when possible, and can explicitly retry residual cleanup.
|
|
185
|
+
|
|
186
|
+
### Task 4: Full verification
|
|
187
|
+
|
|
188
|
+
**Files:**
|
|
189
|
+
- Verify: all modified files
|
|
190
|
+
|
|
191
|
+
- [ ] **Step 1: Run repository validation**
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm run validate
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Expected: TypeScript build succeeds and every test passes.
|
|
198
|
+
|
|
199
|
+
- [ ] **Step 2: Inspect final state**
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git diff main...HEAD --check
|
|
203
|
+
git status --short
|
|
204
|
+
git log --oneline main..HEAD
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Expected: no whitespace errors, clean worktree, and only the design, plan,
|
|
208
|
+
and implementation commits on the branch.
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Structured Role Results Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Accept validated, versioned role-result envelopes while preserving Markdown-only SDK outputs and expose structured evidence through run state, artifacts, and MCP.
|
|
6
|
+
|
|
7
|
+
**Architecture:** The core package owns envelope types and state migration. The adapter recognizes one explicitly marked JSON block, validates role-specific data, and returns Markdown with the protocol block removed. The orchestrator uses the normalized result without changing gates; artifacts render a human-readable appendix, and MCP continues returning the persisted state.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript strict mode, Node.js built-in test runner, `tsx`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Define normalized envelope types and old-state migration
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `packages/core/src/types.ts:91-100`
|
|
17
|
+
- Modify: `packages/core/src/store.ts:24-70`
|
|
18
|
+
- Test: `tests/core.test.ts`
|
|
19
|
+
|
|
20
|
+
- [ ] **Step 1: Write the failing migration test**
|
|
21
|
+
|
|
22
|
+
Append this test to `tests/core.test.ts`:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
test("loadState marks pre-envelope role results as legacy", async () => {
|
|
26
|
+
const cwd = await tempProject();
|
|
27
|
+
const started = await startWorkflow({ cwd, host: "codex", mode: "feature", request: "Migrate roles" });
|
|
28
|
+
const path = runDir(cwd, started.runId);
|
|
29
|
+
const raw = JSON.parse(await readFile(join(path, "state.json"), "utf8")) as Record<string, unknown>;
|
|
30
|
+
raw.roles = [{ role: "pm", backend: "codex", summary: "old", markdown: "# Old", usedFallback: false }];
|
|
31
|
+
await writeFile(join(path, "state.json"), JSON.stringify(raw));
|
|
32
|
+
|
|
33
|
+
const role = (await loadState(cwd, started.runId)).roles[0] as unknown as { format?: string };
|
|
34
|
+
assert.equal(role.format, "legacy");
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- [ ] **Step 2: Verify it fails**
|
|
39
|
+
|
|
40
|
+
Run: `npm test -- --test-name-pattern="pre-envelope role"`
|
|
41
|
+
|
|
42
|
+
Expected: FAIL because `RoleResult.format` does not exist.
|
|
43
|
+
|
|
44
|
+
- [ ] **Step 3: Add the smallest normalized types and migration**
|
|
45
|
+
|
|
46
|
+
Add these exports in `packages/core/src/types.ts` and make `format` mandatory on `RoleResult`:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export interface CommandEvidence {
|
|
50
|
+
command: string;
|
|
51
|
+
exitCode: number;
|
|
52
|
+
output?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface RoleQuestion {
|
|
56
|
+
id: string;
|
|
57
|
+
prompt: string;
|
|
58
|
+
context?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface StructuredRoleData {
|
|
62
|
+
schemaVersion: 1;
|
|
63
|
+
role: Exclude<RoleResult["role"], "conductor">;
|
|
64
|
+
summary: string;
|
|
65
|
+
risks: string[];
|
|
66
|
+
evidence: CommandEvidence[];
|
|
67
|
+
questions?: RoleQuestion[];
|
|
68
|
+
decisions?: string[];
|
|
69
|
+
reviewDecision?: ArchitectureReviewDecision;
|
|
70
|
+
changedFiles?: string[];
|
|
71
|
+
testCases?: Array<{ id: string; scenario: string; type: "happy" | "edge" | "failure" | "regression"; expected: string }>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
In `loadState`, map each persisted role missing a valid `format` to `{ ...role, format: "legacy" }`, leaving all prior persisted fields intact.
|
|
76
|
+
|
|
77
|
+
- [ ] **Step 4: Verify it passes**
|
|
78
|
+
|
|
79
|
+
Run: `npm test -- --test-name-pattern="pre-envelope role"`
|
|
80
|
+
|
|
81
|
+
Expected: PASS.
|
|
82
|
+
|
|
83
|
+
- [ ] **Step 5: Commit**
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git add packages/core/src/types.ts packages/core/src/store.ts tests/core.test.ts
|
|
87
|
+
git commit -m "feat: normalize persisted role results"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Task 2: Parse and validate the marked envelope in adapters
|
|
91
|
+
|
|
92
|
+
**Files:**
|
|
93
|
+
- Modify: `packages/adapters/src/index.ts:69-102,143-162,424-459`
|
|
94
|
+
- Test: `tests/adapters.test.ts`
|
|
95
|
+
|
|
96
|
+
- [ ] **Step 1: Write failing parser tests**
|
|
97
|
+
|
|
98
|
+
Export `parseRoleResultOutput` and add tests that call it with this valid PM output and each invalid variation:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const output = `<!-- devcrew-role-result -->
|
|
102
|
+
\`\`\`json
|
|
103
|
+
{"schemaVersion":1,"role":"pm","summary":"Need scope","risks":[],"evidence":[],"questions":[{"id":"format","prompt":"Which formats?"}]}
|
|
104
|
+
\`\`\`
|
|
105
|
+
# Requirements
|
|
106
|
+
|
|
107
|
+
## Functional Scope
|
|
108
|
+
`;
|
|
109
|
+
const result = parseRoleResultOutput("pm", "requirements", output);
|
|
110
|
+
assert.equal(result.format, "structured");
|
|
111
|
+
assert.deepEqual(result.questions, ["Which formats?"]);
|
|
112
|
+
assert.doesNotMatch(result.markdown, /devcrew-role-result/);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Add separate `assert.throws` tests for malformed marked JSON, two marked blocks, `schemaVersion: 2`, role mismatch, duplicate/blank PM question IDs, and a non-integer command exit code. Add a Markdown-only test proving `format === "legacy"` and existing PM/review parsing remain unchanged.
|
|
116
|
+
|
|
117
|
+
- [ ] **Step 2: Verify they fail**
|
|
118
|
+
|
|
119
|
+
Run: `npm test -- --test-name-pattern="role result output"`
|
|
120
|
+
|
|
121
|
+
Expected: FAIL because `parseRoleResultOutput` is not exported.
|
|
122
|
+
|
|
123
|
+
- [ ] **Step 3: Implement the protocol parser and prompt contract**
|
|
124
|
+
|
|
125
|
+
Implement a parser that finds exactly one `<!-- devcrew-role-result -->` followed by a `json` fenced block. When no marker exists, return the original Markdown and `format: "legacy"`; when a marker exists, reject malformed or invalid data. Require all common fields, role-matching field sets, unique PM question IDs, nonempty strings, and integer command exit codes. Return the Markdown with only the marked block removed.
|
|
126
|
+
|
|
127
|
+
Add this exact prompt guidance before `Required Sections`:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
"Return this protocol block first:",
|
|
131
|
+
"<!-- devcrew-role-result -->",
|
|
132
|
+
"```json",
|
|
133
|
+
"{\"schemaVersion\":1,\"role\":\"<current role>\",\"summary\":\"...\",\"risks\":[],\"evidence\":[]}",
|
|
134
|
+
"```",
|
|
135
|
+
"Then return the required Markdown H2 sections. Do not include a second marked result block.",
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Replace direct Markdown extraction in `runRole` with the parser's normalized result. For legacy output, keep `extractOpenQuestions` and `extractArchitectureReviewDecision`; for structured output, derive those compatibility fields from validated structured data.
|
|
139
|
+
|
|
140
|
+
- [ ] **Step 4: Verify adapter tests pass**
|
|
141
|
+
|
|
142
|
+
Run: `npm test -- tests/adapters.test.ts`
|
|
143
|
+
|
|
144
|
+
Expected: PASS.
|
|
145
|
+
|
|
146
|
+
- [ ] **Step 5: Commit**
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git add packages/adapters/src/index.ts tests/adapters.test.ts
|
|
150
|
+
git commit -m "feat: parse structured role result envelopes"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Task 3: Persist and render structured implementation and test evidence
|
|
154
|
+
|
|
155
|
+
**Files:**
|
|
156
|
+
- Modify: `packages/orchestrator/src/index.ts:317-344,456-481`
|
|
157
|
+
- Test: `tests/orchestrator.test.ts`
|
|
158
|
+
|
|
159
|
+
- [ ] **Step 1: Write failing workflow tests**
|
|
160
|
+
|
|
161
|
+
Add a structured PM runner result with a question and assert `awaiting_input`. Add an implementation result with `format: "structured"` and `structured.changedFiles` plus command evidence, then assert the persisted role retains them and `implementation-review.md` contains `## Structured Role Result` and the command/exit code. Add an analogous tester assertion for `test-report.md` risks and evidence.
|
|
162
|
+
|
|
163
|
+
- [ ] **Step 2: Verify they fail**
|
|
164
|
+
|
|
165
|
+
Run: `npm test -- --test-name-pattern="structured (PM|implementation|tester)"`
|
|
166
|
+
|
|
167
|
+
Expected: FAIL because artifacts do not render structured role data.
|
|
168
|
+
|
|
169
|
+
- [ ] **Step 3: Render the structured appendix without changing workflow evidence sources**
|
|
170
|
+
|
|
171
|
+
Add an orchestrator artifact helper used by `appendExecutionSections` that renders `state.roles.at(-1)?.structured` only when `format === "structured"`. It must show schema version, summary, changed files, command evidence, test cases, risks, and questions when present, escaping no data and preserving the role's Markdown body as the primary artifact.
|
|
172
|
+
|
|
173
|
+
Keep the existing captured Git diff and configured verification as authoritative promotion evidence. Structured implementation `changedFiles` and tester commands are supplemental role assertions, not a way to overwrite `state.changedFiles`, `state.implementationDiff`, or `state.verification`.
|
|
174
|
+
|
|
175
|
+
In the orchestrator, use structured PM question prompts for `pendingQuestions` and structured review decisions for `architectureReview`; preserve the existing compatibility fields so legacy runners and callers continue to work.
|
|
176
|
+
|
|
177
|
+
- [ ] **Step 4: Verify workflow tests pass**
|
|
178
|
+
|
|
179
|
+
Run: `npm test -- tests/orchestrator.test.ts`
|
|
180
|
+
|
|
181
|
+
Expected: PASS.
|
|
182
|
+
|
|
183
|
+
- [ ] **Step 5: Commit**
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git add packages/orchestrator/src/index.ts tests/orchestrator.test.ts
|
|
187
|
+
git commit -m "feat: render structured role evidence"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Task 4: Surface format and results through MCP and complete regression checks
|
|
191
|
+
|
|
192
|
+
**Files:**
|
|
193
|
+
- Modify: `packages/service/src/tools.ts:215-320`
|
|
194
|
+
- Test: `tests/service.test.ts`
|
|
195
|
+
- Modify: `docs/codex.md`
|
|
196
|
+
- Modify: `docs/claude-code.md`
|
|
197
|
+
|
|
198
|
+
- [ ] **Step 1: Write the failing MCP test**
|
|
199
|
+
|
|
200
|
+
Start a workflow with a structured PM runner, invoke `devcrew_status`, and assert its text includes `role_format=structured` plus `structuredContent.state.roles.at(-1).structured.questions[0].id === "format"`. The test must use `callDevCrewTool`, not inspect the state file directly.
|
|
201
|
+
|
|
202
|
+
- [ ] **Step 2: Verify it fails**
|
|
203
|
+
|
|
204
|
+
Run: `npm test -- --test-name-pattern="MCP.*structured role"`
|
|
205
|
+
|
|
206
|
+
Expected: FAIL until the state summary exposes the normalized role format.
|
|
207
|
+
|
|
208
|
+
- [ ] **Step 3: Keep MCP state safe and document the migration**
|
|
209
|
+
|
|
210
|
+
Return the existing state object unchanged except for its newly persisted normalized role fields; do not create a second incompatible tool response shape. Extend `summarizeState` with `role_format=<legacy|structured|none>`.
|
|
211
|
+
|
|
212
|
+
Document the marker, schema version, legacy fallback rule, and the rule that malformed marked output is rejected rather than downgraded in both host guides.
|
|
213
|
+
|
|
214
|
+
- [ ] **Step 4: Verify the focused service test passes**
|
|
215
|
+
|
|
216
|
+
Run: `npm test -- tests/service.test.ts`
|
|
217
|
+
|
|
218
|
+
Expected: PASS.
|
|
219
|
+
|
|
220
|
+
- [ ] **Step 5: Run complete validation and inspect the patch**
|
|
221
|
+
|
|
222
|
+
Run: `npm run validate && npm test && git diff main...HEAD --check`
|
|
223
|
+
|
|
224
|
+
Expected: validation and all tests pass; whitespace check prints no output.
|
|
225
|
+
|
|
226
|
+
- [ ] **Step 6: Commit**
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
git add packages/service/src/tools.ts tests/service.test.ts docs/codex.md docs/claude-code.md
|
|
230
|
+
git commit -m "docs: describe structured role result migration"
|
|
231
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# P0 Verification and Review Remediation Design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Close two apply-mode safety gaps: a run with no verification evidence must not
|
|
6
|
+
be promoted without an explicit waiver, and an architecture review that requires
|
|
7
|
+
changes must return to implementation before it can be reviewed again.
|
|
8
|
+
|
|
9
|
+
## Verification Gate Semantics
|
|
10
|
+
|
|
11
|
+
`passed` is the only verification status that opens the normal `testing` gate.
|
|
12
|
+
Both `failed` and `not_run` set the testing gate to `rejected` and move the run
|
|
13
|
+
to `awaiting_input` with evidence explaining the condition.
|
|
14
|
+
|
|
15
|
+
The existing `devcrew_waive_verification` operation is the sole exception path
|
|
16
|
+
for either `failed` or `not_run`. It requires a non-empty requester risk reason,
|
|
17
|
+
persists the waiver, and reopens the testing gate. Promotion additionally
|
|
18
|
+
defends this invariant: any status other than `passed` requires a recorded
|
|
19
|
+
waiver, even if an invalid state was written by an older client.
|
|
20
|
+
|
|
21
|
+
## Architecture Review Remediation
|
|
22
|
+
|
|
23
|
+
When the architect returns `changes_required`, DevCrew continues to record the
|
|
24
|
+
decision, artifact, and feedback, while keeping the isolated execution
|
|
25
|
+
worktree. The implementation-review gate becomes `rejected` and the run enters
|
|
26
|
+
`awaiting_input`.
|
|
27
|
+
|
|
28
|
+
After `devcrew_answer`, DevCrew records the response then transitions the same
|
|
29
|
+
run to `execution/ready`. It resets the implementation-review gate to
|
|
30
|
+
`not_started`; it does not run another architect review. A subsequent
|
|
31
|
+
`devcrew_continue` invokes the implementer in the existing worktree, captures a
|
|
32
|
+
new diff, and schedules a fresh architecture review. This mirrors the existing
|
|
33
|
+
testing-rejection remediation flow.
|
|
34
|
+
|
|
35
|
+
## Error Handling and Compatibility
|
|
36
|
+
|
|
37
|
+
Existing persisted runs remain readable. A legacy run with `not_run` cannot be
|
|
38
|
+
promoted unless it has a waiver. The waiver tool's error message and validation
|
|
39
|
+
are expanded from "failed verification" to "failed or missing verification".
|
|
40
|
+
No new MCP tool or public state field is required.
|
|
41
|
+
|
|
42
|
+
## Acceptance Tests
|
|
43
|
+
|
|
44
|
+
1. Apply-mode testing with no discovered or configured commands results in
|
|
45
|
+
`verificationStatus: not_run`, `testing: rejected`, and `awaiting_input`.
|
|
46
|
+
2. A non-empty waiver can reopen that `not_run` testing gate; an empty reason or
|
|
47
|
+
a run outside `failed`/`not_run` is rejected.
|
|
48
|
+
3. Promotion rejects `not_run` without a waiver even if a caller manufactured a
|
|
49
|
+
pending testing gate.
|
|
50
|
+
4. An architect `changes_required` decision followed by `devcrew_answer`
|
|
51
|
+
transitions to `execution/ready` in the existing worktree, and the next
|
|
52
|
+
continuation invokes the implementer rather than the architect.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Configuration Integrity and Run Recovery Design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Reject unsafe DevCrew configuration before a workflow starts, serialize every
|
|
6
|
+
repository-mutating MCP operation, and give a requester an explicit, auditable
|
|
7
|
+
way to stop or recover an interrupted run without executing work automatically.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
This change covers configuration validation, repository mutation locking,
|
|
12
|
+
terminal aborted runs, `devcrew_abort`, and `devcrew_recover`. It does not add
|
|
13
|
+
automatic run resumption, change execution permissions, or introduce a remote
|
|
14
|
+
lock service.
|
|
15
|
+
|
|
16
|
+
## Configuration Contract
|
|
17
|
+
|
|
18
|
+
`readConfig` validates the complete version-1 shape rather than merging
|
|
19
|
+
untyped JSON into defaults.
|
|
20
|
+
|
|
21
|
+
- Top-level keys are limited to `version`, `defaultBackend`, `executionMode`,
|
|
22
|
+
`verifyCommands`, `lintCommands`, `coverageCommands`, and `workflow`.
|
|
23
|
+
- `defaultBackend`, `executionMode`, and every workflow gate must be members
|
|
24
|
+
of their exported enums.
|
|
25
|
+
- Command lists, when supplied, must be arrays of non-empty strings. Empty
|
|
26
|
+
lists remain valid; omitted command lists retain their version-1 default of
|
|
27
|
+
an empty array for backward compatibility.
|
|
28
|
+
- `workflow` may contain only `gates` and `artifactDirectory`; gates must be
|
|
29
|
+
unique. The existing runtime rule still makes implementation review and
|
|
30
|
+
testing mandatory.
|
|
31
|
+
- `artifactDirectory` must be a non-empty relative path whose resolved target
|
|
32
|
+
is inside the requester repository. Absolute paths and traversal outside the
|
|
33
|
+
repository are rejected before any artifact is written.
|
|
34
|
+
|
|
35
|
+
Unknown keys and invalid values fail closed with an error that names the
|
|
36
|
+
offending field. Existing default configuration remains valid.
|
|
37
|
+
|
|
38
|
+
## Repository Mutation Lock
|
|
39
|
+
|
|
40
|
+
All MCP operations that can write a run, active-run pointer, artifact, or
|
|
41
|
+
execution worktree take one repository-wide lock at `.devcrew/lock` for their
|
|
42
|
+
full operation. This includes start, answer, approve, reject, continue,
|
|
43
|
+
complete-execution, waive-verification, abort, and recovery cleanup. Status
|
|
44
|
+
and artifact reads remain unlocked.
|
|
45
|
+
|
|
46
|
+
The lock is an atomically-created directory containing owner metadata:
|
|
47
|
+
process id, a random owner id, and creation time. A second mutating request
|
|
48
|
+
does not wait or retry; it returns a clear busy error and cannot overwrite the
|
|
49
|
+
first operation's state. The holder removes only its own lock in `finally`.
|
|
50
|
+
|
|
51
|
+
Normal operations never remove a lock they did not create. A lock whose owner
|
|
52
|
+
metadata is absent, invalid, or refers to a non-running local process is
|
|
53
|
+
considered stale, but it still requires the explicit recovery operation to
|
|
54
|
+
remove it. This prevents a timeout heuristic from treating a long-running SDK
|
|
55
|
+
call as abandoned.
|
|
56
|
+
|
|
57
|
+
## Abort and Recovery Lifecycle
|
|
58
|
+
|
|
59
|
+
`RunStatus` gains terminal `aborted`, and `RunState` records an abort reason
|
|
60
|
+
and timestamp. An aborted run remains readable for audit but cannot continue,
|
|
61
|
+
approve, reject, answer, or complete execution. Repeating abort is idempotent
|
|
62
|
+
and preserves the first reason.
|
|
63
|
+
|
|
64
|
+
`devcrew_abort` accepts `cwd`, optional `runId`, and a non-empty `reason`.
|
|
65
|
+
Under the repository lock it records the abort, removes any isolated execution
|
|
66
|
+
worktree, and clears `active-run.json` only when it points at that run. If
|
|
67
|
+
worktree cleanup fails, the aborted state and audit evidence still persist;
|
|
68
|
+
the workspace reference remains for recovery.
|
|
69
|
+
|
|
70
|
+
`devcrew_recover` is explicit and never runs an agent or validation command.
|
|
71
|
+
It may remove a confirmed stale lock. When given a terminal run that retains
|
|
72
|
+
an execution workspace after an interrupted cleanup, it retries only that
|
|
73
|
+
worktree cleanup and clears the persisted workspace reference on success. A
|
|
74
|
+
live lock is never overridden.
|
|
75
|
+
|
|
76
|
+
## MCP and Error Semantics
|
|
77
|
+
|
|
78
|
+
The service exposes `devcrew_abort` and `devcrew_recover`. Both use the
|
|
79
|
+
existing optional-run-id resolution where applicable. Tool results retain the
|
|
80
|
+
current state summary; an aborted run reports `status=aborted`. Busy locks,
|
|
81
|
+
invalid configuration, live-lock recovery, and invalid abort/recovery targets
|
|
82
|
+
return normal structured MCP errors.
|
|
83
|
+
|
|
84
|
+
## Tests
|
|
85
|
+
|
|
86
|
+
- Config tests cover unknown fields, invalid enum/gate/command values,
|
|
87
|
+
duplicate gates, absolute artifact directories, and parent traversal.
|
|
88
|
+
- Service tests cover lock contention between mutation calls, while reads stay
|
|
89
|
+
available.
|
|
90
|
+
- Orchestrator/service tests cover abort audit persistence, worktree cleanup,
|
|
91
|
+
active-run clearing, idempotency, terminal-state blocking, stale-lock
|
|
92
|
+
recovery, and recovery cleanup after a simulated failed abort cleanup.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Structured Role Results Design
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Make DevCrew role output machine-readable without breaking existing Markdown-only
|
|
6
|
+
SDK output or the human-readable artifact workflow. A structured result becomes
|
|
7
|
+
the canonical source for workflow decisions and evidence when it is present;
|
|
8
|
+
Markdown remains the reviewable artifact body and the compatibility fallback.
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
This change covers the PM, architect, implementer, and tester role results used
|
|
13
|
+
by the SDK adapters and orchestrator. It does not remove Markdown artifacts,
|
|
14
|
+
change MCP tool names, change execution policies, or require existing installed
|
|
15
|
+
plugins to update simultaneously.
|
|
16
|
+
|
|
17
|
+
## Result Envelope
|
|
18
|
+
|
|
19
|
+
The core package will define a versioned role-result envelope with common fields:
|
|
20
|
+
|
|
21
|
+
- `schemaVersion: 1`
|
|
22
|
+
- `role`
|
|
23
|
+
- `summary`
|
|
24
|
+
- `risks`
|
|
25
|
+
- `evidence`
|
|
26
|
+
|
|
27
|
+
Role-specific fields are validated by role:
|
|
28
|
+
|
|
29
|
+
- PM: `questions`, where each question has a stable `id`, prompt, and optional
|
|
30
|
+
context.
|
|
31
|
+
- Architect: `decisions`; during the post-execution review phase,
|
|
32
|
+
`reviewDecision` is `approved` or `changes_required`.
|
|
33
|
+
- Implementer: `changedFiles` and `commands`, including command, exit code, and
|
|
34
|
+
captured output.
|
|
35
|
+
- Tester: `testCases`, `commands`, and `risks`.
|
|
36
|
+
|
|
37
|
+
The canonical TypeScript representation stores a `format` discriminator:
|
|
38
|
+
`structured` for a validated envelope and `legacy` for a Markdown-only result.
|
|
39
|
+
This allows callers and MCP clients to identify migration state without parsing
|
|
40
|
+
text.
|
|
41
|
+
|
|
42
|
+
## Adapter Parsing And Compatibility
|
|
43
|
+
|
|
44
|
+
SDK prompts will request one `<!-- devcrew-role-result -->` marker followed by a
|
|
45
|
+
fenced `json` block containing the DevCrew result, then the existing required
|
|
46
|
+
Markdown H2 sections. The marker avoids treating incidental JSON examples in a
|
|
47
|
+
human artifact as protocol data. The adapter extracts and validates that JSON
|
|
48
|
+
before accepting a structured result, removes the marked block, and uses the
|
|
49
|
+
remaining Markdown as the role result's `markdown` field.
|
|
50
|
+
|
|
51
|
+
If a JSON block is absent, the adapter preserves existing Markdown validation and
|
|
52
|
+
the current PM-question and architecture-review decision parsing. It returns a
|
|
53
|
+
`legacy` result and no new structured fields are inferred from arbitrary prose.
|
|
54
|
+
|
|
55
|
+
If a DevCrew JSON block is present but malformed, has an unsupported schema
|
|
56
|
+
version, does not match the active role, or fails field validation, the adapter
|
|
57
|
+
rejects the SDK output. It must not silently downgrade malformed claimed
|
|
58
|
+
structured output to legacy Markdown. Existing plan-mode fallback and apply-mode
|
|
59
|
+
failure rules remain unchanged.
|
|
60
|
+
|
|
61
|
+
## Workflow And Artifact Behaviour
|
|
62
|
+
|
|
63
|
+
The orchestrator consumes validated structured fields when available. PM
|
|
64
|
+
questions continue to route the run to `awaiting_input`; legacy Markdown
|
|
65
|
+
questions retain the current behavior. Architecture review decisions retain
|
|
66
|
+
their blocking semantics. Implementer and tester evidence is persisted in the
|
|
67
|
+
role result, surfaced by MCP state, and rendered into the corresponding Markdown
|
|
68
|
+
artifact as a readable structured-results appendix.
|
|
69
|
+
|
|
70
|
+
The role result stored in run state must remain loadable when older saved states
|
|
71
|
+
lack the new fields. Missing fields migrate to `format: legacy` and empty
|
|
72
|
+
optional arrays. No saved state becomes invalid merely because it predates this
|
|
73
|
+
feature.
|
|
74
|
+
|
|
75
|
+
## Error Handling
|
|
76
|
+
|
|
77
|
+
- No JSON envelope: accept the valid legacy Markdown result.
|
|
78
|
+
- Multiple candidate envelopes: reject as ambiguous.
|
|
79
|
+
- Invalid envelope: report field-specific validation errors.
|
|
80
|
+
- Envelope role/phase mismatch: reject before state or artifact writes.
|
|
81
|
+
- Structured PM questions: require nonempty unique IDs and nonempty prompts.
|
|
82
|
+
- Command evidence: require a command and integer exit code; output is optional.
|
|
83
|
+
|
|
84
|
+
## Testing
|
|
85
|
+
|
|
86
|
+
Tests will be written before production code and prove:
|
|
87
|
+
|
|
88
|
+
1. A valid envelope parses and exposes each role's structured fields.
|
|
89
|
+
2. Missing envelope preserves legacy Markdown behavior.
|
|
90
|
+
3. Malformed, ambiguous, version-invalid, and role-invalid envelopes fail rather
|
|
91
|
+
than downgrade.
|
|
92
|
+
4. Structured PM questions enter `awaiting_input`.
|
|
93
|
+
5. Structured architecture review decisions gate testing correctly.
|
|
94
|
+
6. Implementer and tester evidence persists and is visible in generated
|
|
95
|
+
artifacts and structured MCP state.
|
|
96
|
+
7. Loading a prior state without the new fields migrates safely.
|