---
title: when-to-batch
description: "The when-to-batch block of the CLI's guide."
---

## 1. The block, verbatim

Writing more than about three memories in one task? Call `memhtml apply` once with a JSONL op stream instead of running `memhtml write` N times. A batch stages every file, makes ONE commit, and reindexes ONCE, where N separate writes make N commits and pay N index passes over N diffs. Pass the stream as `memhtml apply --file ops.jsonl`, or pipe it: `memhtml apply -` and a bare `memhtml apply` both read stdin. One complete JSON object per line, no wrapping array, no pretty-printing. A line looks like this:

```json
{"op":"write","title":"One writer and many readers share the index","type":"semantic","body":"WAL admits a single writer at a time and any number of concurrent readers, so a CLI command and a running `memhtml serve mcp` can work against one store.","tag":"infra"}
```

`op` is `write` (the only verb in the vocabulary today), `title` and `type` are required, and each op carries the same optional fields `memhtml write` takes, in snake\_case: `path`, `strict_path`, `workspace`, `tag`, `entity`, `importance`, `confidence`, `status`, `due`, `session_id`, `prompt_id`, `turn_uuid`. The whole file is validated for shape before ANY op executes, so a malformed line 7 is exit 2 naming line 7 with nothing written. A failed apply costs you nothing but the call. You get one result per op in INPUT ORDER, each naming its own `index`, so you can match results back to the lines you sent. A batch is ATOMIC by default: the first refused op aborts the whole batch, no file is written, no commit is made, and the surviving ops report `skipped: true`. Pass `--continue-on-error` for best-effort instead, and a refused op comes back as one failed result carrying its own `code` and `error` while every op that succeeded lands in the one commit. A duplicate is never an error: an op whose exact content is already stored comes back `ok: true` with `deduped: true` and the existing path, so re-applying a file you already applied is safe and writes nothing. `commit_sha` is null exactly when nothing was committed: a batch that only deduped, or one that aborted.

## 2. Commands it names

* [`memhtml write`](/reference/commands/write/)
* [`memhtml apply`](/reference/commands/apply/)
* [`memhtml serve mcp`](/reference/commands/serve-mcp/)

## 3. Provenance

A loader generates this page from `apps/cli/src/commands.ts` while the site builds, so no file in the repository holds it: the block is authored beside `COMMANDS` and is served verbatim by `memhtml manifest`, so this page and the live answer are the same bytes. Change the registry and this page changes with it.