Day 94: Two Companies, One Chart of Accounts
The best bug reports are the ones I generate myself by using the thing. This one came out of a real Finance Controller chat on the production instance: a multi-company Odoo database where two GmbHs share the same chart of accounts, so 1000 Wareneinsatz exists twice — once per company. The agent had no way to tell which one a record belonged to, and nothing stopped it from writing a line in one company that referenced an account from the other. It didn't crash. It just quietly risked booking things into the wrong set of books, which in accounting is the failure that matters.
Company Becomes First-Class (PR #425, 10:24)
The fix runs along three axes, all aimed at the same thing: making "which company?" impossible for the agent to lose track of. odoo_read now auto-includes company_id for any model that has the field, so the agent sees the company on every record without having to know to ask for it. The _pinchy_ref labels — the stable handles the agent passes between tool calls — gain a [CompanyName] suffix when the source record carries a company, which ends the silent-collision class of bug where "use ref X" was ambiguous because X existed in three companies. And a new write-time guard, assertNoCrossCompanyRefs, refuses an odoo_create or odoo_write whose company_id disagrees with a referenced record's company — decoded locally, no Odoo round-trip, so it fails fast before the bad write leaves the agent. Legacy untagged refs and genuinely shared models like res.partner pass through untouched, and old ref tokens still decode cleanly, so nothing in flight breaks.
The guard has an honest limit, documented in the PR: it only walks the top-level fields of a write. Nested line-item command tuples — the [[0, 0, {...}]] shape Odoo uses for invoice_line_ids — aren't inspected, so a mixed-company invoice line still relies on Odoo's own server-side constraint as the final authority. Walking that structure is a bigger job for another day; the guard catches the dominant case, which is a top-level company plus sibling refs on an accounting write. The multi-company guidance also goes into the two write-heavy accounting templates, the Finance Controller and the Bookkeeper, with a data-driven test that enforces it in both directions — accounting templates that touch journal lines must carry the guidance, and ones that don't must not.
A Desk That Exists Before You Need It (PR #419, 09:18)
Day 91 gave agents pinchy_write. It had a sharp edge: a fresh agent couldn't write anything until the user had uploaded at least one file, because the only writable directory — uploads/ — was created lazily by the first upload, and writing anywhere else was rejected. So a brand-new agent asked to produce a file would ENOENT on its own workspace. This PR provisions a workbench/ subdir on every workspace spawn as the canonical place for agent-produced files, separate from the user's uploads/, and adds it to the write allow-list. The rejection message now lists the allowed paths, so when the model does try the wrong location it gets told where the right one is instead of retrying the same wrong path in a loop. Three zones, cleanly separated: the read-only root for system files, uploads/ for what the user brought, workbench/ for what the agent makes.
Quotation-Ready Sales (PR #428, 14:59)
The CRM & Sales template gained the three models a sales agent needs to build a quotation end to end: account.fiscal.position (read-write, so it can set the right VAT regime), sale.order.line (read-create-write), and account.move (read-only). The read-only on invoices is deliberate — drafting, posting, and amending invoices is regulated work that belongs to the Bookkeeper, with its draft-first, four-eyes pattern. A sales agent only ever needs to look up whether an invoice is paid; granting it write access would open a second, ungoverned invoicing path. Registering account.fiscal.position in the model categories also means the schema sync probes it and it shows up in the permissions UI for any connected Odoo — previously it was simply absent, so you couldn't grant access to it even by hand.
Two Bumps and a Review
OpenClaw moved twice today — a bump landing around midday (PR #421, 12:44) and a second one early afternoon (PR #432, 14:39) catching up to 2026.5.20 — followed by a post-merge review pass (PR #434, 15:38) cleaning up what the two dependency bumps dragged in. An image-tool fix (PR #417, 08:58) emits an explicit primary image model and demotes a misleading vision flag, closing one of the confusing Ollama-Cloud failure modes the v0.5.5 notes will mention. Dependency bumps are never just a version number; the review afterward is the part that keeps them from being a slow leak.
Day 94
The multi-company fix is the one I'll remember, because of where it came from. I didn't find it in a test or a backlog — I found it by running my own books through the agent and watching it get confused by two companies sharing an account number. That's the whole argument for using the product you build for real work: the bugs that matter most are the ones that only show up when the data is yours and the stakes are real.