# What your comments changed in my AI-agent office (Cubicle v0.7)

> Source: <https://dev.to/megventure/what-your-comments-changed-in-my-ai-agent-office-cubicle-v07-4265>
> Published: 2026-09-28 18:09:08+00:00

Yesterday I wrote about [Cubicle](https://dev.to/megventure/i-gave-my-ai-agents-an-office-and-made-it-read-only-on-purpose-5dpd), a live pixel-art office for AI agents that is read-only on purpose. A few of the comments were better code reviews than I get from most tools, so here's what they changed.

ContentClips pointed out that several Claude Code sessions fire hooks at the same moment, and every hook run rewrites the same `claude-code.json`. I was already writing to a temp file and renaming it, so a reader never sees half a file. But that only solves torn reads, not lost updates: two runs can both read the file, both change it, and the second rename silently throws away the first one's change.

I measured it before touching anything. 30 hook runs started at once, each registering a different session:

```
sessions recorded: 20 of 30
```

A third of the updates were gone. With subagents in the mix (more on that below), bursts like this are normal.

The fix is a lock file created with `O_EXCL` around the read-modify-write. The constraint that shaped it: this hook runs on every tool call, so it must never hold Claude up. It waits at most one second and then writes anyway, and a lock left behind by a crashed run is taken over after two seconds.

``` js
function withLock(fn) {
  const deadline = Date.now() + LOCK_WAIT_MS;
  let fd = null;
  while (fd === null) {
    try { fd = fs.openSync(LOCK, 'wx'); break; } catch (e) { if (e.code !== 'EEXIST') break; }
    try { if (Date.now() - fs.statSync(LOCK).mtimeMs > STALE_LOCK_MS) { fs.unlinkSync(LOCK); continue; } } catch (_) {}
    if (Date.now() > deadline) break;   // write anyway rather than block Claude
    pause(5 + Math.random() * 10);
  }
  try { return fn(); } finally {
    if (fd !== null) { try { fs.closeSync(fd); fs.unlinkSync(LOCK); } catch (_) {} }
  }
}
```

After: 40 of 40, three runs in a row, no lock file left behind. Both cases are in the test suite now.

The same comment raised a second point: in kiosk mode the office ends up on a shared TV, and task titles or tool calls can contain things you don't want on a wall.

When I looked, the problem was bigger than the bubbles. The proxy passed Paperclip's responses through unchanged. A single issue on my own instance had more than 70 fields: descriptions, workspace settings, run ids, monitor notes. The page uses about five of them. Read-only was never the same as minimal.

Two changes in v0.7:

`--redact`:` MEG-35`, `Edit`, `allow Bash` and statuses, and the "needs you" signal still works. Because it happens server-side, changing the page URL doesn't bring the details back, and neither does someone calling the API on that port directly.
The test for it feeds in an AWS key, an SSH key path, an API key in adapter config and a bearer token inside a `curl` command, then checks that none of them come out of any endpoint.

Hamid Ahmadian made a point I wish I'd written down myself: a hook that draws the office and a hook that blocks tool calls have opposite contracts. The first must never fail loudly; the second exists to fail loudly (exit 2). If one script does both, a bug in the drawing code can start blocking tool calls.

Cubicle keeps to the first contract: it always exits 0, prints nothing, and `install-hooks` adds its own separate entries next to whatever hooks you already have instead of merging into them. Reid Marlow's comment on ignoring the one-minute idle reminder was the same idea from the user's side: the office is only useful if the raised hands mean something.

A day after launch, [@omeruyanik03](https://github.com/omeruyanik03) opened the first pull request. Claude Code subagents share their parent's session id, so a session that fans out to four subagents showed up as one character whose bubble flickered between five tool calls. The PR gives every subagent its own character, keyed on the `agent_id` that Claude Code puts on hook events, with tests for the awkward cases: a subagent first seen halfway through its run, older Claude Code versions without `agent_id`, and a session ending while its subagents are still at their desks.

Because that hook runs on every tool call on my machine, I read it line by line before merging: no network, no child processes, no new dependencies, still exits 0 on every error. It shipped in v0.6.0 the same evening, and it's also why the lock in section 1 mattered so quickly.

```
# Paperclip and Claude Code in one office, details hidden for a shared screen
npx @caglarutkuguler/cubicle@latest install-hooks
npx @caglarutkuguler/cubicle@latest --source paperclip,claude-code --redact
```

Then open `http://127.0.0.1:3200/?kiosk`. Or try the [live demo](https://caglarutkuguler.github.io/cubicle/?lang=en) without installing anything.

Thanks to everyone who took the time to read the code and not just the post. The repo is [github.com/caglarutkuguler/cubicle](https://github.com/caglarutkuguler/cubicle), MIT licensed, and the open issues labelled `good first issue` are a nice place to start.
