Skip to content

Commit 0a6bd35

Browse files
committed
Harden workflow runner for production
1 parent dbd7ebc commit 0a6bd35

38 files changed

Lines changed: 1547 additions & 239 deletions

‎README.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,24 @@ from Codex or from the CLI.
3737

3838
## What it includes
3939

40-
- `bug-sweep`: broad codebase review, adversarial verification, repro planning,
41-
and synthesis.
40+
- `bug-sweep`: bounded codebase review, adversarial verification, repro
41+
planning, and synthesis.
42+
- `bug-sweep-deep`: larger opt-in fanout for deeper bug hunts.
4243
- `release-diff-review`: release-blocker review against a branch or diff.
4344
- `security-auth-review`: auth, permission, injection, secret, sandbox, and MCP
4445
boundary review.
4546
- Live dashboard with phases, agent rows, token/tool/time metrics, detail view,
4647
pause/resume/stop/restart/save controls, and final report path.
48+
- Workflow definitions can explicitly request up to 64 concurrent workers and
49+
2000 total agents.
4750
- Durable run state under `${CODEX_HOME:-~/.codex}/codex-workflows/projects/<project-hash>/runs/<run-id>/`
4851
by default, so read-only bug hunts do not dirty the target repo.
4952
- Project-local storage remains available with `storageScope: "project"` or
5053
`--storage-scope project`.
54+
- Downstream verify, probe, and synthesize agents receive prior phase findings
55+
as explicit context instead of guessing at earlier results.
56+
- Detached worker heartbeats let status reads and dashboards identify orphaned
57+
runs when a background workflow process dies.
5158
- Isolated workflow script loading through QuickJS. Workflow scripts define
5259
phases and agent prompts; they do not get direct filesystem, shell, network,
5360
process, or Node built-in access.
@@ -85,6 +92,7 @@ pnpm test
8592
pnpm cwf validate workflows/bug-sweep.workflow.js
8693
pnpm cwf run workflows/bug-sweep.workflow.js --watch --adapter simulate
8794
pnpm cwf run workflows/bug-sweep.workflow.js --watch --adapter auto --model gpt-5.4-mini
95+
pnpm cwf run workflows/bug-sweep-deep.workflow.js --watch --adapter auto --model gpt-5.4-mini
8896
```
8997

9098
Useful local commands:

‎docs/architecture.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ started by either the CLI or MCP server.
4040

4141
MCP launches use a detached worker process. That matters: a workflow should keep
4242
running if the Codex thread disconnects or the MCP request finishes.
43+
Detached workers write heartbeats into `status.json`; status/list/dashboard
44+
reads reconcile stale runs when the recorded runner process is gone and the
45+
heartbeat has aged out.
4346

4447
## Resume and restart
4548

@@ -55,10 +58,17 @@ relaunches the workflow in resume mode.
5558
Workflow scripts are loaded in QuickJS. The script can define workflow metadata,
5659
phases, and agent prompts. It does not get Node globals, direct filesystem
5760
access, shell access, network access, process env, or dynamic imports.
61+
Workflow definitions may request up to 64 concurrent workers and 2000 total
62+
agents; the actual worker count is still determined by the agents declared in
63+
the workflow phases.
5864

5965
Actual code review work happens in Codex worker adapters, under explicit sandbox
6066
and approval settings.
6167

68+
Agents may declare `contextFrom` to receive prior phase findings. Built-in
69+
workflows use that for verify, probe, and synthesize phases so later agents
70+
review concrete earlier claims instead of generic instructions.
71+
6272
## Generated plugin
6373

6474
`pnpm validate:plugin` builds the TypeScript packages, bundles the CLI and MCP

‎docs/claude-workflows-parity.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ Use a small DSL that mirrors Claude's model while staying vendor-neutral:
139139
export default workflow({
140140
name: "branch-review",
141141
version: "1",
142-
maxConcurrency: 16,
143-
maxAgents: 1000,
142+
maxConcurrency: 64,
143+
maxAgents: 2000,
144144
async run({ args, agent, phase, checkpoint, budget }) {
145145
const files = args.files;
146146

‎docs/plugin-distribution.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ codex plugin add codex-workflows@codex-workflows
2626
Pinned install:
2727

2828
```bash
29-
codex plugin marketplace add robzilla1738/Codex-Workflows --ref v0.1.0
29+
codex plugin marketplace add robzilla1738/Codex-Workflows --ref v0.2.0
3030
codex plugin add codex-workflows@codex-workflows
3131
```
3232

@@ -82,7 +82,7 @@ reload plugin skills and MCP tools.
8282
7. Tag and push:
8383

8484
```bash
85-
git tag v0.1.0
85+
git tag v0.2.0
8686
git push origin main --tags
8787
```
8888

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codex-workflows",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"private": true,
55
"type": "module",
66
"license": "MIT",

‎packages/cli/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codex-workflows/cli",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"type": "module",
55
"license": "MIT",
66
"bin": {

‎packages/cli/src/dashboard.test.tsx‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ const fixture: RunSummary = {
4545
model: "Codex",
4646
sandbox: "read-only",
4747
status: "running",
48-
tokens: 138500,
48+
tokens: 0,
4949
tools: 95,
5050
elapsedMs: 554000,
51-
attempts: 1
51+
attempts: 1,
52+
startedAt: new Date(Date.now() - 2 * 60 * 1000).toISOString(),
53+
lastActivityAt: new Date(Date.now() - 15 * 1000).toISOString(),
54+
lastMessage: "command_execution"
5255
}
5356
],
5457
totals: {
@@ -70,4 +73,18 @@ describe("DashboardFrame", () => {
7073
expect(lastFrame()).toContain("find:reflow-engine");
7174
expect(lastFrame()).toContain("tok");
7275
});
76+
77+
it("shows pending token metrics and activity for running agents", () => {
78+
const { lastFrame } = render(
79+
<DashboardFrame
80+
summary={fixture}
81+
width={88}
82+
selectedPhaseId="find"
83+
selectedAgentId="find:concurrency"
84+
/>
85+
);
86+
expect(lastFrame()).toContain("tok pending");
87+
expect(lastFrame()).toContain("idle");
88+
expect(lastFrame()).toContain("command_execution");
89+
});
7390
});

‎packages/cli/src/dashboard.tsx‎

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ const phaseColor = (status: string, active: boolean) => {
5353
return "gray";
5454
};
5555

56+
const effectiveAgentElapsed = (agent: AgentSummary) =>
57+
agent.status === "running" && agent.startedAt
58+
? Math.max(0, Date.now() - new Date(agent.startedAt).getTime())
59+
: agent.elapsedMs;
60+
61+
const formatAgentTokens = (agent: AgentSummary) =>
62+
agent.status === "running" && agent.tokens === 0 ? "tok pending" : `${formatCount(agent.tokens)} tok`;
63+
64+
const formatActivity = (at?: string) => {
65+
if (!at) {
66+
return "no activity";
67+
}
68+
return `idle ${formatDuration(Math.max(0, Date.now() - new Date(at).getTime()))}`;
69+
};
70+
5671
export function DashboardFrame({
5772
summary,
5873
width,
@@ -168,8 +183,8 @@ export function DashboardFrame({
168183
</Text>
169184
</Box>
170185
<Text color="gray">
171-
{formatCount(agent.tokens)} tok · {agent.tools} tools ·{" "}
172-
{formatDuration(agent.elapsedMs)}
186+
{formatAgentTokens(agent)} · {agent.tools} tools ·{" "}
187+
{formatDuration(effectiveAgentElapsed(agent))}
173188
</Text>
174189
</Box>
175190
))}
@@ -197,11 +212,28 @@ export function DashboardFrame({
197212
</Text>
198213
))
199214
) : (
200-
<Text color="gray">
201-
{selectedAgent.status === "running"
202-
? "Agent is running. Live token/tool metrics update above."
203-
: truncateMiddle(selectedAgent.error ?? selectedAgent.prompt, width - 4)}
204-
</Text>
215+
<>
216+
<Text color="gray">
217+
{selectedAgent.status === "running"
218+
? truncateMiddle(
219+
`Agent is running. ${formatActivity(
220+
selectedAgent.lastActivityAt ?? selectedAgent.startedAt
221+
)}. ${selectedAgent.lastMessage ?? "Waiting for worker events."}`,
222+
width - 4
223+
)
224+
: truncateMiddle(selectedAgent.error ?? selectedAgent.prompt, width - 4)}
225+
</Text>
226+
{selectedAgent.lastActivityAt && selectedAgent.status !== "running" ? (
227+
<Text color="gray">
228+
{truncateMiddle(
229+
`Last activity: ${selectedAgent.lastActivityAt}${
230+
selectedAgent.lastMessage ? ` · ${selectedAgent.lastMessage}` : ""
231+
}`,
232+
width - 4
233+
)}
234+
</Text>
235+
) : null}
236+
</>
205237
)}
206238
</Box>
207239
) : null}

‎packages/cli/src/index.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const storeFor = (cwd: string, storageScope: StorageScope = "codex-home", storeR
8585
program
8686
.name("cwf")
8787
.description("Codex workflow-as-code runtime")
88-
.version("0.1.0");
88+
.version("0.2.0");
8989

9090
program
9191
.command("__run-worker", { hidden: true })
@@ -183,6 +183,7 @@ program
183183
const runs = await store.listRuns();
184184
console.log("Built-in workflows:");
185185
console.log(" workflows/bug-sweep.workflow.js");
186+
console.log(" workflows/bug-sweep-deep.workflow.js");
186187
console.log(" workflows/release-diff-review.workflow.js");
187188
console.log(" workflows/security-auth-review.workflow.js");
188189
console.log("");
@@ -271,6 +272,7 @@ program
271272
const cwd = path.resolve(options.cwd);
272273
const storeRoot = options.storeRoot ?? defaultStoreRoot(cwd, options.storageScope);
273274
const store = storeFor(cwd, options.storageScope, storeRoot);
275+
await store.clearControl(runId);
274276
const summary = await store.markAgentForRestart(runId, agentId);
275277
const manifest = await store.readManifest(runId);
276278
await runWorkflow({
@@ -299,7 +301,6 @@ program
299301
storeRoot,
300302
resume: true
301303
});
302-
await store.writeControl(runId, { type: "restart-agent", at: nowIso(), agentId });
303304
console.log(`restarted ${agentId} in ${runId}`);
304305
});
305306

‎packages/codex-adapter/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codex-workflows/codex-adapter",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"type": "module",
55
"license": "MIT",
66
"main": "dist/index.js",

0 commit comments

Comments
 (0)