muse-crew 0.1.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/AGENTS.md +19 -0
- package/API.md +213 -0
- package/README.md +68 -0
- package/docs/AGENTS.md +3 -0
- package/docs/guide.md +333 -0
- package/identities/AGENTS.md +7 -0
- package/identities/cass.md +36 -0
- package/identities/hazel.md +35 -0
- package/identities/mara.md +31 -0
- package/identities/personality-notes.md +81 -0
- package/identities/sage.md +31 -0
- package/identities/tate.md +35 -0
- package/identities/wren.md +36 -0
- package/lib/AGENTS.md +8 -0
- package/lib/crew-release.sh +188 -0
- package/lib/merge-lock.sh +89 -0
- package/lib/orphan-sweep.sh +95 -0
- package/lib/worktree-lifecycle.sh +313 -0
- package/package.json +29 -0
- package/personas/AGENTS.md +5 -0
- package/personas/beginner.md +24 -0
- package/personas/completionist.md +24 -0
- package/personas/designer.md +26 -0
- package/personas/financial-stakeholder.md +25 -0
- package/personas/power-user.md +26 -0
- package/seed/AGENTS.md +8 -0
- package/seed/cron-body-template.md +20 -0
- package/seed/feedback/AGENTS.md +3 -0
- package/seed/feedback/README.md +40 -0
- package/seed/posture.md +8 -0
- package/seed/workflows/AGENTS.md +3 -0
- package/seed/workflows/bugfix.md +39 -0
- package/seed/workflows/chore.md +29 -0
- package/seed/workflows/docs.md +17 -0
- package/seed/workflows/standard.md +34 -0
- package/workflows/AGENTS.md +12 -0
- package/workflows/bugfix.js +341 -0
- package/workflows/chore.js +294 -0
- package/workflows/crew-dispatch.js +315 -0
- package/workflows/crew-init.js +254 -0
- package/workflows/docs.js +141 -0
- package/workflows/standard.js +350 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Standard
|
|
2
|
+
|
|
3
|
+
The default workflow for feature work and improvements. Like Bugfix but without reproduction — the problem is already understood.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
### Triage
|
|
8
|
+
**Identity:** Sage
|
|
9
|
+
Validate, prioritize, connect dependencies, assign this workflow.
|
|
10
|
+
|
|
11
|
+
### Map
|
|
12
|
+
**Identity:** Mara
|
|
13
|
+
Research options, pick the path, write the spec.
|
|
14
|
+
|
|
15
|
+
### Build
|
|
16
|
+
**Identity:** Wren
|
|
17
|
+
Execute the spec.
|
|
18
|
+
|
|
19
|
+
### Review
|
|
20
|
+
**Identity:** Cass
|
|
21
|
+
**Constraint:** Independent context. Review cold.
|
|
22
|
+
|
|
23
|
+
### Integrate
|
|
24
|
+
**Identity:** Wren
|
|
25
|
+
Merge changes into the target branch. Create the commit with a clear message.
|
|
26
|
+
|
|
27
|
+
### Deploy
|
|
28
|
+
**Identity:** Wren
|
|
29
|
+
Deploy the changes.
|
|
30
|
+
|
|
31
|
+
### QA
|
|
32
|
+
**Identity:** Hazel
|
|
33
|
+
**Personas:** Yes — Hazel wears a persona costume to test from that perspective.
|
|
34
|
+
**Constraint:** No code context. Pass or fail. File follow-up tasks for related issues found during testing.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Executable Muse workflow scripts (JavaScript). These are what the workflow runtime actually runs.
|
|
4
|
+
|
|
5
|
+
- `crew-dispatch.js` — reads the board, claims eligible tasks, returns structured launch records
|
|
6
|
+
- `crew-init.js` — sets up a new crew instance: orchestration folders, dashboard, cron, sample project
|
|
7
|
+
- `standard.js` — default task workflow: Triage → Map → Build → Review → Integrate → Deploy → QA
|
|
8
|
+
- `bugfix.js` — adds Reproduce after Triage
|
|
9
|
+
- `chore.js` — drops QA (low-risk)
|
|
10
|
+
- `docs.js` — Tate writes, Cass reviews
|
|
11
|
+
|
|
12
|
+
The `.js` scripts here are distinct from the `.md` definitions in `seed/workflows/` and `.orchestration/workflows/`. The `.md` files describe phases and prompts; these `.js` files execute them.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: "crew-bugfix",
|
|
3
|
+
description: "Bugfix workflow: Triage → Reproduce → Map → Build → Review → Integrate → Deploy → QA",
|
|
4
|
+
phases: ["Triage", "Reproduce", "Map", "Build", "Review", "Integrate", "Deploy", "QA"],
|
|
5
|
+
steps: [
|
|
6
|
+
{ name: "Triage", identity: "sage" },
|
|
7
|
+
{ name: "Reproduce", identity: "hazel" },
|
|
8
|
+
{ name: "Map", identity: "mara" },
|
|
9
|
+
{ name: "Build", identity: "wren" },
|
|
10
|
+
{ name: "Review", identity: "cass" },
|
|
11
|
+
{ name: "Integrate", identity: "wren" },
|
|
12
|
+
{ name: "Deploy", identity: "wren" },
|
|
13
|
+
{ name: "QA", identity: "hazel" }
|
|
14
|
+
],
|
|
15
|
+
reworkTarget: "Build"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const inputs = args ?? {};
|
|
19
|
+
const taskId = inputs.task_id;
|
|
20
|
+
const taskTitle = inputs.task_title || "";
|
|
21
|
+
const taskDescription = inputs.task_description || "";
|
|
22
|
+
const firstSessionId = inputs.session_id || null;
|
|
23
|
+
const startStepIndex = inputs.start_step_index || 0;
|
|
24
|
+
|
|
25
|
+
// Config from args — backward-compatible fallbacks for manual launches
|
|
26
|
+
const DASHBOARD_SLUG = inputs.dashboardSlug || "orchestra-dashboard";
|
|
27
|
+
const crewHome = inputs.crewHome || "~/workspace/.jarvis";
|
|
28
|
+
const ORCH_PATH = crewHome + "/.orchestration";
|
|
29
|
+
// Pin lifecycle scripts to this run
|
|
30
|
+
const LIFECYCLE_SRC = crewHome + "/lib/worktree-lifecycle.sh";
|
|
31
|
+
const MERGE_LOCK_SRC = crewHome + "/lib/merge-lock.sh";
|
|
32
|
+
const RUN_LIB = "/tmp/crew-lib-" + taskId;
|
|
33
|
+
const LIFECYCLE = RUN_LIB + "/worktree-lifecycle.sh";
|
|
34
|
+
const MERGE_LOCK = RUN_LIB + "/merge-lock.sh";
|
|
35
|
+
|
|
36
|
+
// Project config — passed by dispatcher, falls back to dashboard defaults
|
|
37
|
+
const projectConfig = inputs.project_config || {};
|
|
38
|
+
const REPO_PATH = projectConfig.repo_path || "~/workspace/ts-spaces/orchestra-dashboard";
|
|
39
|
+
const DEPLOY_TYPE = projectConfig.deploy_type || "artifact";
|
|
40
|
+
const DEPLOY_SLUG = projectConfig.deploy_slug || DASHBOARD_SLUG;
|
|
41
|
+
const PROJECT_DESC = projectConfig.description || "React + TypeScript web dashboard (client/src/, server/src/, drizzle/)";
|
|
42
|
+
const RELEASE_SCRIPT = crewHome + "/crew-release.sh";
|
|
43
|
+
|
|
44
|
+
if (!taskId) {
|
|
45
|
+
throw new Error("task_id is required in args");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Work-agent result schema. Runtime retries on non-JSON (structured outputs).
|
|
49
|
+
// Workflow wraps in try/catch so exhausted retries block instead of crashing.
|
|
50
|
+
const WORK_SCHEMA = {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: { passed: { type: "boolean" }, summary: { type: "string" } },
|
|
53
|
+
required: ["passed", "summary"]
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// STEPS inline — export const meta is parsed as metadata, not a runtime binding
|
|
57
|
+
const STEPS = [
|
|
58
|
+
{ name: "Triage", identity: "sage" },
|
|
59
|
+
{ name: "Reproduce", identity: "hazel" },
|
|
60
|
+
{ name: "Map", identity: "mara" },
|
|
61
|
+
{ name: "Build", identity: "wren" },
|
|
62
|
+
{ name: "Review", identity: "cass" },
|
|
63
|
+
{ name: "Integrate", identity: "wren" },
|
|
64
|
+
{ name: "Deploy", identity: "wren" },
|
|
65
|
+
{ name: "QA", identity: "hazel" }
|
|
66
|
+
];
|
|
67
|
+
const BUILD_INDEX = STEPS.findIndex(s => s.name === 'Build');
|
|
68
|
+
if (BUILD_INDEX < 0) throw new Error("STEPS missing 'Build' step");
|
|
69
|
+
const MAX_REWORK = 2;
|
|
70
|
+
let reworkCount = 0;
|
|
71
|
+
let rejectionNotes = inputs.rejection_notes || "";
|
|
72
|
+
let mapperSpec = "";
|
|
73
|
+
let i = startStepIndex;
|
|
74
|
+
|
|
75
|
+
// Pin lifecycle scripts
|
|
76
|
+
await agent(
|
|
77
|
+
"Snapshot lifecycle scripts for version pinning.\n" +
|
|
78
|
+
"Run: mkdir -p " + RUN_LIB + " && cp " + LIFECYCLE_SRC + " " + LIFECYCLE + " && cp " + MERGE_LOCK_SRC + " " + MERGE_LOCK + " && chmod +x " + LIFECYCLE + " " + MERGE_LOCK,
|
|
79
|
+
{ key: "pin-lifecycle", label: "Pinning lifecycle scripts", schema: { type: "object" } }
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
while (i < STEPS.length) {
|
|
83
|
+
const step = STEPS[i];
|
|
84
|
+
const isFirstClaim = (i === startStepIndex && reworkCount === 0);
|
|
85
|
+
|
|
86
|
+
phase(step.name);
|
|
87
|
+
log(step.name + " step (" + step.identity + ") for task " + taskId);
|
|
88
|
+
|
|
89
|
+
// Claim session
|
|
90
|
+
let activeSessionId;
|
|
91
|
+
if (isFirstClaim && firstSessionId) {
|
|
92
|
+
activeSessionId = firstSessionId;
|
|
93
|
+
} else {
|
|
94
|
+
const claimResult = await agent(
|
|
95
|
+
"Claim a session for this task step.\n" +
|
|
96
|
+
"Call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"claimtask\", args:\n" +
|
|
97
|
+
"{ \"task_id\": \"" + taskId + "\", \"identity\": \"" + step.identity + "\", \"step\": \"" + step.name + "\", \"notes\": \"" + step.name + " step started" + (reworkCount > 0 ? " (rework #" + reworkCount + ")" : "") + "\" }.\n" +
|
|
98
|
+
"Return the session_id from the response.",
|
|
99
|
+
{
|
|
100
|
+
key: "claim-" + step.name + (reworkCount > 0 ? "-r" + reworkCount : ""),
|
|
101
|
+
label: "Claiming " + step.name,
|
|
102
|
+
schema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: { session_id: { type: "string" } },
|
|
105
|
+
required: ["session_id"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
activeSessionId = claimResult.session_id;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
var safeTitle = taskTitle.replace(/"/g, "'").replace(/\\/g, "\\\\").replace(/`/g, "'");
|
|
113
|
+
var instructions = "";
|
|
114
|
+
|
|
115
|
+
if (step.name === "Triage") {
|
|
116
|
+
instructions = "Validate the task, check clarity, note dependencies, confirm the bugfix workflow assignment.\nIf the task needs decomposition, note that in your assessment.\nYour final response MUST be valid JSON and nothing else: { \"summary\": \"your assessment\", \"passed\": true }. No prose, no markdown, just the JSON object.";
|
|
117
|
+
|
|
118
|
+
} else if (step.name === "Reproduce") {
|
|
119
|
+
instructions = "Reproduce the bug from a user's perspective. You are CODE-BLIND — do NOT read source code.\n" +
|
|
120
|
+
"To investigate, use artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\" with action \"getstate\" (args {}) to read current sessions, events, and tasks.\n" +
|
|
121
|
+
"Look at session notes in the returned data — check whether multiline content has newlines preserved or runs together.\n" +
|
|
122
|
+
"You can also check specific sessions with the getevents action for evidence.\n" +
|
|
123
|
+
"Do NOT use artifact_inspect — it is async and will not return results inline.\n" +
|
|
124
|
+
"Capture concrete evidence from the data you retrieve.\n" +
|
|
125
|
+
"If reproduction succeeds, your final response MUST be valid JSON and nothing else: { \"passed\": true, \"summary\": \"reproduction evidence and steps\" }.\n" +
|
|
126
|
+
"If reproduction fails, your final response MUST be valid JSON and nothing else: { \"passed\": false, \"summary\": \"what you tried and why reproduction failed\" }.\n" +
|
|
127
|
+
"No prose, no markdown, just the JSON object.";
|
|
128
|
+
|
|
129
|
+
} else if (step.name === "Map") {
|
|
130
|
+
instructions = "Update the task with a solution-oriented spec. Research options, pick the shortest path.\nThe builder will edit source files in a git worktree.\nProject: " + PROJECT_DESC + "\nWrite it clearly enough that the builder does not need to ask questions.\nYour final response MUST be valid JSON and nothing else: { \"summary\": \"what you specified\", \"passed\": true }. No prose, no markdown, just the JSON object.";
|
|
131
|
+
|
|
132
|
+
} else if (step.name === "Build") {
|
|
133
|
+
instructions = "STEP 1: Prepare your worktree.\n" +
|
|
134
|
+
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " prepare " + taskId + "\n" +
|
|
135
|
+
"If the output says CREATED or REUSED, proceed. If it says ERROR, stop and set passed to false.\n\n" +
|
|
136
|
+
"STEP 2: Edit source files to implement the mapper's spec below.\n" +
|
|
137
|
+
(mapperSpec ? "MAPPER'S SPEC (implement exactly this):\n" + mapperSpec + "\n\n" : "") +
|
|
138
|
+
"Your working directory: " + REPO_PATH + "/.worktrees/" + taskId + "/\n" +
|
|
139
|
+
"This is the project source: " + PROJECT_DESC + "\n" +
|
|
140
|
+
"Edit the TypeScript source files directly. Do NOT use artifact_edit — that happens in the Deploy phase.\n" +
|
|
141
|
+
"Do not add unrequested features. Build exactly what the spec calls for.\n\n" +
|
|
142
|
+
"STEP 3: Commit your changes.\n" +
|
|
143
|
+
"cd " + REPO_PATH + "/.worktrees/" + taskId + "\n" +
|
|
144
|
+
"git add -A\n" +
|
|
145
|
+
"git commit -m \"fix: " + safeTitle + "\"\n\n" +
|
|
146
|
+
(rejectionNotes ? "This is REWORK after rejection. Address these specific issues:\n" + rejectionNotes + "\n\n" : "") +
|
|
147
|
+
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"what you built\", \"passed\": true }. No prose, no markdown, just the JSON object.";
|
|
148
|
+
|
|
149
|
+
} else if (step.name === "Review") {
|
|
150
|
+
instructions = "Review independently and cold. You have NOT seen any reasoning from the builder.\nDo NOT access the task dashboard, event log, or any comments. Your review is based solely on the spec and the code.\n\n" +
|
|
151
|
+
(mapperSpec ? "MAPPER'S SPEC (the builder was asked to implement exactly this):\n" + mapperSpec + "\n\n" : "Read the spec (from the task description or spec files under " + crewHome + "/).\n\n") +
|
|
152
|
+
"Examine the code changes by running:\n" +
|
|
153
|
+
"CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " inspect " + taskId + "\n\n" +
|
|
154
|
+
"You can also read specific files in the worktree at:\n" +
|
|
155
|
+
REPO_PATH + "/.worktrees/" + taskId + "/\n\n" +
|
|
156
|
+
"Check quality, correctness, and spec compliance.\n" +
|
|
157
|
+
"If the work passes review, your final response MUST be valid JSON and nothing else: { \"passed\": true, \"summary\": \"approval notes\" }.\n" +
|
|
158
|
+
"If the work fails review, your final response MUST be valid JSON and nothing else: { \"passed\": false, \"summary\": \"rejection notes explaining what needs to change\" }.\n" +
|
|
159
|
+
"No prose, no markdown, just the JSON object.";
|
|
160
|
+
|
|
161
|
+
} else if (step.name === "Integrate") {
|
|
162
|
+
instructions = "Merge the approved task branch into main.\n\n" +
|
|
163
|
+
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " integrate " + taskId + " \"merge: fix: " + safeTitle + "\"\n\n" +
|
|
164
|
+
"Read the output:\n" +
|
|
165
|
+
"- If it contains MERGED, integration succeeded. Report the merged commit hash.\n" +
|
|
166
|
+
"- If it contains LOCK_HELD, another task is deploying. Set passed to false.\n" +
|
|
167
|
+
"- If it contains CONFLICT, a merge conflict occurred. Set passed to false with details.\n" +
|
|
168
|
+
"- If it contains ERROR, something else failed. Set passed to false.\n\n" +
|
|
169
|
+
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"result\", \"passed\": true/false }. No prose, no markdown, just the JSON object.";
|
|
170
|
+
|
|
171
|
+
} else if (step.name === "Deploy") {
|
|
172
|
+
if (DEPLOY_TYPE === "repo") {
|
|
173
|
+
// Repo projects: install immutable release and atomically activate
|
|
174
|
+
instructions = "Deploy repo changes via the immutable release system.\n\n" +
|
|
175
|
+
"STEP 0: Refresh the merge lock to prevent stale-lock breaking during deploy.\n" +
|
|
176
|
+
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " refresh-lock " + taskId + "\n\n" +
|
|
177
|
+
"STEP 1: Install and activate the new release.\n" +
|
|
178
|
+
"Run: " + RELEASE_SCRIPT + " deploy " + REPO_PATH + "\n" +
|
|
179
|
+
"Verify the output contains INSTALLED and ACTIVATED (or EXISTS and ACTIVATED if unchanged).\n\n" +
|
|
180
|
+
"STEP 2: Finalize.\n" +
|
|
181
|
+
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " post-deploy " + taskId + "\n" +
|
|
182
|
+
"If the output contains DEPLOYED, finalization is complete.\n\n" +
|
|
183
|
+
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"release activated and finalized\", \"passed\": true }.\n" +
|
|
184
|
+
"No prose, no markdown, just the JSON object.";
|
|
185
|
+
} else {
|
|
186
|
+
instructions = "Deploy the merged code to the live artifact.\n\n" +
|
|
187
|
+
"STEP 0: Refresh the merge lock to prevent stale-lock breaking during deploy.\n" +
|
|
188
|
+
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " refresh-lock " + taskId + "\n\n" +
|
|
189
|
+
"STEP 1: Deploy to the live artifact.\n" +
|
|
190
|
+
"Get the change summary: cd " + REPO_PATH + " && git log -1 --stat\n" +
|
|
191
|
+
"Then call artifact_edit with slug \"" + DEPLOY_SLUG + "\" and verbatim_request:\n" +
|
|
192
|
+
"'Rebuild the application from current source. Do not modify any source files — just rebuild and deploy what is on disk.'\n" +
|
|
193
|
+
"Wait for the build to complete by polling artifact_status until it is no longer running.\n\n" +
|
|
194
|
+
"STEP 2: Finalize.\n" +
|
|
195
|
+
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " post-deploy " + taskId + "\n" +
|
|
196
|
+
"If the output contains DEPLOYED, deployment is complete.\n\n" +
|
|
197
|
+
"If artifact_edit failed, still run post-deploy to release the merge lock and clean up.\n" +
|
|
198
|
+
"Report the failure: { \"passed\": false, \"summary\": \"artifact deployment failed: [details]\" }.\n\n" +
|
|
199
|
+
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"deployed changes\", \"passed\": true }.\n" +
|
|
200
|
+
"No prose, no markdown, just the JSON object.";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
} else if (step.name === "QA") {
|
|
204
|
+
instructions = "Final QA testing. You are CODE-BLIND — do NOT read source code.\n" +
|
|
205
|
+
"To test, use artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\" with action \"getstate\" (args {}) to read current sessions, events, and tasks.\n" +
|
|
206
|
+
"Verify the fix by checking that session notes in the returned data now handle newlines correctly.\n" +
|
|
207
|
+
"You can also check specific data with the getevents action.\n" +
|
|
208
|
+
"Do NOT use artifact_inspect — it is async and will not return results inline.\n" +
|
|
209
|
+
"File follow-up tasks via artifact_invoke_action createtask on slug \"" + DASHBOARD_SLUG + "\" for related issues.\n" +
|
|
210
|
+
"If testing passes, your final response MUST be valid JSON and nothing else: { \"passed\": true, \"summary\": \"test results\" }.\n" +
|
|
211
|
+
"If testing fails, your final response MUST be valid JSON and nothing else: { \"passed\": false, \"summary\": \"failure details\" }.\n" +
|
|
212
|
+
"No prose, no markdown, just the JSON object.";
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Task event history — all phases except Review see the comment log
|
|
216
|
+
var eventPreamble = "";
|
|
217
|
+
if (step.name !== "Review") {
|
|
218
|
+
eventPreamble = "CONTEXT: First, fetch this task's event history for background.\n" +
|
|
219
|
+
"Call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"getevents\", args: { \"limit\": 100 }.\n" +
|
|
220
|
+
"Look through returned events for entries matching task_id \"" + taskId + "\". They contain notes and decisions from prior phases.\n\n";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
var stepResult;
|
|
224
|
+
try {
|
|
225
|
+
stepResult = await agent(
|
|
226
|
+
"Read the identity file at " + ORCH_PATH + "/identities/" + step.identity + ".md using the read tool, and embody that character fully.\n\n" +
|
|
227
|
+
"## Your Assignment\n\n" +
|
|
228
|
+
"Task: " + taskTitle + "\n" +
|
|
229
|
+
"Task ID: " + taskId + "\n" +
|
|
230
|
+
"Description: " + taskDescription + "\n" +
|
|
231
|
+
"Step: " + step.name + "\n" +
|
|
232
|
+
(step.name !== "Review" ? "Dashboard slug: " + DASHBOARD_SLUG + "\n" : "") +
|
|
233
|
+
"\n## Instructions\n\n" + eventPreamble + instructions + "\n\n" +
|
|
234
|
+
"CONSTRAINT: Do NOT call logevent or upsertagentsession — the workflow handles all phase tracking after your step completes.\n\n" +
|
|
235
|
+
"Stay in character. Do the work thoroughly.",
|
|
236
|
+
{
|
|
237
|
+
key: "work-" + step.name + (reworkCount > 0 ? "-r" + reworkCount : ""),
|
|
238
|
+
label: step.identity + ": " + step.name + " on \"" + taskTitle + "\"",
|
|
239
|
+
timeoutMs: 3600000,
|
|
240
|
+
schema: WORK_SCHEMA
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
log(step.name + " agent failed: " + (e.message || String(e)).slice(0, 500));
|
|
245
|
+
stepResult = null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// If schema retries were exhausted, block gracefully
|
|
249
|
+
if (!stepResult || typeof stepResult.summary !== "string") {
|
|
250
|
+
log(step.name + " closeout unrecoverable — blocking");
|
|
251
|
+
await agent(
|
|
252
|
+
"Record closeout failure.\n" +
|
|
253
|
+
"Call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"upsertagentsession\", args:\n" +
|
|
254
|
+
"{ \"id\": \"" + activeSessionId + "\", \"task_id\": \"" + taskId + "\", \"identity\": \"" + step.identity + "\", \"step\": \"" + step.name + "\", \"status\": \"blocked\", \"notes\": \"Agent failed to return structured result after retries\" }.\n" +
|
|
255
|
+
"Then call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"logevent\", args:\n" +
|
|
256
|
+
"{ \"task_id\": \"" + taskId + "\", \"type\": \"blocked\", \"message\": \"" + step.name + " agent failed after retries — workflow blocked\" }.",
|
|
257
|
+
{ key: "record-block-" + step.name, label: "Recording closeout failure", schema: { type: "object" } }
|
|
258
|
+
);
|
|
259
|
+
return {
|
|
260
|
+
__hatchWorkflowControl: "blocked",
|
|
261
|
+
result: {
|
|
262
|
+
blocked_reason: step.name + " agent failed after retries",
|
|
263
|
+
message: "The " + step.identity + " agent's work may be valid — structured output failed.",
|
|
264
|
+
task_id: taskId
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const summary = (stepResult.summary || "Step completed").slice(0, 2000);
|
|
270
|
+
const passed = stepResult.passed !== false;
|
|
271
|
+
const status = passed ? "completed" : "rejected";
|
|
272
|
+
|
|
273
|
+
// Capture mapper's spec for Build and Review
|
|
274
|
+
if (step.name === "Map" && passed) {
|
|
275
|
+
mapperSpec = summary;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await agent(
|
|
279
|
+
"Update the session and log the event.\n" +
|
|
280
|
+
"Call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"upsertagentsession\", args:\n" +
|
|
281
|
+
"{ \"id\": \"" + activeSessionId + "\", \"task_id\": \"" + taskId + "\", \"identity\": \"" + step.identity + "\", \"step\": \"" + step.name + "\", \"status\": \"" + status + "\", \"notes\": " + JSON.stringify(summary) + " }.\n" +
|
|
282
|
+
"Then call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"logevent\", args:\n" +
|
|
283
|
+
"{ \"task_id\": \"" + taskId + "\", \"type\": \"" + status + "\", \"identity\": \"" + step.identity + "\", \"message\": \"" + step.name + " " + status + " by " + step.identity + "\" }.",
|
|
284
|
+
{
|
|
285
|
+
key: "record-" + step.name + (reworkCount > 0 ? "-r" + reworkCount : ""),
|
|
286
|
+
label: "Recording " + step.name + " result",
|
|
287
|
+
schema: { type: "object" }
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Reproduce failure blocks the task
|
|
292
|
+
if (!passed && step.name === "Reproduce") {
|
|
293
|
+
log("Reproduction failed for task " + taskId + " — blocking");
|
|
294
|
+
await agent(
|
|
295
|
+
"Mark this task as blocked because reproduction failed.\n" +
|
|
296
|
+
"Call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"logevent\", args:\n" +
|
|
297
|
+
"{ \"task_id\": \"" + taskId + "\", \"type\": \"blocked\", \"message\": \"Reproduction failed — needs PM attention\" }.",
|
|
298
|
+
{ key: "block-repro", label: "Blocking: reproduction failed", schema: { type: "object" } }
|
|
299
|
+
);
|
|
300
|
+
return { status: "blocked", task_id: taskId, reason: "Reproduction failed" };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Review/QA rejection bounces to Build
|
|
304
|
+
if (!passed && (step.name === "Review" || step.name === "QA")) {
|
|
305
|
+
reworkCount++;
|
|
306
|
+
if (reworkCount > MAX_REWORK) {
|
|
307
|
+
log("Max rework attempts reached for task " + taskId + " — worktree preserved at .worktrees/" + taskId + " for manual inspection");
|
|
308
|
+
return { status: "blocked", task_id: taskId, reason: "Exceeded " + MAX_REWORK + " rework attempts after " + step.name + " rejection. Worktree preserved." };
|
|
309
|
+
}
|
|
310
|
+
rejectionNotes = summary;
|
|
311
|
+
i = BUILD_INDEX;
|
|
312
|
+
log(step.name + " rejected — bouncing to Build (rework #" + reworkCount + ")");
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Integrate failure blocks the task
|
|
317
|
+
if (!passed && step.name === "Integrate") {
|
|
318
|
+
log("Integration failed for task " + taskId + ": " + summary);
|
|
319
|
+
return { status: "blocked", task_id: taskId, reason: "Integration failed: " + summary };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Deploy failure blocks the task
|
|
323
|
+
if (!passed && step.name === "Deploy") {
|
|
324
|
+
log("Deploy failed for task " + taskId + ": " + summary);
|
|
325
|
+
return { status: "blocked", task_id: taskId, reason: "Deploy failed: " + summary };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
i++;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
await agent(
|
|
332
|
+
"Mark this task as done.\n" +
|
|
333
|
+
"Call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"updatetask\", args: { \"id\": \"" + taskId + "\", \"state\": \"done\" }.\n" +
|
|
334
|
+
"Then call artifact_invoke_action on slug \"" + DASHBOARD_SLUG + "\", action \"logevent\", args:\n" +
|
|
335
|
+
"{ \"task_id\": \"" + taskId + "\", \"type\": \"completed\", \"message\": \"All bugfix workflow steps complete.\" }.",
|
|
336
|
+
{ key: "task-done", label: "Completing task: " + taskTitle, schema: { type: "object" } }
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
log("Bugfix workflow complete for task " + taskId);
|
|
340
|
+
await agent("Clean up pinned lifecycle scripts: rm -rf " + RUN_LIB, { key: "cleanup-pins", label: "Cleaning pinned scripts", schema: { type: "object" } });
|
|
341
|
+
return { status: "ok", task_id: taskId, message: "Bugfix workflow complete for " + taskTitle };
|