@aion0/forge 0.5.22 → 0.5.24
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/RELEASE_NOTES.md +16 -29
- package/app/api/smith-templates/route.ts +81 -0
- package/components/SettingsModal.tsx +6 -1
- package/components/WorkspaceView.tsx +841 -83
- package/lib/claude-sessions.ts +2 -1
- package/lib/forge-mcp-server.ts +247 -33
- package/lib/help-docs/11-workspace.md +722 -166
- package/lib/telegram-bot.ts +1 -1
- package/lib/workspace/orchestrator.ts +292 -76
- package/lib/workspace/presets.ts +535 -58
- package/lib/workspace/requests.ts +287 -0
- package/lib/workspace/session-monitor.ts +4 -3
- package/lib/workspace/types.ts +1 -0
- package/lib/workspace/watch-manager.ts +1 -1
- package/lib/workspace-standalone.ts +1 -1
- package/package.json +1 -1
- package/scripts/bench/README.md +66 -0
- package/scripts/bench/results/.gitignore +2 -0
- package/scripts/bench/run.ts +635 -0
- package/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/templates/smith-lead.json +45 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Validator for text utility task.
|
|
3
|
+
# Runs in harness_test project root. Exits 0 = pass, non-zero = fail.
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
PROJECT_ROOT="${1:-/Users/zliu/IdeaProjects/harness_test}"
|
|
7
|
+
cd "$PROJECT_ROOT/src" || { echo "FAIL: src/ directory not found"; exit 1; }
|
|
8
|
+
|
|
9
|
+
# 1. Check files exist
|
|
10
|
+
[ -f utils/text.js ] || { echo "FAIL: utils/text.js missing"; exit 1; }
|
|
11
|
+
[ -f utils/text.test.js ] || { echo "FAIL: utils/text.test.js missing"; exit 1; }
|
|
12
|
+
|
|
13
|
+
# 2. Check exports
|
|
14
|
+
grep -q "export.*capitalize" utils/text.js || { echo "FAIL: capitalize not exported"; exit 1; }
|
|
15
|
+
grep -q "export.*reverseWords" utils/text.js || { echo "FAIL: reverseWords not exported"; exit 1; }
|
|
16
|
+
|
|
17
|
+
# 3. Run tests
|
|
18
|
+
node --test utils/text.test.js 2>&1 | tee /tmp/text-test-output.txt
|
|
19
|
+
TEST_EXIT=${PIPESTATUS[0]}
|
|
20
|
+
if [ "$TEST_EXIT" != "0" ]; then
|
|
21
|
+
echo "FAIL: tests failed (exit=$TEST_EXIT)"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 4. Additional smoke test — behavior verification independent of agent's tests
|
|
26
|
+
node -e "
|
|
27
|
+
import('./utils/text.js').then(m => {
|
|
28
|
+
const assert = require('node:assert/strict');
|
|
29
|
+
// capitalize
|
|
30
|
+
assert.equal(m.capitalize('hello'), 'Hello', 'capitalize basic');
|
|
31
|
+
assert.equal(m.capitalize('a'), 'A', 'capitalize single char');
|
|
32
|
+
try { m.capitalize(''); assert.fail('expected throw on empty'); } catch (e) { assert.ok(e instanceof TypeError); }
|
|
33
|
+
try { m.capitalize(null); assert.fail('expected throw on null'); } catch (e) { assert.ok(e instanceof TypeError); }
|
|
34
|
+
try { m.capitalize(123); assert.fail('expected throw on number'); } catch (e) { assert.ok(e instanceof TypeError); }
|
|
35
|
+
// reverseWords
|
|
36
|
+
assert.equal(m.reverseWords('hello world'), 'world hello');
|
|
37
|
+
assert.equal(m.reverseWords(' a b c '), 'c b a');
|
|
38
|
+
assert.equal(m.reverseWords(''), '');
|
|
39
|
+
assert.equal(m.reverseWords('single'), 'single');
|
|
40
|
+
try { m.reverseWords(null); assert.fail('expected throw'); } catch (e) { assert.ok(e instanceof TypeError); }
|
|
41
|
+
console.log('SMOKE_TEST_PASSED');
|
|
42
|
+
}).catch(err => { console.error('SMOKE_TEST_FAILED:', err.message); process.exit(1); });
|
|
43
|
+
" || { echo "FAIL: smoke test failed"; exit 1; }
|
|
44
|
+
|
|
45
|
+
echo "PASS"
|
|
46
|
+
exit 0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create a basic user list module without pagination.
|
|
3
|
+
set -e
|
|
4
|
+
PROJECT="${1:-/Users/zliu/IdeaProjects/harness_test}"
|
|
5
|
+
mkdir -p "$PROJECT/src/api"
|
|
6
|
+
|
|
7
|
+
cat > "$PROJECT/src/api/users.js" <<'EOF'
|
|
8
|
+
const USERS = Array.from({ length: 127 }, (_, i) => ({
|
|
9
|
+
id: i + 1,
|
|
10
|
+
name: `User ${i + 1}`,
|
|
11
|
+
email: `user${i + 1}@example.com`,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
export function listUsers() {
|
|
15
|
+
return USERS;
|
|
16
|
+
}
|
|
17
|
+
EOF
|
|
18
|
+
|
|
19
|
+
echo "Setup complete: created src/api/users.js with 127 users and a listUsers() function."
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Task: Add Pagination to User List
|
|
2
|
+
|
|
3
|
+
The file `src/api/users.js` currently has a `listUsers()` function that returns all users. Add pagination support.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
Replace `listUsers()` (or add a new function) with a paginated version:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
listUsers({ page = 1, pageSize = 20 } = {})
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Return format**:
|
|
14
|
+
```js
|
|
15
|
+
{
|
|
16
|
+
items: [...], // users on the current page
|
|
17
|
+
total: 127, // total number of users
|
|
18
|
+
page: 1, // current page (1-indexed)
|
|
19
|
+
pageSize: 20, // page size (after validation)
|
|
20
|
+
totalPages: 7, // Math.ceil(total / pageSize)
|
|
21
|
+
hasNext: true, // true if more pages exist
|
|
22
|
+
hasPrev: false // true if page > 1
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Validation Rules
|
|
27
|
+
|
|
28
|
+
- `page` must be integer ≥ 1. If invalid (not a number, < 1, NaN, float), throw `RangeError`.
|
|
29
|
+
- `pageSize` must be integer in [1, 100]. If invalid, throw `RangeError`.
|
|
30
|
+
- If `page` exceeds available pages, return empty `items` array but still return correct `total`, `page`, `pageSize`, `totalPages`, `hasNext: false`, `hasPrev: true`.
|
|
31
|
+
|
|
32
|
+
## Test File
|
|
33
|
+
|
|
34
|
+
Also create `src/api/users.test.js` using `node:test` and `node:assert/strict` covering:
|
|
35
|
+
- Default params return page 1 with 20 items
|
|
36
|
+
- Page 2 returns items 21-40
|
|
37
|
+
- Last page (page 7) returns items 121-127
|
|
38
|
+
- Page 8 returns empty items but correct metadata
|
|
39
|
+
- Custom pageSize (e.g., 50)
|
|
40
|
+
- Invalid page (0, -1, 'abc', 1.5, NaN) throws RangeError
|
|
41
|
+
- Invalid pageSize (0, 101, 'abc', 1.5) throws RangeError
|
|
42
|
+
|
|
43
|
+
## Constraints
|
|
44
|
+
|
|
45
|
+
- Keep ES module syntax
|
|
46
|
+
- No external deps
|
|
47
|
+
- Preserve the existing USERS array
|
|
48
|
+
- Tests must pass via: `cd src && node --test api/users.test.js`
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
PROJECT="${1:-/Users/zliu/IdeaProjects/harness_test}"
|
|
4
|
+
cd "$PROJECT/src"
|
|
5
|
+
|
|
6
|
+
[ -f api/users.js ] || { echo "FAIL: api/users.js missing"; exit 1; }
|
|
7
|
+
[ -f api/users.test.js ] || { echo "FAIL: api/users.test.js missing"; exit 1; }
|
|
8
|
+
grep -q "export function listUsers\|export const listUsers\|export { listUsers" api/users.js || { echo "FAIL: listUsers not exported"; exit 1; }
|
|
9
|
+
|
|
10
|
+
# Run agent's tests
|
|
11
|
+
node --test api/users.test.js 2>&1 | tee /tmp/paginate-test-output.txt
|
|
12
|
+
TEST_EXIT=${PIPESTATUS[0]}
|
|
13
|
+
[ "$TEST_EXIT" = "0" ] || { echo "FAIL: agent tests failed"; exit 1; }
|
|
14
|
+
|
|
15
|
+
# Independent smoke test
|
|
16
|
+
node -e "
|
|
17
|
+
import('./api/users.js').then(m => {
|
|
18
|
+
const assert = require('node:assert/strict');
|
|
19
|
+
// Default: page 1, 20 items
|
|
20
|
+
let r = m.listUsers();
|
|
21
|
+
assert.equal(r.items.length, 20, 'default pageSize 20');
|
|
22
|
+
assert.equal(r.items[0].id, 1);
|
|
23
|
+
assert.equal(r.total, 127);
|
|
24
|
+
assert.equal(r.page, 1);
|
|
25
|
+
assert.equal(r.pageSize, 20);
|
|
26
|
+
assert.equal(r.totalPages, 7);
|
|
27
|
+
assert.equal(r.hasNext, true);
|
|
28
|
+
assert.equal(r.hasPrev, false);
|
|
29
|
+
|
|
30
|
+
// Page 2
|
|
31
|
+
r = m.listUsers({ page: 2 });
|
|
32
|
+
assert.equal(r.items[0].id, 21);
|
|
33
|
+
assert.equal(r.items.length, 20);
|
|
34
|
+
assert.equal(r.hasPrev, true);
|
|
35
|
+
|
|
36
|
+
// Last page (127 / 20 = 6.35 → 7 pages; page 7 has 7 items)
|
|
37
|
+
r = m.listUsers({ page: 7 });
|
|
38
|
+
assert.equal(r.items.length, 7, 'last page has 7 items');
|
|
39
|
+
assert.equal(r.items[0].id, 121);
|
|
40
|
+
assert.equal(r.hasNext, false);
|
|
41
|
+
|
|
42
|
+
// Page 8 (beyond) — empty but correct metadata
|
|
43
|
+
r = m.listUsers({ page: 8 });
|
|
44
|
+
assert.equal(r.items.length, 0, 'page beyond: empty items');
|
|
45
|
+
assert.equal(r.total, 127);
|
|
46
|
+
assert.equal(r.totalPages, 7);
|
|
47
|
+
assert.equal(r.hasNext, false);
|
|
48
|
+
|
|
49
|
+
// Custom pageSize
|
|
50
|
+
r = m.listUsers({ page: 1, pageSize: 50 });
|
|
51
|
+
assert.equal(r.items.length, 50);
|
|
52
|
+
assert.equal(r.totalPages, 3);
|
|
53
|
+
|
|
54
|
+
// Invalid page
|
|
55
|
+
for (const p of [0, -1, 'abc', 1.5, NaN]) {
|
|
56
|
+
try { m.listUsers({ page: p }); assert.fail('expected RangeError for page=' + p); }
|
|
57
|
+
catch (e) { assert.ok(e instanceof RangeError, 'page=' + p + ' should throw RangeError, got: ' + e.constructor.name); }
|
|
58
|
+
}
|
|
59
|
+
// Invalid pageSize
|
|
60
|
+
for (const ps of [0, 101, 'abc', 1.5]) {
|
|
61
|
+
try { m.listUsers({ page: 1, pageSize: ps }); assert.fail('expected RangeError for pageSize=' + ps); }
|
|
62
|
+
catch (e) { assert.ok(e instanceof RangeError, 'pageSize=' + ps + ' should throw RangeError'); }
|
|
63
|
+
}
|
|
64
|
+
console.log('SMOKE_TEST_PASSED');
|
|
65
|
+
}).catch(err => { console.error('SMOKE_TEST_FAILED:', err.message); process.exit(1); });
|
|
66
|
+
" || { echo "FAIL: smoke test failed"; exit 1; }
|
|
67
|
+
|
|
68
|
+
echo "PASS"
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create a date range calculator with 2 bugs.
|
|
3
|
+
set -e
|
|
4
|
+
PROJECT="${1:-/Users/zliu/IdeaProjects/harness_test}"
|
|
5
|
+
mkdir -p "$PROJECT/src/lib" "$PROJECT/src/lib/__tests__"
|
|
6
|
+
|
|
7
|
+
cat > "$PROJECT/src/lib/dateRange.js" <<'EOF'
|
|
8
|
+
// Compute the inclusive number of days between two YYYY-MM-DD dates.
|
|
9
|
+
// Returns a positive integer. If end is before start, throws RangeError.
|
|
10
|
+
export function daysBetween(startStr, endStr) {
|
|
11
|
+
if (typeof startStr !== 'string' || typeof endStr !== 'string') {
|
|
12
|
+
throw new TypeError('daysBetween expects two YYYY-MM-DD strings');
|
|
13
|
+
}
|
|
14
|
+
const start = new Date(startStr);
|
|
15
|
+
const end = new Date(endStr);
|
|
16
|
+
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
|
|
17
|
+
throw new TypeError('invalid date format');
|
|
18
|
+
}
|
|
19
|
+
if (end < start) throw new RangeError('end before start');
|
|
20
|
+
// BUG: missing +1 to be inclusive of both endpoints
|
|
21
|
+
return Math.floor((end - start) / (1000 * 60 * 60 * 24));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Return array of YYYY-MM-DD strings from start to end (inclusive).
|
|
25
|
+
export function dateRange(startStr, endStr) {
|
|
26
|
+
const days = daysBetween(startStr, endStr);
|
|
27
|
+
const result = [];
|
|
28
|
+
const current = new Date(startStr);
|
|
29
|
+
// BUG: loop condition uses < instead of <=, excluding final day
|
|
30
|
+
for (let i = 0; i < days; i++) {
|
|
31
|
+
result.push(current.toISOString().slice(0, 10));
|
|
32
|
+
current.setDate(current.getDate() + 1);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
EOF
|
|
37
|
+
|
|
38
|
+
cat > "$PROJECT/src/lib/__tests__/dateRange.test.js" <<'EOF'
|
|
39
|
+
import { test } from 'node:test';
|
|
40
|
+
import assert from 'node:assert/strict';
|
|
41
|
+
import { daysBetween, dateRange } from '../dateRange.js';
|
|
42
|
+
|
|
43
|
+
test('daysBetween: same day returns 1', () => {
|
|
44
|
+
assert.equal(daysBetween('2026-01-01', '2026-01-01'), 1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('daysBetween: one day apart returns 2', () => {
|
|
48
|
+
assert.equal(daysBetween('2026-01-01', '2026-01-02'), 2);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('daysBetween: one week', () => {
|
|
52
|
+
assert.equal(daysBetween('2026-01-01', '2026-01-07'), 7);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('daysBetween: end before start throws RangeError', () => {
|
|
56
|
+
assert.throws(() => daysBetween('2026-01-05', '2026-01-01'), RangeError);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('daysBetween: non-string throws TypeError', () => {
|
|
60
|
+
assert.throws(() => daysBetween(20260101, '2026-01-02'), TypeError);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('dateRange: single day returns array with one date', () => {
|
|
64
|
+
assert.deepEqual(dateRange('2026-01-01', '2026-01-01'), ['2026-01-01']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('dateRange: three days', () => {
|
|
68
|
+
assert.deepEqual(
|
|
69
|
+
dateRange('2026-01-01', '2026-01-03'),
|
|
70
|
+
['2026-01-01', '2026-01-02', '2026-01-03']
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('dateRange: includes both endpoints', () => {
|
|
75
|
+
const r = dateRange('2026-03-30', '2026-04-02');
|
|
76
|
+
assert.equal(r.length, 4);
|
|
77
|
+
assert.equal(r[0], '2026-03-30');
|
|
78
|
+
assert.equal(r[r.length - 1], '2026-04-02');
|
|
79
|
+
});
|
|
80
|
+
EOF
|
|
81
|
+
|
|
82
|
+
echo "Setup complete: created src/lib/dateRange.js (with 2 bugs) and tests that currently fail."
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Task: Fix Bugs in dateRange Module
|
|
2
|
+
|
|
3
|
+
The file `src/lib/dateRange.js` has 2 bugs. The existing test file `src/lib/__tests__/dateRange.test.js` describes the expected behavior.
|
|
4
|
+
|
|
5
|
+
## Your job
|
|
6
|
+
|
|
7
|
+
1. Run the existing tests — several will fail. Identify what's wrong.
|
|
8
|
+
2. Fix both bugs in `src/lib/dateRange.js`.
|
|
9
|
+
3. Do NOT modify the test file. The tests correctly express the expected behavior.
|
|
10
|
+
4. Do NOT change the function signatures or add new functions.
|
|
11
|
+
5. After fixing, all tests must pass.
|
|
12
|
+
|
|
13
|
+
## Verify
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd src && node --test lib/__tests__/dateRange.test.js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
All tests should pass.
|
|
20
|
+
|
|
21
|
+
## Hints
|
|
22
|
+
|
|
23
|
+
- `daysBetween('2026-01-01', '2026-01-01')` should return `1` (inclusive count)
|
|
24
|
+
- `dateRange('2026-01-01', '2026-01-03')` should return all 3 days including both endpoints
|
|
25
|
+
|
|
26
|
+
## Constraints
|
|
27
|
+
|
|
28
|
+
- Minimal diff — fix only what's broken
|
|
29
|
+
- Keep the functions pure
|
|
30
|
+
- No new dependencies
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
PROJECT="${1:-/Users/zliu/IdeaProjects/harness_test}"
|
|
4
|
+
cd "$PROJECT/src"
|
|
5
|
+
|
|
6
|
+
[ -f lib/dateRange.js ] || { echo "FAIL: lib/dateRange.js missing (agent deleted it?)"; exit 1; }
|
|
7
|
+
[ -f lib/__tests__/dateRange.test.js ] || { echo "FAIL: test file missing (agent deleted it?)"; exit 1; }
|
|
8
|
+
|
|
9
|
+
# Run the existing (unmodified) tests
|
|
10
|
+
node --test lib/__tests__/dateRange.test.js 2>&1 | tee /tmp/bugfix-test-output.txt
|
|
11
|
+
TEST_EXIT=${PIPESTATUS[0]}
|
|
12
|
+
[ "$TEST_EXIT" = "0" ] || { echo "FAIL: tests still failing after fix"; exit 1; }
|
|
13
|
+
|
|
14
|
+
# Extra smoke: verify functions exist and behave
|
|
15
|
+
node -e "
|
|
16
|
+
import('./lib/dateRange.js').then(m => {
|
|
17
|
+
const assert = require('node:assert/strict');
|
|
18
|
+
assert.equal(m.daysBetween('2026-01-01', '2026-01-01'), 1);
|
|
19
|
+
assert.equal(m.daysBetween('2026-01-01', '2026-01-10'), 10);
|
|
20
|
+
const r = m.dateRange('2026-01-01', '2026-01-05');
|
|
21
|
+
assert.equal(r.length, 5, 'should include both endpoints');
|
|
22
|
+
assert.equal(r[0], '2026-01-01');
|
|
23
|
+
assert.equal(r[4], '2026-01-05');
|
|
24
|
+
console.log('SMOKE_TEST_PASSED');
|
|
25
|
+
}).catch(err => { console.error('SMOKE_TEST_FAILED:', err.message); process.exit(1); });
|
|
26
|
+
" || { echo "FAIL: smoke test failed"; exit 1; }
|
|
27
|
+
|
|
28
|
+
echo "PASS"
|
|
29
|
+
exit 0
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Lead",
|
|
3
|
+
"icon": "👑",
|
|
4
|
+
"description": "Primary coordinator — SOP-driven requirement intake, delegation, gap coverage, and quality gate",
|
|
5
|
+
"config": {
|
|
6
|
+
"label": "Lead",
|
|
7
|
+
"icon": "👑",
|
|
8
|
+
"backend": "cli",
|
|
9
|
+
"agentId": "claude",
|
|
10
|
+
"primary": true,
|
|
11
|
+
"persistentSession": true,
|
|
12
|
+
"workDir": "./",
|
|
13
|
+
"outputs": ["docs/lead/"],
|
|
14
|
+
"plugins": ["playwright", "shell-command"],
|
|
15
|
+
"role": "You are the Lead — primary coordinator of this workspace.\n\nYour context automatically includes a \"Workspace Team\" section showing all agents, their roles, status, and missing standard roles. Read it before every action.\n\n## SOP: Requirement Intake\n\nWhen you receive a requirement (from user input or inbox message):\n\n```\n1. Read the Workspace Team section in your context\n2. Classify the requirement:\n - Single task → one request document\n - Multi-module → break into independent request documents, group in a batch\n3. Route based on available roles:\n\n HAS Architect?\n └─ YES → create_request with full description → Architect breaks it down further\n └─ NO → you break it down yourself, then:\n\n HAS Engineer?\n └─ YES → create_request for each module (status: open)\n Engineers claim via claim_request\n └─ NO → implement it yourself in src/\n Record files_changed in docs/lead/impl-notes.md\n\n4. After implementation (by you or Engineer):\n\n HAS QA?\n └─ YES → update_response(section: engineer) triggers auto-notify to QA\n └─ NO → you test it:\n - Read acceptance_criteria from the request\n - Write tests in tests/ or run manually\n - Record results: update_response(section: qa, result: passed/failed)\n\n5. After testing:\n\n HAS Reviewer?\n └─ YES → auto-notified when QA passes\n └─ NO → you review it:\n - Check code quality, security, PRD compliance\n - Record: update_response(section: review, result: approved/changes_requested)\n - If changes_requested → send_message to Engineer or fix yourself\n```\n\n## SOP: Monitoring & Coordination\n\nWhile work is in progress:\n\n```\n1. get_status → check all agents' smith/task status\n2. list_requests → check request progress\n\nIF agent taskStatus = failed:\n → Read their error from get_status\n → send_message asking what went wrong\n → If no response or unfixable: handle the request yourself\n\nIF agent taskStatus = running for too long:\n → send_message to check progress\n\nIF request stuck in one status:\n → Check which agent should be handling it\n → send_message to nudge, or cover it yourself\n\nIF multiple Engineers exist and request unclaimed:\n → send_message to available Engineer suggesting they claim_request\n```\n\n## SOP: Quality Gate (before declaring done)\n\n```\n1. list_requests(batch: current_batch)\n2. ALL requests status = done?\n └─ NO → identify stuck ones, apply Monitoring SOP\n └─ YES → continue\n3. Any request with review.result = changes_requested?\n └─ YES → verify changes were made, re-review if no Reviewer\n4. Any request with qa.result = failed?\n └─ YES → verify fix was applied, re-test if no QA\n5. Write summary to docs/lead/delivery-summary.md:\n - Requirements covered\n - Requests created and their final status\n - Roles you covered due to gaps\n - Any open issues\n```\n\n## Gap Coverage Reference\n\nWhen you cover a missing role, follow that role's standards:\n\n| Missing Role | What You Do | Output |\n|---|---|---|\n| PM/Architect | Break requirements into modules with acceptance_criteria | request documents via create_request |\n| Engineer | Read request → implement in src/ → update_response(section: engineer) | source code + files_changed |\n| QA | Read acceptance_criteria → write/run tests → update_response(section: qa) | test results in tests/ or docs/qa/ |\n| Reviewer | Read code changes → check quality/security → update_response(section: review) | review findings |\n\n## Rules\n\n- Workspace Team is in your context — don't call get_agents redundantly at start, just read it\n- DO call get_agents/get_status when you need live status updates mid-task\n- Every delegated task MUST go through request documents (create_request) — never just send_message with vague instructions\n- Each request needs concrete acceptance_criteria that QA can verify\n- Do NOT duplicate work an active agent is already doing — check status first\n- When covering a gap, be thorough — don't half-do it just because it's not your \"main\" role",
|
|
16
|
+
"steps": [
|
|
17
|
+
{
|
|
18
|
+
"id": "intake",
|
|
19
|
+
"label": "Intake & Analyze",
|
|
20
|
+
"prompt": "Read the Workspace Team section in your context. Identify: (1) which standard roles are present and missing, (2) incoming requirements from upstream input or inbox. For each requirement, decide scope: single task or multi-module. List what you will delegate vs handle yourself."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "delegate",
|
|
24
|
+
"label": "Create Requests & Route",
|
|
25
|
+
"prompt": "For each module/task: create_request with title, description, acceptance_criteria, and batch name. If Architect exists, create high-level requests for them to break down. If only Engineers exist, create implementation-ready requests. If no one to delegate to, note which requests you will implement yourself. Verify with list_requests that all were created."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "cover-gaps",
|
|
29
|
+
"label": "Cover Missing Roles",
|
|
30
|
+
"prompt": "Handle all work for missing roles. If no Engineer: implement code, then update_response(section: engineer). If no QA: write/run tests against acceptance_criteria, then update_response(section: qa). If no Reviewer: review code changes for quality and security, then update_response(section: review). Use get_status between tasks to check if other agents have completed their work."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "monitor",
|
|
34
|
+
"label": "Monitor & Unblock",
|
|
35
|
+
"prompt": "Run get_status and list_requests. For each stuck/failed agent: diagnose and send_message to unblock, or take over the request yourself. For unclaimed requests: nudge available agents. Continue until all requests show progress."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "gate",
|
|
39
|
+
"label": "Quality Gate & Summary",
|
|
40
|
+
"prompt": "list_requests for the current batch. Verify ALL requests are status=done with review.result=approved and qa.result=passed. If any are not: apply gap coverage. Write docs/lead/delivery-summary.md with: requirements covered, request statuses, roles you covered, open issues."
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"exportedAt": 1743724800000
|
|
45
|
+
}
|