We Used Gas Town to Build a Wasteland – an honest remediation gastown log A developer built Wasteland Wire, a working Mad Max fan site that aggregates real headlines from public RSS feeds, using the Gas Town multi-agent orchestration CLI to dispatch a single Claude-powered worker. The trial ran one task with one executing worker and no merge queue, and the writeup documents failed worker-start attempts alongside the successful run, noting that durable task state in Beads/Dolt is not durable model memory and that a separate worktree or tmux session is not a security sandbox. We Used Gas Town to Build a Wasteland A hands-on trial of multi-agent tooling, one actual coding agent, and a surprisingly good Mad Max fan website. The prompt was simple: let's try Gas Town by building a Mad Max fan website that aggregates post-apocalyptic news. The result was Wasteland Wire : an unofficial fan site with oversized condensed typography, a rust-and-sand palette, and an original road-to-the-sunset illustration. Underneath the wasteland aesthetic, it pulls real headlines from public RSS feeds. No invented articles. No decorative fake news cards. Try the live site: wasteland-wire.onrender.com https://wasteland-wire.onrender.com · Source: bdmorin/wasteland-wire https://github.com/bdmorin/wasteland-wire . It runs on Render's free tier, so an idle instance can take about a minute to wake. The website turned out well. Getting the agent orchestration running was less straightforward. This post records both sides of that experience—without treating workarounds as fixes. First: what does Gas Town actually do? At one point during setup, the obvious question came up: is gt just a proxy that executes Claude? Not quite. Claude does the coding; Gas Town organizes the work around it. Gas Town's Go CLI creates managed project repositories called rigs , allocates worker Git worktrees, records assignments in Beads/Dolt , launches external coding runtimes in tmux, and provides messaging and lifecycle tools. Workers are called polecats . Optional AI roles coordinate tasks, monitor workers, or process the merge queue. For this trial, the flow was: text Create a tracked issue → gt sling assigns it and creates a worker worktree → Claude implements the website → the worker records completion → a human-facing browser check verifies the result That distinction matters: durable task state is not the same thing as durable model memory. And a separate worktree or tmux session is not a security sandbox. We deliberately ran a narrow trial: one task, one executing worker, no automatic infrastructure boot, and no merge queue. There were failed worker-start attempts before the successful run, but this was not a multi-worker coordination benchmark. The website we asked for Wasteland Wire had to be a working application, not just a visual mockup: - Real RSS aggregation for Mad Max/post-apocalyptic entertainment, climate/environment, and science/resilience. - Publisher names, publication dates, source links, and short plain-text excerpts. - Category filters, search, newest/oldest sorting, and manual refresh. - Honest loading, empty, and upstream-error states. - Partial failures that do not erase headlines from working sources. - Original artwork and an explicit unofficial-fan disclaimer. - No paid news API or API keys. The implementation uses a plain Node HTTP server, rss-parser , and vanilla HTML/CSS/JavaScript. Fonts are self-hosted. There is no frontend build pipeline. Science and climate reporting are labelled as real-world news, not falsely presented as proof that an apocalypse is imminent. Actually driving Gas Town: the commands and prompts Here is the operational story missing from a simple architecture diagram. Commands in this section were executed inside the container unless prefixed with docker . Output excerpts are abridged; task and worker names are from the actual run. From “build a fan site” to a durable assignment We did not ask a Mayor to plan the project. The human-facing assistant wrote the product brief and explicitly dispatched one worker. That brief lived at /gt/website-brief.md , was initially committed as the project's README, and is now preserved in docs/BRIEF.md https://github.com/bdmorin/wasteland-wire/blob/main/docs/BRIEF.md . Its opening instructions were: Build and run an unofficial Mad Max fan website that aggregates real post-apocalyptic-themed news. This is a real working website, not a mockup. Theme: cinematic desert-industrial editorial, sand/charcoal/rust, oversized condensed headlines, strong typography, responsive layout, readable contrast. No copyrighted movie stills or claims of official affiliation. Original CSS/SVG artwork is preferred. The brief supplied the functional requirements, verification criteria, one already-checked feed, and concrete boundaries: no external repository writes, deployment, GitHub PRs, additional agents, or merge to main. It also told the worker not to fabricate news and to verify actual feed entries before choosing sources. We gave the project a disposable local bare origin, then registered it: bash git init --initial-branch=main /gt/trial-source cp /gt/website-brief.md /gt/trial-source/README.md git -C /gt/trial-source add README.md git -C /gt/trial-source commit -m "Define Wasteland Wire website requirements" git clone --bare /gt/trial-source /gt/trial-origin.git gt rig add wasteland file:///gt/trial-origin.git \ --local-repo /gt/trial-source --prefix ww --branch main Gas Town created a shared bare repository, a mayor clone, a refinery worktree, a Beads directory, and agent bookkeeping. Creating those directories did not mean those AI roles were running. The actual task creation command, run from the rig root, was: bash bd create --json \ --title 'Build and run Wasteland Wire Mad Max fan news website' \ --type task --priority 2 \ --description 'Implement all requirements in README.md and /gt/website-brief.md. Real RSS aggregation, polished responsive UI, filters/search/refresh, source attribution and honest failure states. Use Node.js. Run and verify the server; preserve work on a feature branch, no merging or deployment. Do not launch additional agents.' It returned ww-bx2 . That ID—not a conversation floating in a terminal—became the assignment we could inspect, reassign, and eventually close. Slinging the task: more than launching Claude bash gt sling ww-bx2 wasteland --agent claude \ --no-boot --no-convoy --no-merge --dry-run gt sling ww-bx2 wasteland --agent claude \ --no-boot --no-convoy --no-merge The dry-run previewed the orchestration: instantiate mol-polecat-work , bond it to ww-bx2 , update the issue to hooked with a worker assignee, and inject a startup prompt. The real dispatch created worker rust , allocated its worktree, instantiated workflow ww-wisp-cad , attached the issue, and enabled no-merge mode. In other words, the coding session started inside an already assigned project/workflow context. The flags mattered: - --no-boot : do not wake the rig's Witness/Refinery infrastructure. - --no-convoy : do not create an automatic work-tracking convoy for this single task. - --no-merge : preserve the feature branch for review instead of submitting it to the merge queue. None is a security sandbox or an enforced spending cap. What was the priming prompt? The observed interactive Claude launch included Gas Town's role settings and an initial message whose actionable text was: text Run gt prime --hook and begin work on your hook. Its generated banner identified the worker and rig. We are not reproducing the entire generated role document here; the key is that the startup message did not contain the whole website specification . It directed the agent to recover its role, attached assignment, and workflow from Gas Town. The successful headless worker chrome actually executed these commands, as recorded in its transcript: bash gt prime --hook 2 &1 | tail -150 gt prime --hook 2 &1 | grep -n "Step\|ww-bx2" | head -40 bd show ww-bx2 Those output-trimming pipelines were the worker's choices, not prerequisites for priming. The core operation was gt prime --hook . The prime output contained the hooked issue, attached mol-polecat-work formula, and eight workflow steps: 1. Load context and verify assignment. 2. Set up working branch. 3. Implement the solution. 4. Commit all implementation changes. 5. Self-review changes. 6. Build and sanity check. 7. Pre-merge rebase verification. 8. Submit work and self-clean. The output also directed the worker to inspect bd show ww-bx2 , explained how to preserve findings in issue notes, and specified the eventual gt done handoff. Build/test gate variables were empty for this newly created rig; we had not configured a pre-existing project test command. The worker added and ran the site's own checks rather than inheriting a configured gate suite. One revealing rough edge: the generic formula text described submitting to the merge queue even though the actual assignment had no merge: true . The effective no-merge behavior was confirmed by the completion state, not by assuming every paragraph of the generic prompt reflected that override. Watching and recovering the worker We observed the actual session rather than trusting a successful dispatch: bash gt peek wasteland/rust 70 gt session status wasteland/rust That is how we discovered Claude was in onboarding instead of building the site. We attempted to advance it using the supported messaging interface: bash gt nudge wasteland/rust '' --mode immediate This was an onboarding workaround attempt, not an implementation instruction. It did not solve interactive startup. We stopped/restarted sessions while investigating and ultimately registered the headless runtime described below. Re-dispatch retained the same task ID : bash gt sling ww-bx2 wasteland --agent claude-build \ --force --no-boot --no-convoy --no-merge The output made the state transition explicit: text Created polecat: chrome Bead already hooked to wasteland/polecats/rust, forcing reassignment... Sent LIFECYCLE:Shutdown to wasteland/witness for rust Burning 1 stale molecule s from previous assignment: ww-wisp-cad Formula wisp created: ww-wisp-p8v Formula bonded to ww-bx2 Work attached to hook status=hooked No-merge mode enabled work stays on feature branch Sending that lifecycle message did not mean a Witness was running to act on it; we supervised the trial ourselves. Once chrome was executing, gt peek wasteland/chrome exposed its streamed tool activity. This is the useful orchestration distinction: assignment and workflow history survived a failed runtime launch and explicit reassignment. Completion had its own recovery step The worker rebased onto origin/main , ran npm run check , and invoked: bash gt done --pre-verified --target main The first attempt refused completion because it detected uncommitted files. Its attempted auto-commit failed. The worker inspected Git state, added Gas Town-only workspace paths to .gitignore .beads/ , .claude/ , .runtime/ , and root CLAUDE.md , committed that exclusion, and retried the same completion command. This final remediation was absent from the initial account: the implementation was done, but runtime scaffolding still interfered with the clean-worktree completion check. Ignoring that scaffolding kept it out of the public website export. We checked the durable result: bash bd show ww-bx2 gt polecat status wasteland/chrome The issue was CLOSED , with close reason “No-merge work completed; merge queue skipped.” Chrome was done , its session stopped, and its branch remained polecat/chrome/ww-bx2+murp42sq . That is the end-to-end Gas Town story for this run: brief → bead → formula-backed assignment → primed worker → explicit recovery/reassignment → committed implementation → recorded no-merge completion. The remaining setup details below explain why getting to that path took repairs. The setup: where the friction was 1. Install the tools—but distinguish release from checkout The initial machine had Git, Go, tmux, SQLite, Claude, Homebrew, and Docker tooling, but no gt , bd , or dolt on PATH. bash brew install gastown Homebrew installed Gas Town 1.1.0 , Beads 1.3.1 , and Dolt 2.4.0 . Version and help commands worked. However, the inspected checkout declared 1.2.1 . Installing a packaged release is not testing that source tree. We built the repository's Docker image to exercise the checkout instead. The container build took roughly 399 seconds on this run. Because Git metadata was excluded from the image build context, the resulting CLI reported a dev version label. That label alone does not identify a release. 2. Start the database explicitly The container had an initialized headquarters, but gt dolt status reported that Dolt was not running. bash gt dolt start That started the isolated SQL server. We did not establish why it was stopped after bootstrap; an observed symptom is not a root-cause diagnosis. 3. Respect the schema-migration guard Creating the website rig exposed a Beads compatibility issue: 17 pending migrations, from schema v49 to v66 . Automatic migration was refused because the database was served through a shared Dolt server. That refusal protects other clients: promoting the schema can lock out older binaries. In our case, the server was newly created and isolated, so an explicit migration was appropriate. bash bd migrate schema The command's working directory mattered. An attempt from the mayor clone reported no Beads database; running from the rig root verified schema v66. Task creation then succeeded. This was a useful reminder: a working bd version command says nothing about whether bd can write to your actual database. 4. Account for bind-mount ownership Git rejected the HQ bind mount as having dubious ownership. We added the exact /gt directory to Git's safe-directory list inside the disposable container account. We did not disable that protection globally on the host. 5. Authentication does not cross the container boundary Claude was authenticated on the host, but not inside the container. We did not copy host credentials. The interactive login had to happen in a real terminal: bash docker exec -it gastown-sandbox claude auth login An automation-side attempt using -it failed because stdin was not a terminal. A non-TTY attempt produced an OAuth code prompt but was not suitable for unattended completion. After the user's login, claude auth status reported success. 6. Authenticated is not the same as ready The first gt sling created the worker and hooked the issue correctly. But the actual Claude session showed its first-run theme selector, then a login-selection screen—not task execution. Advancing the wizard through Gas Town's messaging interface did not resolve the problem. We also attempted an onboarding-config repair. That uncovered another operational trap: docker cp can leave a configuration file owned by the wrong user. The agent encountered EACCES until ownership was corrected. An orphaned login process from an earlier attempt was also found and stopped. We did not prove it caused the configuration rewrites observed during these attempts. Even after the configuration was readable, interactive worker startup still failed with startup blocked: no tmux server running . The exact cause of the interactive exit remained unresolved. 7. Prove inference separately, then use a headless runtime Before continuing, we tested authenticated Claude inference directly from the assigned worktree: bash claude -p 'Reply with READY only.' --dangerously-skip-permissions It returned READY . The same smoke check worked with Gas Town's settings file. We then registered a headless runtime through Gas Town: bash gt config agent set claude-build \ 'claude -p --verbose --output-format stream-json --dangerously-skip-permissions' Security note: automatic tool approval is appropriate here only because this was a disposable, isolated trial. It is not a recommendation for untrusted code or a credential-rich production environment. The assignment was re-dispatched to a fresh worker with: bash gt sling