Why Your OpenFang Agent Forgot Its Data (And How to Fix It)

Heads up: Everything in this post was tested on OpenFang v0.5.1. The project is still in early stages and things can change between releases. If something isn’t working, always cross-check with the latest OpenFang docs. It’ll be more up to date than this post.

I ran into this with a job search tracker I’d been building. The agent had weeks of application data stored locally. I updated the skill, respawned the agent, and suddenly it was pulling from the old global skill instead of my updated workspace one. The data was still there. The agent just wasn’t looking at the right folder anymore.

Once I understood the three-layer structure OpenFang uses, the fix was obvious. But nobody explains it clearly upfront.

The three layers every OpenFang agent has

1. The template

This is the blueprint. It lives in your OpenFang repo:

openfang/agents/job-search/agent.toml

It defines the agent’s name, model settings, capabilities, and which skills it should load. It holds no data. Think of it like a recipe card, not the meal.

2. The workspace

This is the real working folder. OpenFang creates it automatically the first time you spawn the agent:

~/.openfang/workspaces/job-search/
├── data/          ← your SQLite databases and files
├── sessions/      ← conversation history
├── memory/        ← agent memory
├── logs/
└── skills/        ← workspace-local skill overrides

Your actual data lives here. This folder persists across respawns. When you want to update a skill without losing your data, this is the folder that needs to stay intact.

3. Skills: global vs workspace-local

Skills can live in two places:

  • Global: ~/.openfang/skills/job-search/ (shared across all agents, fallback)
  • Workspace-local: ~/.openfang/workspaces/job-search/skills/job-search/ (private to this agent only)

When both exist, the workspace-local skill takes priority. That’s the intended behavior. The problem is when the agent loses track of which workspace it belongs to and silently falls back to the global one.

What actually goes wrong

Here’s the exact sequence that causes the bug:

  1. You update the skill inside the workspace
  2. You respawn the agent (or reinstall OpenFang)
  3. OpenFang creates a fresh agent but doesn’t know which workspace to attach it to
  4. It falls back to the global skill at ~/.openfang/skills/job-search/
  5. Your data is still sitting in the workspace folder. The agent just isn’t pointing at it anymore

It looks like the agent forgot everything. But the data is fine. The link is broken.

Before you hardcode anything: know how OpenFang resolves the workspace path

OpenFang decides the workspace folder using this priority order:

  1. workspace field in agent.toml (strongest override, always wins)
  2. workspaces_dir in the main OpenFang config (sets the default base folder for all agents)
  3. Default fallback: ~/.openfang/workspaces/<agent-name>/

If OPENFANG_HOME is set in your environment, it replaces ~/.openfang/ throughout. So the default becomes $OPENFANG_HOME/workspaces/<agent-name>/.

Check what you have set before writing any paths:

echo $OPENFANG_HOME

If it prints a path, that’s your base. If it prints nothing, the default ~/.openfang/ applies.

One thing worth knowing: OpenFang names the workspace folder after the agent name, not the agent ID. So for an agent named job-search, the folder is always workspaces/job-search/ regardless of the UUID assigned at spawn time. There is no CLI command that directly prints a running agent’s workspace path, so knowing this resolution order is the only reliable way to find it.

The fix: pin the workspace path in your template

Open your agent.toml and add one line:

workspace = "~/.openfang/workspaces/job-search"

Here’s a full working example for the job search agent:

name = "job-search"
version = "0.1.0"
description = "Tracks job applications, interviews, and follow-ups locally."
author = "you"
module = "builtin:chat"
workspace = "~/.openfang/workspaces/job-search"

[model]
provider = "default"
model = "default"
max_tokens = 4096
temperature = 0.1
system_prompt = """You are a private job search assistant.

Rules:
- Use the job search tools to log and query applications.
- Never guess application counts or statuses; always query first.
- Keep answers concise and factual.
"""

[resources]
max_llm_tokens_per_hour = 50000
max_concurrent_tools = 3

[capabilities]
tools = ["file_read", "file_write", "shell_exec"]
memory_read = ["*"]
memory_write = ["self.*"]

Now every time you spawn this agent, OpenFang reads the template, sees the fixed workspace path, attaches to the right folder, and loads the local skill at ~/.openfang/workspaces/job-search/skills/. Your data is intact. The updated skill is used.

The right workflow for updating a local skill

Once the workspace is pinned, this is the process every time:

  1. Edit skill files in ~/.openfang/workspaces/job-search/skills/job-search/
  2. Restart OpenFang to reload the skill registry:
openfang stop
openfang start
  1. If you need to respawn the agent:
openfang agent spawn openfang/agents/job-search/agent.toml

The application data in ~/.openfang/workspaces/job-search/data/ stays untouched. The updated skill loads. Nothing is lost.

You can verify the agent came up correctly:

openfang status
openfang agent list

Quick reference

PieceDefault pathWhat it holds
Templateopenfang/agents/job-search/agent.tomlConfig, settings, capabilities
Workspace~/.openfang/workspaces/job-search/Data, sessions, memory
Global skill~/.openfang/skills/job-search/Shared fallback across all agents
Local skill~/.openfang/workspaces/job-search/skills/job-search/Override, private to this agent

If OPENFANG_HOME is set, replace ~/.openfang/ with $OPENFANG_HOME/ in every path above. If workspaces_dir is set in your OpenFang config, that overrides the workspace base path for all agents.

The workspace-local skill always wins over the global one, as long as the agent is actually pointing at the workspace.

If you’re new to OpenFang skills, these two posts cover the two main patterns for adding custom logic to an agent: