DESIGN.md@CLAUDE/Plan/Completed/00543-journal-file-per-entry · git:20261021.36fe7a3 · 2026-10-21 · sha256 c2132f938e8580ca

DESIGN.md@CLAUDE/Plan/Completed/00543-journal-file-per-entry git:20261021.36fe7a3A

Immutable. This exact content never changes and is served at /api/v1/blob/c2132f938e8580ca.

# Plan 00543 design: one journal file per entry

Task 1.1 output. The map was read from the source on branch `worktree-journal-per-entry` at `48aebe158`.

## Map: what knows the day-file name or path today

| Component                                                 | File                                                                                                                                                                         | What it does with day-files                                                                                              | Change needed                                                                                                                                                                                                                                             |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Writer, `--journal`                                       | `CLAUDE/Plan/mkplan.bash` = `install/templates/mkplan.bash` (identical copies)                                                                                               | Opens `JOURNAL/NNNNN-Journal-YY-MM-DD.md` from the template preamble, appends `## HH:MM · cat · REF` and the body        | Write one new entry file; never open an existing file                                                                                                                                                                                                     |
| Writer, plan scaffold                                     | same script, "scaffold the plan JOURNAL/" block                                                                                                                              | Writes today's day-file with the template preamble and the seeded `plan scaffolded` entry                                | Write the seeded entry as an entry file                                                                                                                                                                                                                   |
| Correction lookup                                         | `_journal_has_entry_at`, `JOURNAL_CORRECTION_REF_PATTERN`                                                                                                                    | `--ref HH:MM` or `YY-MM-DD/HH:MM` must name an entry in that day's day-file                                              | Search that day's legacy day-file AND that day's entry folder; CLI unchanged                                                                                                                                                                              |
| Conflict repair                                           | `--resolve-conflict`, `JOURNAL_DAYFILE_NAME_PATTERN`                                                                                                                         | Union of two sides of a day-file                                                                                         | Unchanged; legacy day-files only (entry files cannot conflict)                                                                                                                                                                                            |
| Template                                                  | `_JOURNAL_TEMPLATE_.md` (plan dir and `install/templates`)                                                                                                                   | Preamble + seeded creation entry; presence = "this project journals"                                                     | Preamble prose describes the folder layout; still the presence marker and the source of the creation entry body. UTC sentinel kept (pinned by `test_journal_sentinel_sync.py`)                                                                            |
| Plan template comment                                     | `_TEMPLATE_.md` and mkplan heredoc line ~866                                                                                                                                 | Names `JOURNAL/$padded-Journal-YY-MM-DD.md`                                                                              | Name `JOURNAL/YYYY-MM-DD/<entry>.md`                                                                                                                                                                                                                      |
| Name grammar                                              | `plan_qa/model.py` `_JOURNAL_DAYFILE_RE`, `parse_journal_dayfile_name`                                                                                                       | Day-file parse                                                                                                           | Add `parse_journal_entry_path` (folder date + file name) beside it                                                                                                                                                                                        |
| Freshness input                                           | `model._scan_journal`                                                                                                                                                        | Latest date from day-file NAMES only                                                                                     | Also count date folders holding a well-formed entry file                                                                                                                                                                                                  |
| Classifier                                                | `plan_qa/paths.py` `classify`, `is_journal_file`                                                                                                                             | Nested files are `JOURNAL_OTHER`                                                                                         | New kind `JOURNAL_ENTRY` for `JOURNAL/<YYYY-MM-DD>/*.md`; still `is_journal`                                                                                                                                                                              |
| Edit/sweep targets                                        | `checks/common.py` `journal_edit_target`, `journal_tree_targets`                                                                                                             | Only `.md` directly in `JOURNAL/`                                                                                        | Also yield files in date folders, marked as entry files                                                                                                                                                                                                   |
| `journal-dayfile-naming`                                  | checks                                                                                                                                                                       | Grammar of direct children                                                                                               | Entry files: folder is a real date, name matches `HHMMSSZ-<category>-<6 hex>.md`, category legal                                                                                                                                                          |
| `journal-dayfile-is-today`                                | checks                                                                                                                                                                       | Blocks an edit to a day-file not dated today                                                                             | Reframed: an EXISTING journal file is never edited. Legacy day-files are closed (any edit), entry files any edit once they exist; plus a COMMIT half: a staged M or D of an entry file. Check id and `today_only_mode` knob kept for config compatibility |
| `journal-append-only`                                     | checks                                                                                                                                                                       | Advisory on a history rewrite of a day-file                                                                              | Unchanged for day-files; entry files belong to the reframed check above (no double finding)                                                                                                                                                               |
| `journal-entry-ordering`                                  | checks                                                                                                                                                                       | Times increase down a day-file (EDIT + SWEEP), cross-day correction voids                                                | Entry file: exactly one heading, and its `HH:MM` equals the file name's. Corrections in entry files feed the same `(date, HH:MM)` voids, a same-day `HH:MM` ref in an entry file voids that date's label                                                  |
| `journal-entry-future-dated`                              | checks                                                                                                                                                                       | Date from day-file name, UTC if the sentinel is present                                                                  | Entry file: date from folder, always UTC                                                                                                                                                                                                                  |
| `journal-freshness`                                       | checks                                                                                                                                                                       | `latest_journal_date`                                                                                                    | Works once `_scan_journal` reads folders                                                                                                                                                                                                                  |
| `journal-folder-present`                                  | checks                                                                                                                                                                       | `has_journal`                                                                                                            | Unchanged                                                                                                                                                                                                                                                 |
| `journal-entry-with-progress`, `journal-completion-entry` | `common.has_staged_journal_entry`                                                                                                                                            | Any Added/Modified file under `<plan>/JOURNAL/`                                                                          | Already satisfied by a staged new entry file (prefix match is depth-blind); add a test that pins it                                                                                                                                                       |
| `same-commit-plan-doc`                                    | checks                                                                                                                                                                       | Keys on `PLAN.md`                                                                                                        | Unaffected                                                                                                                                                                                                                                                |
| `command_journal.py`                                      | plan_qa                                                                                                                                                                      | Reads `mkplan.bash --journal N` in the commit's own command                                                              | Unaffected                                                                                                                                                                                                                                                |
| Guard `R-JOURNAL-HAND-WRITTEN-ENTRY`                      | `handlers/pre_tool_use/plan_journal_guard.py`                                                                                                                                | `_dayfile_target` requires parent `JOURNAL/` + day-file name; unplaced Bash destinations judged by `_could_name_dayfile` | `_dayfile_target` also accepts `JOURNAL/<anything>/<file>`; `_could_name_dayfile` fails closed when ANY component after `JOURNAL/` is built at run time (`JOURNAL/$(date +%F)/x.md`). Remedy text, `get_claude_md`                                        |
| Docs                                                      | `CLAUDE/PlanJournalling.md` (+ `install/templates/PlanJournalling.md`), `core/PlanWorkflow.core.md`, `CLAUDE/DirectoryRoles.md`, `pipe_blocker` guidance ("tail a day-file") | Describe day-files                                                                                                       | Describe the folder layout and reading order; regenerate the CLAUDE.md block the supported way                                                                                                                                                            |

Not changed: `plan_links`, `archival_links_resolve`, `plan_promotion_disposition`, `markdown_organization`, `index_no_log`, `plan_doc_size` only ask "is this journal territory", which `is_journal` still answers for the new kind.

## Chosen entry file name

```
<plan>/JOURNAL/YYYY-MM-DD/HHMMSSZ-<category>-<6 hex>.md
```

- One `date -u +%Y-%m-%d %H%M%S` call gives the folder and the time, so a midnight straddle cannot split them.
- 6 hex from `/dev/urandom` (`od`): two writers in the same second with the same category collide with odds 1 in 16.7M,
  and the file is created with `noclobber`, so even that collision fails loudly instead of overwriting.
- Four-digit year in the folder (brief), so a folder never reads as a plan folder (`NNNNN-<letter>`).
- The file holds `## HH:MM · category · REF   — title`, a blank line, the body. The heading grammar is unchanged, so
  `journal_entry_headings` reads it as one entry. No preamble per file: the grammar is documented once.
- `--ref` is unchanged (`HH:MM` today, `YY-MM-DD/HH:MM` earlier). A minute may now match several entry files; a
  correction voids that minute on that date, as it already did inside a day-file.

## Reading order

Sort every entry of a plan by `(UTC date, HH:MM, source rank, name)`:

- legacy day-file entry: date from the file name, `HH:MM` from the heading, source rank 0, file order kept;
- entry file: date from the folder, `HH:MM:SS` from the name, source rank 1, then file name.

Worked example, plan 00543, both layouts on one day:

```
JOURNAL/00543-Journal-26-10-21.md       ## 16:40 action   (legacy, written before the switch)
JOURNAL/00543-Journal-26-10-21.md       ## 17:05 finding
JOURNAL/2026-10-21/170500Z-action-3fa2c1.md   ## 17:05     (same minute: legacy first)
JOURNAL/2026-10-21/171233Z-decision-0b9e44.md ## 17:12
JOURNAL/2026-10-22/090101Z-handoff-77d0aa.md  ## 09:01     (newest; the next agent's entry point)
```

The newest entry is the last file of the newest date folder, unless a legacy day-file is dated later (only possible on a
branch cut before the switch).

## Does the map make the design wrong?

No blocker, three adjustments:

1. Checks only see `JOURNAL/` direct children today, and `classify` puts nested files in `JOURNAL_OTHER`. Keeping entry
   files one folder down means an OLDER daemon sees them as `JOURNAL_OTHER` and reports nothing false (issue's point),
   but the new checks must opt them in explicitly. Only date-shaped folders count, so an existing non-date subfolder of
   `JOURNAL/` is not suddenly judged.
2. The reframed today-only rule closes legacy day-files to every edit, including removing conflict markers by hand.
   That was already blocked for any day-file not dated today; the route stays `mkplan.bash --resolve-conflict`.
3. The commit-stage half of "never edited" must not flag the `git mv` into `Completed/` (a content-identical rename),
   so it judges `M` and `D`, and an `R` only when content changed.