← Portfolio/The Target Project
The Target Project · docs

Line up the work. Let the agent run it, one step at a time.

The Target Project is a small local hub. You write a workflow as a list of steps, and a coding agent — Claude Code or free-code — runs them in order, on one shared conversation. You watch the progress, approve the steps that need a human, and reuse the step lists you repeat. This page walks through it like a story.

Meet The Target Project

Think of it as a to-do list for a coding agent. Each item on the list is a step. The agent picks the first one, does it, then moves to the next — and because every step continues the same conversation, the agent remembers everything it did before.

You drive it from a web UI at http://127.0.0.1:8893 or from a target command line. Under the hood it is a tiny, dependency-free Node 24 service: a SQLite database, a hand-written HTTP server, and a bridge to agent-webhook-bridge (the thing that actually spawns the agent for each step).

The whole point is order and memory: steps run one after another, not in parallel, and each new step resumes the previous session. So a workflow reads as one long, continuous conversation — not a pile of unrelated jobs.

The story in five moves

You create a workflow. Give it a name, pick a working folder, choose the agent runtime and its permissions. A dedicated agent and hook are created for it automatically.

You add steps. Each step is a plain-language task, with optional acceptance criteria, retries, a retry interval, and a “needs a human” toggle.

You press Start. The first step is sent to the agent. When it finishes, the next selected step is sent — always resuming the same conversation.

You watch and steer. A progress bar and a badge track the run. A step that needs review pauses the whole workflow and waits for your Continue.

It finishes. When every selected step is done, the workflow is complete — and it can tell you on Slack that it’s done.

That’s the whole shape. The rest of this page is the details — with screenshots.

The web UI

The UI is a React app served by the hub. It has three tabs across the top — Workflows, Templates and Settings — and a small admin-token button on the right (the hub prints that token when it starts; the UI remembers it for next time).

The Workflows tab: a searchable, filterable list of workflows on the left, and the selected workflow's detail on the right with its status, progress bar, conversation context, and steps.
The Workflows tab. Left: the list, with search and status filters. Right: the selected workflow — its badge, progress bar, the conversation context block, and its steps. The brand reads “The Target Project”.

The list is sorted to put the work that needs you first: a workflow waiting on a human sits above even a running one, so the thing you most need to act on is at the top. Search filters by name or agent, and the status chips let you narrow to just running, failed, and so on.

Pick a workflow and the detail pane opens with everything in the order the work actually reads: the conversation context it starts from, the steps that run, and the conversation those steps produced. The page polls the hub every couple of seconds, so the progress bar and statuses stay live — and the selected workflow lives in the URL (#/w/<id>), so a reload or a shared link reopens it.

Create a workflow

Press + New and a dialog opens. It creates a dedicated agent and hook for the workflow, so each one is fully isolated.

The New workflow dialog: fields for Name, Working directory (with a browse button), Agent runtime, Agent permissions, and Start from template.
The New workflow dialog. Name, a working directory (typed or browsed), the agent runtime, the agent permissions, and an optional template to seed the steps from.

What you pick here

  • Working directory — where the agent works. Leave it empty for a private sandbox under ~/.target/sandboxes/, or point it at a real repo. The button browses the server’s folders.
  • Agent runtimeClaude Code (the default) or free-code. The engine, judges and retries behave the same either way; only the spawned CLI and the session-id shape differ. The runner is fixed for the workflow’s life — to switch, make a new workflow.
  • Agent permissions — how much the agent may do. Read-only by default: it can answer but can’t write files or run commands. acceptEdits lets it write files inside its sandbox. bypassPermissions removes every check — so it asks you to confirm the risk first.
  • Start from template — copy a saved step list into the new workflow (more on templates below).
The New workflow dialog with bypassPermissions selected, showing a checkbox that reads I understand the risk. Every step of this workflow will be able to run any command on this machine with no permission checks.
The bypass risk gate. bypassPermissions can’t be turned on by accident — you have to tick the box that spells out exactly what it means.

The conversation context is intentionally not part of this dialog. You add it afterwards, from the workflow’s detail pane — see The conversation.

Steps and their statuses

A step is one task written in plain language. Each step has a checkbox (to choose whether it runs on the next Start), a status badge, and a row of actions: ▶ Run (run just this step, out of order, on a fresh session), Abort (force-fail a stuck step), Edit and Remove.

The statuses, in the order a step can move through them

pending queued running judging waiting done failed
  • pending — sitting in the list, not yet run.
  • queued — sent to the agent, waiting its turn. Runs are serialized per working directory, so a second step on the same repo waits behind the first. Its timeout clock doesn’t start until it actually begins.
  • running — the agent is working on it. Next to the elapsed time you’ll see how lively it is: “active 8s ago” or “no activity 6m”.
  • judging — the step had acceptance criteria, finished its work, and is now self-evaluating the result.
  • waiting — the step was flagged for a human review and has finished its work; the workflow pauses until you press Continue. (See Human review & waiting.)
  • done — finished and accepted.
  • failed — the step (and therefore the workflow) ended in error.
badge = bar

The workflow’s badge and its progress bar are computed from the same step data, so they can never disagree. A run that ends with a step still failed ends the workflow failed — never completed with a red bar.

Run only the steps you tick

Start, Resume and Start-over send exactly the ticked steps. Tick nothing and the button disables and tells you why, rather than silently doing nothing. A single Start button maps to the right action for the status: start when draft, resume when paused, restart when completed or failed.

Judge, retries and retry intervals

A step can carry an acceptance criterion — a short sentence describing what a good result looks like. If it has one, finishing the work doesn’t mark the step done. Instead the step enters a judge phase: a second run, on the same session, asks the agent to look at its own result and answer a yes/no verdict.

  • Accepted → the step becomes done and the engine moves on.
  • Rejected → the step is retried, with the judge’s feedback, until the retry budget runs out.
  • No criterion → no judge phase; the step is a plain one-shot.

Each step has a Max retries and a retry interval (seconds to wait before each re-run). You set them when you add or edit a step.

The inline step editor: a task description, an optional acceptance criteria field, a Manual review switch turned on, and Max retries and Interval (s) number fields.
Editing a step. Task description, optional acceptance criteria, the Manual review switch, and Max retries / Interval (s). The interval only matters with more than one retry, so it’s disabled below that.
one retry budget, many causes

A judge rejection and a timeout both draw from the same retry budget. A step that times out with retries left is retried (the hung run is killed, the workdir lock freed, and the step re-dispatched on the same session with a note that the last attempt timed out) — only when the budget is spent does it fail for good. The retry count shows as retryCount/maxRetries next to the step.

Require a human review — the waiting status

Some steps shouldn’t just run and move on — you want to look at the result before the workflow continues. Turn on Manual review on a step, and when that step finishes its work (and passes its judge, if it has one) it does not become done. It becomes waiting, and the whole workflow pauses right there.

A step in the waiting status: a yellow waiting badge, a manual review tag, the result shown for review, and a Continue button.
A step waiting for you. The waiting badge, the “manual review” tag, the result laid out for you to read, and a single Continue button. Nothing else runs until you press it.

How the gate behaves, in plain words:

  • Only accepted work waits. If the step fails, or its judge rejects it past the retry budget, it fails as usual — the gate never holds broken work.
  • Continue is the only way forward. While a workflow is waiting, Start is refused, the step can’t be edited or re-run, and the timeout watchdog leaves it alone — a human may take as long as they need.
  • Restart is the way out if you don’t want to approve. It resets the held step and runs it again.
  • It works on one-off runs too. Press ▶ on a gated step and it holds the same way; continuing it just settles the badge back instead of advancing a run.
heads up

If you’ve turned on notifications, the hub can ping you on Slack the moment a step starts waiting — so you don’t have to keep the UI open. The message names the workflow, the step and what to check. The notification is advisory: the hold is the feature; a failed delivery changes nothing.

Templates

If you keep writing the same sequence of steps, save it as a template. A template is a reusable, ordered list of steps — with their acceptance criteria, retries, retry intervals and manual-review flags — that you can use to seed a new workflow or append to an existing one. Templates never execute themselves; they’re just a blueprint.

The Templates tab: a searchable, tag-filterable list of templates on the left, and the editor for a selected template on the right showing its steps with descriptions, acceptance criteria, retries, intervals and manual review toggles.
The Templates tab. Left: the list, with search and tag filters. Right: the editor — name, tags, and each step’s description, acceptance criteria, Max retries, Interval (s) and Manual review toggle. Reorder with the arrows, remove with ×.

Two ways to use a template:

  • Seed a new workflow — pick it in the Start from template field of the New-workflow dialog, and the workflow is born with those steps.
  • Append to an existing workflow — open Add step on a workflow, choose a template, and press Append. Steps whose description already exists are skipped, so you can’t double-add.

The manual-review flag travels with the step, so a checklist whose sign-off step is gated stays reusable. Search filters by name or tag; the tag chips narrow to one tag at a time.

One conversation, start to finish

A workflow is one continuous conversation on a shared session. Two blocks in the detail pane are about exactly that: the conversation context it starts from, and the conversation those steps produced.

Conversation context — the preamble

The context is optional background that every step should share — the audience, constraints, definitions, a persona. It is injected once, before the first step of a fresh run, so later steps (which resume the session) already carry it and never need it repeated.

Once it’s been injected, the context is locked: the field becomes read-only and Save is disabled, because the agent is already running under it. To change it, start the workflow over — that resets the flag and starts a fresh conversation, which re-injects whatever the context is by then. A badge shows the state: injected, pending, or none.

The conversation block — the live session

The Conversation block: an Open conversation button, the harness and session id, and a context-window meter showing tokens used out of the window with a percentage.
The Conversation block. The harness and session id, a context-window meter (tokens used vs. the window, with a percentage that turns amber then red as it fills), the turn count and token totals — and an Open conversation button that opens a terminal already resuming this session.

Open conversation opens a terminal on this machine, cd’d into the workflow’s working directory, running the harness’s resume command (claude --resume <id> or free-code --session <path>) so you can pick up the conversation by hand.

Settings & notifications

The Settings tab holds hub-wide preferences. Right now there’s one section: Notifications. Flip the master switch on, enter your Slack username, and Save — the hub will message you (a direct message on Slack) when a step starts waiting for your review, and again when a workflow finishes.

The Settings tab: a Configuration heading, a Notifications section with a Receive notifications switch turned on and a Slack username field.
The Settings tab. A master switch and, when it’s on, the Slack username to message. Preferences are stored by the hub, so every browser sees the same values; one Save commits them together.
how it reaches Slack

The hub talks to the same Slack MCP server the agent does, using the OAuth token Claude Code stores in ~/.claude/.credentials.json. If that login can’t be confirmed, nothing is sent — delivery is strictly advisory and never affects the workflow’s state.

Rich text inputs

Every multi-line field — step descriptions, acceptance criteria, the conversation context, template steps — has a small expand button in its corner. Click it to open a larger editor with a formatting toolbar: bold, italic, headings, and bulleted or numbered lists.

The expandable rich-text editor popup: a toolbar with bold, italic, bulleted list, numbered list, heading and normal-text buttons, and a large editor area showing a heading, a bold label, an italic label and a bulleted list.
The rich-text popup. A comfortable editor with a formatting toolbar. Press OK to write the result back into the field; Cancel (or Escape) discards the popup’s edits. The text is stored as Markdown, since everything here becomes plain text handed to the agent.

On OK the edited text is serialized to Markdown (**bold**, - bullets, # headings) and written back through the field’s normal save path. The button hides itself while a field is read-only — for example, a conversation context that’s already locked.

Safety: timeouts, permissions and abort

A step is timed out for going quiet, not for taking long

A running step isn’t failed on a wall clock. The hub watches the files the agent writes while it works (Claude transcripts — subagent transcripts included — free-code session files, and the run log) and only declares a timeout when nothing has changed for stepIdleTimeoutMs (default 10 minutes), or when the step blows past the absolute ceiling stepHardTimeoutMs (default 6 hours) however busy it looks. A step that’s genuinely working is left alone for as long as it keeps working.

Queued steps have a fair clock

A step queued behind another on the same repo isn’t timed out while it waits its turn — its clock starts only when the broker reports the run actually began. A separate queuedTimeoutMs (default 6h) is the safety net for a broker that never calls back.

Permissions are opt-in

By default an agent can answer but can’t write files or run commands. Writing needs acceptEdits at creation, scoped to that agent’s own sandbox. bypassPermissions removes every check and demands an explicit risk confirmation.

Abort a stuck step

If a step’s dispatch never calls back, it blocks everything — ▶ won’t re-run an in-flight step and restart is disabled while the workflow is running. The Abort button force-fails just that step (preserving its session so Open conversation still works) and kills the spawned agent process on the broker, freeing the workdir lock so other workflows on the same repo can proceed. Then ▶ re-run it.

local only

The hub binds to 127.0.0.1 — nothing listens on the network. Every mutating route needs the admin token (a Bearer header, checked with a timing-safe comparison), and the agent’s result callbacks are authenticated with a random per-step token instead.

Install & run

It runs straight from the repo checkout (not on npm). Needs Node 24+.

git clone <this repo> && cd target
npm run target:install

That one command installs the hub, builds the web UI, and vendors agent-webhook-bridge (gitignored) — idempotent, safe to re-run. Set AWB_DIR to reuse an existing awb clone instead.

npm start

Brings up both the awb broker (127.0.0.1:8890) and the hub (127.0.0.1:8893), waits for both ports, and opens the UI in your browser. It holds the foreground; Ctrl-C stops them together. The hub prints its admin token on startup — the UI asks for it once, the CLI reads it from config automatically.

where state lives

Everything lives under ~/.target/: config.json, the SQLite target.db, a sandbox per agent, and a <slug>-<id>.md progress file per workflow (rewritten on every change). Set TARGET_HOME=/your/path before any command to use a different location.

Quick reference

The target CLI

CommandWhat it does
target create <name>Creates a workflow (+ its agent/hook). Flags: --workdir, --permission-mode, --runner claude|free-code, --yes-bypass-risk.
target set-context <id> "<text>"Sets (or clears, with "") the conversation context.
target add-step <id> <description>Appends a step.
target list / target show <id>List workflows / show a workflow’s steps.
target run <id>Start (or continue) sequential dispatch.
target pause / resume / restart <id>Stop dispatching / undo a pause / reset every step and start over.

If target isn’t linked, every command works as node hub/cli.ts … from the repo root.

Handy npm scripts

ScriptWhat it does
npm run target:installOne-command bootstrap (hub + UI build + awb). Idempotent.
npm startBoots awb + the hub, opens the UI, holds the foreground.
npm testRuns the hub’s tests against a throwaway TARGET_HOME.
npm run typecheckType-checks the hub and the UI.
npm run ui:devVite dev server on :5173 with hot reload, proxying /api to a hub started separately.
npm run ui:buildBuilds the UI into hub/ui/dist.

How a step flows

hub -> POST to the workflow's awb hook {stepId, input, callbackUrl, startedCallbackUrl} + secret, session to resume awb -> queued (waiting on the workdir lock) serialized per workdir; the lock survives broker restarts awb -> POST startedCallbackUrl {started:true} the instant the lock is acquired hub -> step running (idle clock starts here) awb -> spawns claude or free-code the step's description, with the subagent instruction appended awb -> POST callbackUrl {ok, result, session_id} when the run finishes hub -> step done . judging . or waiting done if no criteria; judging if it has them; waiting if gated hub -> next selected step dispatched until none remain, then the workflow is completed

Configuration (~/.target/config.json)

FieldDefaultMeaning
host / port127.0.0.1 / 8893Bind address and port (kept away from awb’s 8890).
adminTokenrandom, on first runBearer token for every mutating route. Printed at startup.
stepIdleTimeoutMs600000 (10 min)No sign of progress for this long → stalled. This is the timeout that matters.
stepIdleWarnMs180000 (3 min)When the UI starts showing a step as idle. Display only.
stepHardTimeoutMs21600000 (6 h)Absolute ceiling from started_at, however active.
queuedTimeoutMs21600000 (6 h)A queued step whose run never started.
maxInputBytes65536 (64 KiB)Request body cap; oversized bodies get 413.

HTTP API (the routes the UI and CLI use)

EndpointWhat it does
GET /api/workflowsList workflows with progress % (expires stale steps first).
POST /api/workflowsCreate a workflow (+ its hook). Body: {name, workdir?, permissionMode?, runner?, acceptBypassRisk?, templateId?}.
GET /api/workflows/:idWorkflow detail + its steps.
PATCH /api/workflows/:id/contextSet or clear the conversation context.
POST /api/workflows/:id/stepsAdd a step. Body: {description, acceptanceCriteria?, maxRetries?, retryIntervalSeconds?, manualReview?}.
POST /api/workflows/:id/steps/:stepId/runRun one step now, out of order, on a fresh session.
POST /api/workflows/:id/steps/:stepId/abortForce-fail a stuck step, preserving its session, and kill the agent process on the broker.
POST /api/workflows/:id/steps/:stepId/continueRelease a step’s manual-review hold.
POST /api/workflows/:id/start | pause | resume | restartRun control. Optional {stepIds: [...]} to restrict which steps run.
GET/POST/PUT/DELETE /api/templates[/:id]List, create, update and delete templates.
GET/PUT /api/settings/notificationsRead and save the notification preferences.
POST /api/steps/:id/resultawb’s result callback (per-step ?token=, not the admin token).