Workspace & state

Every run gets a /workspace. It's where your program runs, and it's scratch unless you say otherwise. That's the whole model: a run does its job and leaves nothing behind, unless you name the part that should outlive it.

Naming that part is how a workflow gets better over time instead of starting cold every morning — a cache it reuses, an index it extends, notes an agent keeps.

You run in the workspace

Your program's working directory is the workspace (and so is HOME), on every runner: your laptop, hosted runs, and your own self-hosted runners. So a relative path needs no ceremony:

import { writeFileSync } from "node:fs";

writeFileSync("notes.md", "hello");   // → the run's workspace

When the run ends, that file is gone — the machine is destroyed. For most workflows that's right. If you want an absolute path (to hand to a tool, or to log), runtime.workspaceDir is the same place.

Making things stick

Name the directories that should survive. They're restored before your program starts on the next run:

export const meta = {
  slug: "triager",
  triggers: [{ kind: "cron", expr: "0 9 * * *" }],
  workspace: { persist: ["cache", "state"] },   // these come back; everything else is scratch
};

There's also { persist: true }, which keeps the whole workspace. It's convenient and it's the one that bites: if your workflow clones a repo or runs npm install, it hauls all of that to storage on every single run — toward the size cap and your storage quota. A workspace is mostly scratch with a small part that compounds. Say which part.

Agent memory

An agent() call can keep its own notes, and that directory persists automatically — no workspace declaration needed:

await agent("Triage today's failures. Note anything you learn.", {
  memory: "triager-notes",
});

The agent gets file tools scoped to that directory and reads it at the start of each turn, so next run it picks up where it left off. Your program code can read and write the same files (triager-notes/…). Point two agents at the same path to share what they learn, or separate paths to keep them independent.

One workspace per environment

If you run the same workflow in staging and production, each keeps a separate workspace. They never see each other's state.

That matters because an environmentis chosen when the run is triggered, not in your code: one program, several environments, different secrets and variables. Staging's cache has no business becoming production's starting point. Runs with no environment share a base workspace.

When it saves

At the end of every run — including runs that fail — and before any long sleep.

Failures persist on purpose. If a run spends twenty minutes learning something and then dies on the last step, you want that next time, not a blank slate. The cure for bad state isn't throwing away good state; it's reset.

The exception is a hard crash. If the machine dies outright, nothing is saved and the next attempt starts from the last save. So treat persistence as saved at checkpoints, not saved continuously, and checkpoint expensive side effects (push commits incrementally) rather than relying on the workspace to hold half-finished work.

When state goes wrong

It will, eventually: a cache poisons, an agent learns something untrue, a failed run leaves a half-written index. So you can look, and you can clear:

$ boardwalk workspace show triager
Persistent workspace · triager

  production           4.2 MB   last written 3h ago
  (base)               275 B    last written 2d ago

$ boardwalk workspace reset triager --environment production --yesreset the persistent workspace of triager for production.

Reset clears only the state— the workflow, its triggers, and its run history are untouched. That's the point of it existing separately from deleting the workflow. It's on the workflow's dashboard page too, and available to agents over MCP.

One caveat: it doesn't interrupt a run already in flight. That run holds its own workspace and saves at the end, overwriting your reset. Disable the workflow first if it's on a schedule.

Sharing, size, and where it lives

  • Two runs at once share one workspace, last-writer-wins. If your state can't tolerate tearing, set concurrency: { mode: "serial" }.
  • It counts toward your storage.A snapshot is capped at 512 MB, and what you keep is part of your org's quota — another reason to name directories rather than keep everything.
  • Self-hosted keeps its own. A self-hosted runner persists to its own disk; we never store your workspace. The semantics are identical — only the location differs.
  • It isn't a shared filesystem.Each workflow (and each environment) gets its own; runs don't read each other's. To publish a file out of a run, use artifacts.

If you keep one thing: ask what this workflow should know tomorrow that it doesn't know today. Persist exactly that. A cache, an index, a memory — not a repo you can re-clone, and not node_modules.