pi-crew 0.8.13 → 0.9.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/CHANGELOG.md +296 -0
- package/README.md +118 -2
- package/docs/FEATURE_INTAKE.md +1 -1
- package/docs/HARNESS.md +20 -19
- package/docs/PROJECT_REVIEW.md +132 -133
- package/docs/PROJECT_REVIEW_FIXES.md +130 -131
- package/docs/actions-reference.md +127 -121
- package/docs/architecture.md +1 -1
- package/docs/code-review-2026-05-11.md +134 -134
- package/docs/commands-reference.md +108 -106
- package/docs/comparison-pi-subagents-vs-pi-crew.md +105 -105
- package/docs/deep-review-report.md +1 -1
- package/docs/dynamic-workflows.md +90 -0
- package/docs/fixes/BATCH_A_H1_H2.md +17 -17
- package/docs/fixes/bug-007-async-notifier-stale-ctx.md +23 -23
- package/docs/followup-plan-2026-05-12.md +135 -135
- package/docs/followup-review-2026-05-12.md +86 -86
- package/docs/followup-review-round3-2026-05-12.md +123 -123
- package/docs/goals.md +59 -0
- package/docs/implementation-plan-top3.md +4 -4
- package/docs/issue-29-analysis.md +2 -2
- package/docs/oh-my-pi-research.md +154 -154
- package/docs/optimization-plan.md +2 -0
- package/docs/perf/baseline-2026-05.md +9 -9
- package/docs/perf/final-report-2026-05.md +2 -2
- package/docs/perf/sprint-1-report.md +2 -2
- package/docs/perf/sprint-2-report.md +1 -1
- package/docs/perf/upgrade-plan-2026-05.md +72 -72
- package/docs/pi-crew-bugs.md +230 -230
- package/docs/pi-crew-investigation-report.md +102 -102
- package/docs/pi-crew-test-round5.md +4 -4
- package/docs/runtime-analysis-child-vs-live.md +57 -57
- package/docs/runtime-migration-in-process-analysis.md +97 -97
- package/install.mjs +3 -2
- package/package.json +2 -4
- package/skills/orchestration/SKILL.md +11 -11
- package/src/agents/agent-config.ts +4 -0
- package/src/config/config.ts +39 -0
- package/src/config/types.ts +11 -0
- package/src/extension/action-suggestions.ts +2 -1
- package/src/extension/async-notifier.ts +10 -0
- package/src/extension/help.ts +14 -0
- package/src/extension/project-init.ts +7 -20
- package/src/extension/registration/commands.ts +27 -0
- package/src/extension/team-tool/destructive-gate.ts +1 -1
- package/src/extension/team-tool/goal-wrap.ts +288 -0
- package/src/extension/team-tool/goal.ts +405 -0
- package/src/extension/team-tool/run.ts +103 -4
- package/src/extension/team-tool/workflow-manage.ts +194 -0
- package/src/extension/team-tool.ts +20 -0
- package/src/hooks/types.ts +3 -1
- package/src/runtime/async-runner.ts +24 -2
- package/src/runtime/background-runner.ts +68 -19
- package/src/runtime/child-pi.ts +6 -1
- package/src/runtime/completion-guard.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +450 -0
- package/src/runtime/dynamic-workflow-runner.ts +180 -0
- package/src/runtime/global-worker-cap.ts +96 -0
- package/src/runtime/goal-evaluator.ts +294 -0
- package/src/runtime/goal-loop-runner.ts +612 -0
- package/src/runtime/goal-state-store.ts +209 -0
- package/src/runtime/pi-args.ts +10 -2
- package/src/runtime/result-extractor.ts +32 -0
- package/src/runtime/team-runner.ts +11 -1
- package/src/runtime/verification-gates.ts +85 -5
- package/src/runtime/verification-integrity.ts +110 -0
- package/src/runtime/verification-worktree.ts +136 -0
- package/src/runtime/workspace-lock.ts +448 -0
- package/src/schema/config-schema.ts +26 -0
- package/src/schema/team-tool-schema.ts +39 -4
- package/src/state/atomic-write.ts +9 -0
- package/src/state/contracts.ts +14 -0
- package/src/state/crew-init.ts +18 -5
- package/src/state/event-log.ts +7 -1
- package/src/state/state-store.ts +2 -0
- package/src/state/types.ts +82 -0
- package/src/state/worker-atomic-writer.ts +176 -0
- package/src/utils/redaction.ts +104 -24
- package/src/workflows/discover-workflows.ts +25 -1
- package/src/workflows/workflow-config.ts +13 -0
- package/teams/parallel-research.team.md +1 -1
- package/workflows/examples/hello.dwf.ts +24 -0
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
# Review
|
|
1
|
+
# Review of the fixes applied
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
> Base: PROJECT_REVIEW.md (
|
|
6
|
-
> Working tree: 33
|
|
3
|
+
> Date: 2026-05-18
|
|
4
|
+
> Version: `pi-crew@0.2.20`
|
|
5
|
+
> Base: PROJECT_REVIEW.md (same directory) — the original report.
|
|
6
|
+
> Working tree: 33 files changed (`git diff --stat`), including installing `@biomejs/biome`, adding `biome.json`, fixing source + tests.
|
|
7
7
|
|
|
8
8
|
## TL;DR
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The fixes go in the right direction and **all tests still pass** (1596/1598, 0 fail). However, there are **3 new correctness bugs introduced by the fixes** and **2 conventions to clean up**:
|
|
11
11
|
|
|
12
|
-
| ID | File |
|
|
12
|
+
| ID | File | Severity | Status |
|
|
13
13
|
|---|---|---|---|
|
|
14
|
-
| **NEW-1** | `src/state/event-log-rotation.ts` (rotateEventLog) | HIGH | `require()`
|
|
15
|
-
| **NEW-2** | `src/runtime/task-runner.ts` (M1 transcript per attempt) | HIGH | logic
|
|
16
|
-
| **NEW-3** | `src/runtime/task-runner.ts` (M2 transcript cap) | MED |
|
|
17
|
-
| LINT-1 | `src/runtime/task-runner.ts:350` | LOW | `yieldResult` unused (yield logic
|
|
18
|
-
| LINT-2 | `src/runtime/team-runner.ts:270` | LOW | `runPromise` unused (
|
|
14
|
+
| **NEW-1** | `src/state/event-log-rotation.ts` (rotateEventLog) | HIGH | `require()` in ESM → throws silently |
|
|
15
|
+
| **NEW-2** | `src/runtime/task-runner.ts` (M1 transcript per attempt) | HIGH | logic is wrong, still uses a single shared file |
|
|
16
|
+
| **NEW-3** | `src/runtime/task-runner.ts` (M2 transcript cap) | MED | tail read does not cut on line boundaries → corrupt JSONL; writes artifact with the old relativePath |
|
|
17
|
+
| LINT-1 | `src/runtime/task-runner.ts:350` | LOW | `yieldResult` unused (yield logic removed?) |
|
|
18
|
+
| LINT-2 | `src/runtime/team-runner.ts:270` | LOW | `runPromise` unused (registers a Promise then drops the reference) |
|
|
19
19
|
|
|
20
|
-
Status
|
|
20
|
+
Status of each original issue:
|
|
21
21
|
|
|
22
|
-
| Issue | Status |
|
|
22
|
+
| Issue | Status | Notes |
|
|
23
23
|
|---|---|---|
|
|
24
|
-
| **H1** event-log overflow | OK |
|
|
25
|
-
| **H2** mailbox lock | OK |
|
|
26
|
-
| **H3** atomic-write fallback symlink | OK | re-
|
|
27
|
-
| **H4** rename `__test__mergeTaskUpdates` | OK |
|
|
28
|
-
| **M1** transcript per attempt | **BROKEN (NEW-2)** | logic
|
|
29
|
-
| **M2** transcript cap | **PARTIAL (NEW-3)** |
|
|
30
|
-
| **M3** cleanup race-safe stat | OK |
|
|
31
|
-
| **M4** runSetupHook full-JSON | OK |
|
|
32
|
-
| **M5** symlink fail logging | OK |
|
|
33
|
-
| **M6** final-drain telemetry | OK |
|
|
34
|
-
| **L1** ESLint/Biome | OK |
|
|
35
|
-
| **L12** rename references | OK |
|
|
24
|
+
| **H1** event-log overflow | OK | correct pattern: prioritize terminal events, compact + rotate before append |
|
|
25
|
+
| **H2** mailbox lock | OK | uses `withEventLogLockSync` |
|
|
26
|
+
| **H3** atomic-write fallback symlink | OK | re-checks `lstatSync.isSymbolicLink()` before fallback |
|
|
27
|
+
| **H4** rename `__test__mergeTaskUpdates` | OK | renamed + kept deprecated alias |
|
|
28
|
+
| **M1** transcript per attempt | **BROKEN (NEW-2)** | logic is incorrect |
|
|
29
|
+
| **M2** transcript cap | **PARTIAL (NEW-3)** | has a cap but cuts at the wrong place |
|
|
30
|
+
| **M3** cleanup race-safe stat | OK | uses `withFileTypes` + try/catch |
|
|
31
|
+
| **M4** runSetupHook full-JSON | OK | tries full trimmed first, falls back to last-line |
|
|
32
|
+
| **M5** symlink fail logging | OK | logs the reason, hints at Windows non-admin |
|
|
33
|
+
| **M6** final-drain telemetry | OK | logs internal error when overriding exit |
|
|
34
|
+
| **L1** ESLint/Biome | OK | added `@biomejs/biome` + `biome.json` |
|
|
35
|
+
| **L12** rename references | OK | expanded for workflow step.role + test fixtures |
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
-
## 1.
|
|
39
|
+
## 1. New bugs introduced by the fixes (NEW-*)
|
|
40
40
|
|
|
41
|
-
### NEW-1 (HIGH) — `rotateEventLog`
|
|
41
|
+
### NEW-1 (HIGH) — `rotateEventLog` uses `require()` in an ESM module
|
|
42
42
|
|
|
43
|
-
**File**: `src/state/event-log-rotation.ts` (
|
|
43
|
+
**File**: `src/state/event-log-rotation.ts` (lines 124–129)
|
|
44
44
|
|
|
45
45
|
```ts
|
|
46
46
|
} catch (error) {
|
|
@@ -55,12 +55,12 @@ Status từng issue gốc:
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
**
|
|
59
|
-
1.
|
|
60
|
-
2.
|
|
61
|
-
3.
|
|
58
|
+
**Problem**:
|
|
59
|
+
1. The project declares `"type": "module"` (ESM). In an ESM scope, **`require` does not exist** → throws `ReferenceError: require is not defined`.
|
|
60
|
+
2. The path `"./internal-error.ts"` is wrong — the file is actually at `../utils/internal-error.ts`.
|
|
61
|
+
3. The outer try-catch swallows the error → when `rename` fails, the function returns `false` but **no log is written**. The H1 fix relies on rotateEventLog to reduce size; if rotate fails silently, we are back to the silent-drop scenario.
|
|
62
62
|
|
|
63
|
-
**
|
|
63
|
+
**Correct fix**: import at the top of the file like `compactEventLog` already does:
|
|
64
64
|
```ts
|
|
65
65
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
66
66
|
// ...
|
|
@@ -69,28 +69,28 @@ import { logInternalError } from "../utils/internal-error.ts";
|
|
|
69
69
|
return false;
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
|
-
|
|
72
|
+
There is no circular dependency because `internal-error.ts` does not import from `state/`.
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
76
|
-
### NEW-2 (HIGH) — Transcript-per-attempt
|
|
76
|
+
### NEW-2 (HIGH) — Transcript-per-attempt does not work
|
|
77
77
|
|
|
78
|
-
**File**: `src/runtime/task-runner.ts` (
|
|
78
|
+
**File**: `src/runtime/task-runner.ts` (lines 155–158)
|
|
79
79
|
|
|
80
80
|
```ts
|
|
81
81
|
modelAttempts = [];
|
|
82
82
|
// M1 fix: transcript path per attempt to avoid mixing across fallback attempts.
|
|
83
|
-
const attempt = modelAttempts.length; // 0-based index ←
|
|
83
|
+
const attempt = modelAttempts.length; // 0-based index ← always 0
|
|
84
84
|
transcriptPath = `${manifest.artifactsRoot}/transcripts/${task.id}.attempt-${attempt}.jsonl`;
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
**
|
|
88
|
-
- `modelAttempts = []`
|
|
89
|
-
- `transcriptPath`
|
|
90
|
-
-
|
|
91
|
-
-
|
|
87
|
+
**Problem**:
|
|
88
|
+
- `modelAttempts = []` was just initialized empty → `modelAttempts.length` **is always 0**.
|
|
89
|
+
- `transcriptPath` is set **outside** the `for (let i = 0; i < attemptModels.length; i++)` loop.
|
|
90
|
+
- All N attempts write to `transcripts/${task.id}.attempt-0.jsonl` → still mixing exactly like before.
|
|
91
|
+
- Furthermore: `parsePiJsonOutput(fs.readFileSync(transcriptPath))` reads accumulated content → final text/usage is still mixed across attempts.
|
|
92
92
|
|
|
93
|
-
**
|
|
93
|
+
**Correct fix**: use the loop variable `i`, set transcriptPath inside the for loop:
|
|
94
94
|
```ts
|
|
95
95
|
for (let i = 0; i < attemptModels.length; i++) {
|
|
96
96
|
transcriptPath = `${manifest.artifactsRoot}/transcripts/${task.id}.attempt-${i}.jsonl`;
|
|
@@ -100,9 +100,9 @@ for (let i = 0; i < attemptModels.length; i++) {
|
|
|
100
100
|
|
|
101
101
|
---
|
|
102
102
|
|
|
103
|
-
### NEW-3 (MED) — Transcript cap
|
|
103
|
+
### NEW-3 (MED) — Transcript cap tail read does not respect line boundaries
|
|
104
104
|
|
|
105
|
-
**File**: `src/runtime/task-runner.ts` (
|
|
105
|
+
**File**: `src/runtime/task-runner.ts` (lines 294–315)
|
|
106
106
|
|
|
107
107
|
```ts
|
|
108
108
|
const MAX_TRANSCRIPT_ARTIFACT_BYTES = 5 * 1024 * 1024;
|
|
@@ -121,35 +121,35 @@ if (fs.existsSync(transcriptPath)) {
|
|
|
121
121
|
}
|
|
122
122
|
transcriptArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
123
123
|
kind: "log",
|
|
124
|
-
relativePath: `transcripts/${task.id}.jsonl`, // ←
|
|
124
|
+
relativePath: `transcripts/${task.id}.jsonl`, // ← artifact name differs from source!
|
|
125
125
|
content: transcriptContent,
|
|
126
126
|
producer: task.id,
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
**
|
|
132
|
-
1. **JSONL corruption**: tail
|
|
133
|
-
- Fix:
|
|
134
|
-
2. **`relativePath`
|
|
135
|
-
3. **UTF-8 boundary**: `buf.slice(0, bytesRead).toString('utf-8')`
|
|
136
|
-
4. **Cap
|
|
131
|
+
**Problem**:
|
|
132
|
+
1. **JSONL corruption**: the tail read cuts at a fixed byte offset, not on a `\n` → the first line of the transcript artifact is very likely a **partial JSON line** that cannot be parsed. Any tool replaying the transcript will skip the first line (losing an important event).
|
|
133
|
+
- Fix: after reading, find the first newline and drop the bytes before it. Or prepend a header marker `[truncated head]`.
|
|
134
|
+
2. **`relativePath` does not match the source file**: if NEW-2 is fixed correctly (`attempt-i.jsonl`), then the artifact should reference that name. Currently the artifact always writes `transcripts/${task.id}.jsonl` → loses the attempt information.
|
|
135
|
+
3. **UTF-8 boundary**: `buf.slice(0, bytesRead).toString('utf-8')` can cut in the middle of a multi-byte character → the first character becomes `\uFFFD`. Minor but worth noting.
|
|
136
|
+
4. **Cap is only 5MB** for the artifact, but the source `transcriptPath` is not capped → can still grow very large (M2 only solves the artifact memory, not the disk).
|
|
137
137
|
|
|
138
138
|
---
|
|
139
139
|
|
|
140
|
-
## 2.
|
|
140
|
+
## 2. Remaining lint warnings
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
Installed `@biomejs/biome` (L1 OK). When running `npx biome lint` on the changed files, 2 warnings remain:
|
|
143
143
|
|
|
144
144
|
### LINT-1 — `task-runner.ts:350` `yieldResult` unused
|
|
145
145
|
|
|
146
146
|
```ts
|
|
147
147
|
let yieldResult: YieldResult | undefined;
|
|
148
|
-
// ...
|
|
149
|
-
//
|
|
148
|
+
// ... assigns yieldResult = extractYieldResult(yieldEvent);
|
|
149
|
+
// but never read again
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
`yieldResult`
|
|
152
|
+
`yieldResult` is assigned but never used anywhere below. The yield logic is "dangling". Either remove the variable, or use it to override task.result/finalText. Needs confirmation from the owner.
|
|
153
153
|
|
|
154
154
|
### LINT-2 — `team-runner.ts:270` `runPromise` unused
|
|
155
155
|
|
|
@@ -157,34 +157,34 @@ let yieldResult: YieldResult | undefined;
|
|
|
157
157
|
const runPromise = registerRunPromise(manifest.runId);
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
`registerRunPromise`
|
|
160
|
+
`registerRunPromise` has a side effect (registers into the tracker), but the variable name is unnecessary. You could change it to `void registerRunPromise(manifest.runId);` so biome ignores it, or rename to `_runPromise`.
|
|
161
161
|
|
|
162
|
-
>
|
|
162
|
+
> Do not wire `lint:check` into CI until these 2 warnings are fixed, otherwise it will add noise to every PR.
|
|
163
163
|
|
|
164
164
|
---
|
|
165
165
|
|
|
166
|
-
## 3.
|
|
166
|
+
## 3. Original issues fixed well (details)
|
|
167
167
|
|
|
168
168
|
### H1 — Event-log overflow (PASS)
|
|
169
169
|
|
|
170
|
-
`appendEventInsideLock`
|
|
171
|
-
- Terminal
|
|
172
|
-
- Non-terminal
|
|
173
|
-
- `skippedDueToSize` flag
|
|
170
|
+
`appendEventInsideLock` was fixed correctly:
|
|
171
|
+
- Terminal events are always appended regardless of size.
|
|
172
|
+
- Non-terminal events that hit the overflow → `compactEventLog` immediately, and if still too large → `rotateEventLog`.
|
|
173
|
+
- The `skippedDueToSize` flag is only set when both compact + rotate fail to reduce size (very rare).
|
|
174
174
|
|
|
175
|
-
**
|
|
176
|
-
- `appendCounter++`
|
|
177
|
-
-
|
|
178
|
-
-
|
|
175
|
+
**Minor notes**:
|
|
176
|
+
- `appendCounter++` still runs even when `skippedDueToSize === true`. Not a bug but makes the `% 100` rotation trigger one cycle early — does not affect correctness.
|
|
177
|
+
- The seq number is still consumed when skipped → when a consumer sees a "gap" in seq they may worry. You can set `metadata.appended: false` (already present) so consumers skip safely. OK.
|
|
178
|
+
- Depends on `rotateEventLog` (NEW-1 broken). When NEW-1 fails, the fallback path is `appendFileSync` which still appends to a file > 50MB → the file keeps growing.
|
|
179
179
|
|
|
180
180
|
### H2 — Mailbox lock (PASS)
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
Wraps `appendFileSync` in `withEventLogLockSync`. Reasonable.
|
|
183
183
|
|
|
184
|
-
**
|
|
185
|
-
-
|
|
186
|
-
- `withEventLogLockSync`
|
|
187
|
-
-
|
|
184
|
+
**Notes**:
|
|
185
|
+
- The lock by `eventsPath` is actually by `mailboxFile(...)`, i.e., `inbox.jsonl` and `outbox.jsonl` have independent locks. OK for cross-process.
|
|
186
|
+
- `withEventLogLockSync` was not exported before; it was changed to `export function` — acceptable but the name is slightly misleading when used for the mailbox. Consider extracting a generic `withJsonlAppendLock`.
|
|
187
|
+
- The lock only protects append. Other paths like `updateMailboxMessageReply` (which already uses `atomicWriteFile` rewrite) or `validateMailbox` are not affected.
|
|
188
188
|
|
|
189
189
|
### H3 — Atomic-write fallback symlink TOCTOU (PASS)
|
|
190
190
|
|
|
@@ -200,7 +200,7 @@ try {
|
|
|
200
200
|
}
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
OK.
|
|
203
|
+
OK. Note: the outer catch swallows **all** errors from `lstatSync`, not just ENOENT. If `lstatSync` fails with EACCES (permission denied), the fallback proceeds even though it may not be safe. Could narrow to `(err as NodeJS.ErrnoException).code === "ENOENT"`.
|
|
204
204
|
|
|
205
205
|
### H4 — Rename `__test__mergeTaskUpdates` (PASS)
|
|
206
206
|
|
|
@@ -210,29 +210,29 @@ export function mergeTaskUpdatesPreservingTerminal(...) { ... }
|
|
|
210
210
|
export const __test__mergeTaskUpdates = mergeTaskUpdatesPreservingTerminal;
|
|
211
211
|
```
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
Nice. Good backward compatibility. The caller inside `executeTeamRunCore` also needs updating — quick check:
|
|
214
214
|
|
|
215
215
|
```
|
|
216
216
|
> rg "__test__mergeTaskUpdates" -n src
|
|
217
217
|
src/runtime/team-runner.ts:117:export const __test__mergeTaskUpdates = mergeTaskUpdatesPreservingTerminal;
|
|
218
|
-
src/runtime/team-runner.ts:545: tasks = __test__mergeTaskUpdates(tasks, results); ←
|
|
218
|
+
src/runtime/team-runner.ts:545: tasks = __test__mergeTaskUpdates(tasks, results); ← still uses the alias
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
Production code
|
|
221
|
+
Production code still calls the alias `__test__mergeTaskUpdates`. Suggestion: change the caller to `mergeTaskUpdatesPreservingTerminal` so only the test file uses the alias.
|
|
222
222
|
|
|
223
223
|
### M3 — Cleanup race-safe stat (PASS)
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
Uses `withFileTypes`, wraps `statSync` in try/catch. OK.
|
|
226
226
|
|
|
227
227
|
### M4 — runSetupHook multi-line JSON (PASS)
|
|
228
228
|
|
|
229
|
-
|
|
229
|
+
Tries `JSON.parse(trimmed)` first, then falls back to the last line. OK.
|
|
230
230
|
|
|
231
|
-
**
|
|
231
|
+
**Minor note**: two nested try/catch inside the outer try → the outer catch (parse error logging) almost never triggers because the inner catch already swallows. Could be cleaned up. Does not affect correctness.
|
|
232
232
|
|
|
233
233
|
### M5 — symlink fail logging (PASS)
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
Logs the reason + a Windows non-admin hint. Note the indentation is slightly off (5 tabs instead of 1) — biome auto-format will fix it.
|
|
236
236
|
|
|
237
237
|
### M6 — final-drain telemetry (PASS)
|
|
238
238
|
|
|
@@ -242,22 +242,22 @@ if (forcedFinalDrain && !timeoutError && exitCode !== 0) {
|
|
|
242
242
|
}
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
-
OK.
|
|
245
|
+
OK. Uses `logInternalError` (not a metric counter). In the future, emit a metric `crew.child.final_drain_force_zero_total` via MetricRegistry so the dashboard can count it — `logInternalError` is only a backup observability.
|
|
246
246
|
|
|
247
|
-
**
|
|
247
|
+
**Note**: the indentation block is off (5 tabs for the if-block inside a 4-tab parent block). Biome will flag it.
|
|
248
248
|
|
|
249
249
|
### L1 — Biome added (PASS)
|
|
250
250
|
|
|
251
|
-
`@biomejs/biome ^2.4.15` + `biome.json` config
|
|
252
|
-
- `recommended: true`, indent tab × 4, double
|
|
253
|
-
-
|
|
254
|
-
- `useIgnoreFile: true`
|
|
251
|
+
`@biomejs/biome ^2.4.15` + good `biome.json` config:
|
|
252
|
+
- `recommended: true`, indent tab × 4, double quotes, semicolons always.
|
|
253
|
+
- Disables some unsuitable rules (`noNonNullAssertion`, `noUselessSwitchCase`, …).
|
|
254
|
+
- `useIgnoreFile: true` reads `.gitignore`.
|
|
255
255
|
|
|
256
|
-
**
|
|
257
|
-
- `npm run lint` script
|
|
258
|
-
- CI
|
|
256
|
+
**Still missing**:
|
|
257
|
+
- `npm run lint` script in `package.json`.
|
|
258
|
+
- CI does not run biome in `npm run ci`.
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
Suggested additions:
|
|
261
261
|
```json
|
|
262
262
|
"scripts": {
|
|
263
263
|
"lint": "biome lint .",
|
|
@@ -266,35 +266,35 @@ OK. Đang dùng `logInternalError` (không phải metric counter). Trong tương
|
|
|
266
266
|
}
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
### L12 — Rename references (PASS,
|
|
269
|
+
### L12 — Rename references (PASS, with risk)
|
|
270
270
|
|
|
271
|
-
`updateReferencesForRename`
|
|
272
|
-
1. Workflow step.role →
|
|
273
|
-
-
|
|
274
|
-
2. Update test fixtures
|
|
271
|
+
`updateReferencesForRename` was expanded:
|
|
272
|
+
1. Workflow step.role → renamed along with the agent rename. **Logic warning**: `step.role` is actually the role name within the team, not the agent name. These are two different concepts: agent `coder` can be used for role `developer`. Updating step.role when renaming an agent is **semantically wrong** and can break a valid workflow.
|
|
273
|
+
- Suggestion: only rename `team.roles[*].agent` (already done in the earlier loop), do not touch `step.role`.
|
|
274
|
+
2. Update test fixtures via regex.
|
|
275
275
|
```ts
|
|
276
276
|
const agentPattern = new RegExp('(["\'\\`]agent[="\':\\s]*)' + escapeRegex(oldName) + '(["\'\\`]|\\s)', 'g');
|
|
277
277
|
```
|
|
278
|
-
-
|
|
279
|
-
-
|
|
280
|
-
-
|
|
281
|
-
-
|
|
282
|
-
- `escapeRegex` regex: `/[.*+?^${}()|[\\]\\]/g` —
|
|
283
|
-
-
|
|
284
|
-
3. `walkTsFiles`
|
|
278
|
+
- This regex is complex + has a template-literal mess, very prone to false positives/negatives. For example:
|
|
279
|
+
- It will match `"agent": "coder"` (OK)
|
|
280
|
+
- It will NOT match `agent: coder` (oldName without quotes)
|
|
281
|
+
- It will false-match if there is a variable named `agent_other = "coder"`
|
|
282
|
+
- The `escapeRegex` regex: `/[.*+?^${}()|[\\]\\]/g` — correct (verified the character class).
|
|
283
|
+
- **Suggestion**: test fixture rewrites should not use regex; if needed, parse YAML/markdown frontmatter / TS AST.
|
|
284
|
+
3. `walkTsFiles` recursively processes all `.ts`/`.md` in the test dir. OK but I/O-heavy for a rename op.
|
|
285
285
|
|
|
286
286
|
---
|
|
287
287
|
|
|
288
|
-
## 4.
|
|
288
|
+
## 4. Incidental side fixes (not in the original scope)
|
|
289
289
|
|
|
290
|
-
|
|
290
|
+
Some changed files do not belong to the 4 batches above — appear to be general cleanup:
|
|
291
291
|
|
|
292
|
-
- `src/extension/team-tool.ts` —
|
|
293
|
-
- `src/extension/team-tool.ts` — `let nextTasks` → `const nextTasks`.
|
|
294
|
-
- `src/runtime/team-runner.ts` — `let workflow` → `const workflow`.
|
|
295
|
-
- `src/runtime/code-summary.ts`, `manifest-cache.ts`, `prose-compressor.ts`, `result-extractor.ts`, `retry-executor.ts`, `skill-instructions.ts`, `observability/event-to-metric.ts`, `utils/gh-protocol.ts`, `utils/names.ts`, `utils/sse-parser.ts`, `config/markers.ts`, `config/resilient-parser.ts`, `adapters/export-util.ts`, `worktree/cleanup.ts` (M3 + others) —
|
|
292
|
+
- `src/extension/team-tool.ts` — changed `import { … }` to `import type { … }` for 2 lazy-load spots. Reasonable (avoids runtime import side effects).
|
|
293
|
+
- `src/extension/team-tool.ts` — `let nextTasks` → `const nextTasks`. Correct (not reassigned).
|
|
294
|
+
- `src/runtime/team-runner.ts` — `let workflow` → `const workflow`. Correct.
|
|
295
|
+
- `src/runtime/code-summary.ts`, `manifest-cache.ts`, `prose-compressor.ts`, `result-extractor.ts`, `retry-executor.ts`, `skill-instructions.ts`, `observability/event-to-metric.ts`, `utils/gh-protocol.ts`, `utils/names.ts`, `utils/sse-parser.ts`, `config/markers.ts`, `config/resilient-parser.ts`, `adapters/export-util.ts`, `worktree/cleanup.ts` (M3 + others) — mostly biome auto-fixes (formatting / unused imports). Diff stat is small (~1-2 lines/file).
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
Need to verify biome did not break logic (especially since the `noUnusedImports` rule was turned off but the `1 deletion` changes in `result-extractor.ts`, `skill-instructions.ts`, `sse-parser.ts` are suspicious).
|
|
298
298
|
|
|
299
299
|
```bash
|
|
300
300
|
git diff src/runtime/result-extractor.ts src/runtime/skill-instructions.ts src/utils/sse-parser.ts
|
|
@@ -310,34 +310,33 @@ npm run test:unit → 1596 pass / 2 skip / 0 fail / 87s
|
|
|
310
310
|
npx biome lint <changed files> → 2 warnings (LINT-1, LINT-2)
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
-
Tests
|
|
314
|
-
- NEW-1
|
|
315
|
-
- NEW-2
|
|
316
|
-
- NEW-3
|
|
313
|
+
Tests still pass because:
|
|
314
|
+
- NEW-1 does not trigger in unit tests (rotateEventLog only runs when the file > 50MB).
|
|
315
|
+
- NEW-2 has no specific test for transcript-per-attempt collision.
|
|
316
|
+
- NEW-3 has no test for a transcript cap > 5MB.
|
|
317
317
|
|
|
318
318
|
---
|
|
319
319
|
|
|
320
|
-
## 6.
|
|
320
|
+
## 6. Recommended actions (by priority)
|
|
321
321
|
|
|
322
|
-
1. **Fix NEW-1
|
|
323
|
-
2. **Fix NEW-2**:
|
|
324
|
-
3. **Fix NEW-3**:
|
|
325
|
-
4. **
|
|
322
|
+
1. **Fix NEW-1 now**: change `require` → top-level `import { logInternalError } from "../utils/internal-error.ts"`. (1 minute)
|
|
323
|
+
2. **Fix NEW-2**: move the `transcriptPath = ...attempt-${i}...` line inside the `for` loop. (2 minutes)
|
|
324
|
+
3. **Fix NEW-3**: cut the tail on the `\n` boundary; update the artifact `relativePath` to match the source filename; prepend a `[truncated]\n` marker so consumers know.
|
|
325
|
+
4. **Add unit tests** for:
|
|
326
326
|
- `rotateEventLog` (rename + create empty)
|
|
327
|
-
- `appendEvent`
|
|
328
|
-
- `appendMailboxMessage` concurrent (spawn 2
|
|
329
|
-
- Transcript per-attempt (mock 2 attempts, verify 2
|
|
327
|
+
- `appendEvent` with a file > 50MB → terminal event still persisted
|
|
328
|
+
- `appendMailboxMessage` concurrent (spawn 2 workers, check no interleaving)
|
|
329
|
+
- Transcript per-attempt (mock 2 attempts, verify 2 separate files)
|
|
330
330
|
- Atomic-write fallback symlink TOCTOU (mock rename fail + symlink swap)
|
|
331
|
-
5. **
|
|
332
|
-
6.
|
|
333
|
-
7. **
|
|
334
|
-
8. **Re-verify
|
|
331
|
+
5. **Clean up LINT-1, LINT-2** before wiring biome into CI.
|
|
332
|
+
6. **Suggested: add a `lint` script** to `package.json` + run biome in `ci`.
|
|
333
|
+
7. **Re-review L12**: remove the `step.role` update logic (semantically wrong) or gate it behind an `--unsafe-rename` flag.
|
|
334
|
+
8. **Re-verify biome auto-fix side fixes** in `result-extractor.ts`, `skill-instructions.ts`, `sse-parser.ts` (3 files with suspicious `-1 deletion`).
|
|
335
335
|
|
|
336
336
|
---
|
|
337
337
|
|
|
338
|
-
## 7.
|
|
338
|
+
## 7. Conclusion
|
|
339
339
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
Sau khi fix 3 bugs trên + bổ sung test, ta sẽ có một codebase chắc chắn hơn đáng kể so với baseline review.
|
|
340
|
+
The direction is correct, most of the original issues have been resolved. However, 3 fixes have **logic bugs** (NEW-1, NEW-2, NEW-3) that mean the "anti-overflow" and "per-attempt transcript" features do not work as intended. Because the old tests do not cover these code paths, the regressions pass the current suite.
|
|
343
341
|
|
|
342
|
+
After fixing the 3 bugs above + adding tests, we will have a significantly more robust codebase compared to the baseline review.
|