{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "helper 67, Barbara Hookwright, reported in message 15434 \u2014 read it, then journal helper finish 67 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "helper 71, Alan Seqwell, reported in message 15436 \u2014 read it, then journal helper finish 71 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "todo 2619, Question cards built once on the kit option list, is still blocked\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2619. If it waits on a person or a decision, make it a question to them: journal todo ask 2619 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2622, Float windows skip shell polls they do not show, is still blocked\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2622. If it waits on a person or a decision, make it a question to them: journal todo ask 2622 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2624, BoardPage split into to-do and ticket boards, is still blocked - is\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2624. If it waits on a person or a decision, make it a question to them: journal todo ask 2624 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2664, One elected owner for engine supervision, is still blocked - is it\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2664. If it waits on a person or a decision, make it a question to them: journal todo ask 2664 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2665, Leftovers from the features q-z refactor, is still blocked - is it\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2665. If it waits on a person or a decision, make it a question to them: journal todo ask 2665 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "todo 2619, Question cards built once on the kit option list, is still blocked\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2619. If it waits on a person or a decision, make it a question to them: journal todo ask 2619 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2622, Float windows skip shell polls they do not show, is still blocked\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2622. If it waits on a person or a decision, make it a question to them: journal todo ask 2622 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2624, BoardPage split into to-do and ticket boards, is still blocked - is\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2624. If it waits on a person or a decision, make it a question to them: journal todo ask 2624 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2664, One elected owner for engine supervision, is still blocked - is it\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2664. If it waits on a person or a decision, make it a question to them: journal todo ask 2664 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "1 new message 15438 - answer by opening your turn with [!reply:15438]", "meta": {"from": "journal"}}
{"content": "1 new message 15439 - answer by opening your turn with [!reply:15439]", "meta": {"from": "journal"}}
{"content": "1 new message 15440 - answer by opening your turn with [!reply:15440]", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "helper 73, Charlotte Kitwright, reported in message 15442 \u2014 read it, then journal helper finish 73 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the morning pass - Alan, Barbara, Edsger, Charlotte and Ada on the leftover\u2026", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "your message 15443 names 232, 409 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15443 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "helper 68, Edsger Lockwood, reported in message 15444 \u2014 read it, then journal helper finish 68 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "your command ran 35s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your message 15456 names 502 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15456 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your message 15457 names 8440 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15457 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your message 15458 names 502 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15458 \"<the text>\"; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "your message 15460 names 8440 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15460 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your message 15461 names 8440 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15461 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2580 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2580. Stop only when nothing ready is left.; fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "your message 15463 names 502, 8441 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15463 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "your message 15464 names 404 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15464 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "your command ran 35s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "the reply tag does this in one step \u2014 [!reply:N] makes the turn itself the reply; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "todo 2622, Float windows skip shell polls they do not show, is still blocked\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2622. If it waits on a person or a decision, make it a question to them: journal todo ask 2622 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2624, BoardPage split into to-do and ticket boards, is still blocked - is\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2624. If it waits on a person or a decision, make it a question to them: journal todo ask 2624 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2664, One elected owner for engine supervision, is still blocked - is it\u2026 \u2014 it is blocked because: in a helper's hands (morning pass). If it is not any more, journal todo unblock 2664. If it waits on a person or a decision, make it a question to them: journal todo ask 2664 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed tmp_path = PosixPath('/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1308/popen-gw0/test_every_agent_launches_unde0') def test_every_agent_launches_under_the_journal_and_exits_cleanly(tmp_path): for name in DRIVERS: >           launches(tmp_path / name, CODE / \"journal.py\", name) tests/test_it_boots.py:34: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ scripts/boot_guard.py:42: in launches text = launched.communicate(timeout=WAIT)[0].decode(errors=\"replace\") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py:1221: in communicate stdout, stderr = self._communicate(input, endtime, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py:2154: in _communicate self._check_timeout(endtime, orig_timeout, stdout, stderr) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <Popen: returncode: 255 args: ['/Users/jessegall/projects/agent-journal/.ven...> endtime = 692753.987594208, orig_timeout = 45.0 stdout_seq = [b'Ask Codex to do anything\\r\\n', b'Exception ignored while calling GC callback <function _timed at 0x107c1bc10>:\\nTra...l/projects/agent-journal/src/runner/worker.py\", line 155, in ended\\n', b'    raise SystemExit(STOP)\\nSystemExit: 76\\n'] stderr_seq = None, skip_check_and_raise = False def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq, skip_check_and_raise=False): \"\"\"Convenience for checking if a timeout has expired.\"\"\" if endtime is None: return if skip_check_and_raise or _time() > endtime: >           raise TimeoutExpired( self.args, orig_timeout, output=b''.join(stdout_seq) if stdout_seq else None, stderr=b''.join(stderr_seq) if stderr_seq else None) E           subprocess.TimeoutExpired: Command '['/Users/jessegall/projects/agent-journal/.venv/bin/python', '/Users/jessegall/projects/agent-journal/src/journal.py', '--root', '/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1308/popen-gw0/test_every_agent_launches_unde0/codex/project/.journal', 'codex']' timed out after 45.0 seconds /opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py:1268: TimeoutExpired =========================== short test summary info ============================ FAILED tests/test_it_boots.py::test_every_agent_launches_under_the_journal_and_exits_cleanly 1 failed, 374 passed in 84.81s (0:01:24); check 27 failed - 1 failed, 374 passed in 84.81s (0 -01 -24) \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "answer message 15439 before you write anything \u2014 answer by opening your turn with [!reply:15439]. a reply, a reaction, or journal message processed <n>; Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "your command ran 35s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2671 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2671. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "your message 15477 names 2671 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15477 \"<the text>\"; commit f035842ee closed to-do 2671 \u2014 The rows and the work are done; take the next one.; fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "check 27 passed and f035842e One agent keeps the project's services, a running\u2026 \u2014 check 27 passed and f035842e One agent keeps the project's services, a running service keeps its port, and a timed-out run takes its whole process group is committed; then ran boot guard: installs, serves and launches claude, codex in 4.7s", "meta": {"from": "journal"}}
{"content": "your message 15478 names 2660 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15478 \"<the text>\"; your message 15479 names 2660 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15479 \"<the text>\"; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2650 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2650. Stop only when nothing ready is left.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 621890d3 Moving clean slate's list into its file never\u2026 \u2014 check 27 passed and 621890d3 Moving clean slate's list into its file never adds an entry twice is committed; then ran boot guard: installs, serves and launches claude, codex in 5.9s", "meta": {"from": "journal"}}
{"content": "work 2030 in hand \u2014 Refactor the codebase until it is clean, DRY and\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2650 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2650. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 3f5753d0 Feature.plural goes; its last caller uses\u2026 \u2014 check 27 passed and 3f5753d0 Feature.plural goes; its last caller uses engine.wording.plural is committed; then ran boot guard: installs, serves and launches claude, codex in 6.2s", "meta": {"from": "journal"}}
{"content": "your wait for the gate on the Feature.plural removal is over, because you are\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2650 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2650. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 5d486d32 A check's command guard says what it checks and\u2026 \u2014 check 27 passed and 5d486d32 A check's command guard says what it checks and returns nothing is committed; then ran boot guard: installs, serves and launches claude, codex in 5.3s", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2660 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2660. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your message 15493 names 375 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15493 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2660 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2660. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 319f5912 Methods other classes call are named as public\u2026 \u2014 check 27 passed and 319f5912 Methods other classes call are named as public, now that @action alone makes a command is committed; then ran boot guard: installs, serves and launches claude, codex in 6.1s", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 the files changed since the last check (`base.py`, `feature\u2026 \u2014 Code Commandments \u2014 the files changed since the last check (`base.py`, `features.py`) breaks a rule. Fix it now, at its SOURCE, while the code is still in front of you: \u00b7 \u2022 python-raw-decoded-return at /Users/jessegall/projects/agent-journal/src/controllers/features.py:39 \u00b7 LOAD the skill `commandments-python-value-objects` before fixing \u2014 load it even if you believe you already have. \u00b7 Run `commandments info <sin>` if a rule is not one you recognise. This check reads a file at a time, so it is not the whole picture \u2014 `judge` still is.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2030 stands still while todo 2660 is ready \u2014 if work 2030 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2660. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "the await tag does this in one step \u2014 [!await] makes the rest of the turn what you wait for; [!await on=(\"<id>\", \"helper:<n>\")] waits on those; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "check 27 passed and 03319a02 journal feature configure changes a feature's\u2026 \u2014 check 27 passed and 03319a02 journal feature configure changes a feature's setting is committed; then ran boot guard: installs, serves and launches claude, codex in 5.4s", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you have an OPEN worklist with 76 sins\u2026 \u2014 Code Commandments \u2014 before you wrap up: you have an OPEN worklist with 76 sins still in `.journal/plugin-data/code-commandments/sessions/8951e/sins/sins.md`. Finish it before you stop: work straight down \u2014 fix each at its SOURCE, delete its line \u2014 and do NOT re-run judge, re-scan, or re-verify between fixes. Only when the file is EMPTY, run `judge` again (wave by wave; a clean run deletes it). If you are intentionally pausing here, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check the gate on journal feature configure (to-do 2660) now - you have waited\u2026 \u2014 look at the thing itself: the background shell's output, the process, the run's status. If it is still going, say journal work await \"<what you wait for>\" again and wait. If it finished or failed, journal work log what came of it and take the next step. Never wait for something you can do without.", "meta": {"from": "journal"}}
{"content": "your message 15506 names 627 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15506 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "your wait for the gate on journal feature configure (to-do 2660) is over\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; fact 29 \u2014 Codex's transcript records the end of every exec session, polled or\u2026 \u2014 Seen 2026-10-03 in the Passkey rollout (2026-10-02T22-40-17), Codex 0.160: 118 sessions opened (an exec output carrying \"session_id\":N), 118 item_completed events of type CommandExecution with process_id N, status completed or failed, exit_code and completed_at_ms, including the 7 never polled with write_stdin. A run's end comes from the transcript; no process check is needed.", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.", "meta": {"from": "journal"}}
{"content": "work 2030 in hand \u2014 Refactor the codebase until it is clean, DRY and\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 17 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 17 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and d72c4e89 Code Commandments findings worked through\u2026 \u2014 check 27 passed and d72c4e89 Code Commandments findings worked through: timing takes the record, flatter decisions, one transcript reader, one plugin settings choice, honest peer notes, a typed step in hand is committed; then ran boot guard: installs, serves and launches claude, codex in 4.9s", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "the end tag does this in one step \u2014 [!end:N] makes the turn itself what landed; it runs only when it opens the last text of your turn; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "todo 2672 next", "meta": {"from": "journal"}}
{"content": "work 2031 in hand \u2014 Feature import cycles among boards, tickets, plans\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2031 stands still while todo 2673 is ready \u2014 if work 2031 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2673. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 5 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 5 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and e7f7319d Two feature import cycles cut - plans own the\u2026 \u2014 check 27 passed and e7f7319d Two feature import cycles cut: plans own the must-have section, and boards tell sequences which board a message is about is committed; then ran boot guard: installs, serves and launches claude, codex in 10.5s", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 87 times.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "nothing is ready - every open row waits \u2014 to-do 2672, Feature import cycles among boards, tickets, plans, templates and sequences; to-do 2673, The phone feed and the demo recorder respect the layers. For each that waits on a person or a decision, put it to them now with journal todo ask <n> \"<who decides what>\"; unblock any that can go on and work it. Stop only when each one waits on a question.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (check create) is slower than its budget \u2014 172ms last (171ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "commandments-frontend-vue-components, commandments-python-value-objects\u2026 \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn; fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Nothing else is open on my\u2026 \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 114ms last (58ms of it working), against a budget of 50ms. Seen 21 times.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 564c73dd A restarted service asks its when-check again is\u2026 \u2014 check 27 passed and 564c73dd A restarted service asks its when-check again is committed; then ran boot guard: installs, serves and launches claude, codex in 9.0s", "meta": {"from": "journal"}}
{"content": "commit 564c73ddb closed to-do 2675 and ended work 2032 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "1 new message 15557 - answer by opening your turn with [!reply:15557]", "meta": {"from": "journal"}}
{"content": "command message reply is slower than its budget \u2014 155ms last (54ms of it working), against a budget of 50ms. Seen 10 times.; hook POST /api/hook/claude is slower than its budget \u2014 581ms last (73ms of it working), against a budget of 50ms. Seen 87 times.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your command ran 35s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "the log tag does this in one step \u2014 [!log:N] makes the turn itself the log entry; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2033 stands still while todo 2673 is ready \u2014 if work 2033 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2673. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you have an OPEN worklist with 9 sins\u2026 \u2014 Code Commandments \u2014 before you wrap up: you have an OPEN worklist with 9 sins still in `.journal/plugin-data/code-commandments/sessions/8951e/sins/sins.md`. Finish it before you stop: work straight down \u2014 fix each at its SOURCE, delete its line \u2014 and do NOT re-run judge, re-scan, or re-verify between fixes. Only when the file is EMPTY, run `judge` again (wave by wave; a clean run deletes it). If you are intentionally pausing here, just say so and carry on.; rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 9 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 9 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; the full suite and the cycle check on the boards and sequences decoupling came\u2026", "meta": {"from": "journal"}}
{"content": "check 27 passed and 6dff2443 Features declare the sequences they ship, and\u2026 \u2014 check 27 passed and 6dff2443 Features declare the sequences they ship, and boards keep their cards through a store tickets provide is committed; then ran boot guard: installs, serves and launches claude, codex in 8.2s", "meta": {"from": "journal"}}
{"content": "commit 6dff24433 closed to-do 2672 and ended work 2033 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 67ms last (67ms of it working, 5ms collecting garbage), against a budget of 50ms. Seen 24 times.", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "message 15120 file Screenshot 2026-10-04 at 22.50.13.png needs tags \u2014 inspect the attachment, then journal message tag 15120 \"Screenshot 2026-10-04 at 22.50.13.png\" \"<a few words describing what it shows>\"", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "work 2034 in hand \u2014 The phone feed and the demo recorder respect the layers \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "your message 15585 names 627 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15585 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you have an OPEN worklist with 8 sins\u2026 \u2014 Code Commandments \u2014 before you wrap up: you have an OPEN worklist with 8 sins still in `.journal/plugin-data/code-commandments/sessions/8951e/sins/sins.md`. Finish it before you stop: work straight down \u2014 fix each at its SOURCE, delete its line \u2014 and do NOT re-run judge, re-scan, or re-verify between fixes. Only when the file is EMPTY, run `judge` again (wave by wave; a clean run deletes it). If you are intentionally pausing here, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your message 15591 names 627 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15591 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "commit a8ae266b8 closed to-do 2673 and ended work 2034 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and a8ae266b Features add their own summary parts below them\u2026 \u2014 check 27 passed and a8ae266b Features add their own summary parts below them, and the demo builder runs from the command layer is committed; then ran boot guard: installs, serves and launches claude, codex in 8.0s", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "the end tag does this in one step \u2014 [!end:N] makes the turn itself what landed; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "your message 15593 names 632, 627 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15593 \"<the text>\"; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 804ms last (81ms of it working), against a budget of 50ms. Seen 8 times.; request GET /api/main/agent is slower than its budget \u2014 892ms last (62ms of it working), against a budget of 50ms. Seen 73 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 2721ms last (854ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 25 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "think up what the user might ask for on board 12, Orchestrator \u2014 read its goal and cards (journal board show 12), then write 3 to 5 short things the user might ask for next, each one chip of at most 60 characters, in the user's words: journal board ideas 12 \"<idea>\" \"<idea>\" \"<idea>\". They replace the board's ideas under New work.; think up what the user might ask for on board 13, New work trials \u2014 read its goal and cards (journal board show 13), then write 3 to 5 short things the user might ask for next, each one chip of at most 60 characters, in the user's words: journal board ideas 13 \"<idea>\" \"<idea>\" \"<idea>\". They replace the board's ideas under New work.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 99 times.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "1 new message 15605 - answer by opening your turn with [!reply:15605]", "meta": {"from": "journal"}}
{"content": "message 15605 updated", "meta": {"from": "journal"}}
{"content": "the Code Commandments check of the viewer fix, and the commit gate came back\u2026", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "commit 6381db864 closed to-do 2677 and ended work 2038 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 6381db86 The viewer's overlap check tells journals apart\u2026 \u2014 check 27 passed and 6381db86 The viewer's overlap check tells journals apart is committed; then ran boot guard: installs, serves and launches claude, codex in 7.2s", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 100 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 106ms last (53ms of it working), against a budget of 50ms. Seen 88 times.", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 227ms last (66ms of it working), against a budget of 50ms. Seen 9 times.; request GET /api/main/dashboard is slower than its budget \u2014 2660ms last (792ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 27 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/family is slower than its budget \u2014 237ms last (100ms of it working, 10ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 205ms last (60ms of it working), against a budget of 50ms. Seen 75 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15618 - answer by opening your turn with [!reply:15618]", "meta": {"from": "journal"}}
{"content": "1 new message 15620 - answer by opening your turn with [!reply:15620]", "meta": {"from": "journal"}}
{"content": "1 new message 15622 - answer by opening your turn with [!reply:15622]", "meta": {"from": "journal"}}
{"content": "phone 7 completed", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 103 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15623 - answer by opening your turn with [!reply:15623]", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "1 new message 15626 - answer by opening your turn with [!reply:15626]", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "1 new message 15628 - answer by opening your turn with [!reply:15628]", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "1 new message 15631 - answer by opening your turn with [!reply:15631]", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 106 times.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 147ms last (54ms of it working), against a budget of 50ms. Seen 78 times.", "meta": {"from": "journal"}}
{"content": "rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "work 2039 in hand \u2014 Tunler setup asks for the server address and links to the\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "1 new message 15646 - answer by opening your turn with [!reply:15646]", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you commit \u2014 you've changed 9 judged files since the\u2026 \u2014 Code Commandments \u2014 before you commit: you've changed 9 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 you're editing `test_every_action.py`, a test/stub/fixture\u2026 \u2014 Code Commandments \u2014 you're editing `test_every_action.py`, a test/stub/fixture that `judge` never scans, so nothing here will flag a symptom-fix. If you're changing it to make a failing check pass, first ask WHERE the failure is born: a failing test usually means the PRODUCTION code is missing behaviour \u2014 a method, a type, a total value on the real type. Fix it THERE (trace to the source; re-open the relevant skill), and change this file only if the TEST itself is wrong. If this is just legitimate test coverage, carry on.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "the log tag does this in one step \u2014 [!log:N] makes the turn itself the log entry; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself; commit 2dc8f42d5 closed to-do 2678 and ended work 2039 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 2dc8f42d The viewer can connect a phone again, and tunler\u2026 \u2014 check 27 passed and 2dc8f42d The viewer can connect a phone again, and tunler installs from the server you name is committed; then ran boot guard: installs, serves and launches claude, codex in 11.7s", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; work deferred in words, not parked \u2014 \"once the branch is\" is the title of a to-do: journal todo create \"<title>\" --brief, then say so", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 206ms last (54ms of it working), against a budget of 50ms. Seen 79 times.; request GET /api/main/dashboard is slower than its budget \u2014 560ms last (66ms of it working), against a budget of 50ms. Seen 32 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15665 - answer by opening your turn with [!reply:15665]", "meta": {"from": "journal"}}
{"content": "1 new message 15666 - answer by opening your turn with [!reply:15666]", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 107 times.", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "your message 15674 names 368 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15674 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 194ms last (59ms of it working), against a budget of 50ms. Seen 81 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15677 - answer by opening your turn with [!reply:15677]", "meta": {"from": "journal"}}
{"content": "1 new message 15678 - answer by opening your turn with [!reply:15678]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "work 2043 in hand \u2014 Project paths with a space break every hook and the MCP\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 109 times.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 428ms last (194ms of it working, 6ms collecting garbage), against a budget of 50ms. Seen 33 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 102ms last (50ms of it working), against a budget of 50ms. Seen 83 times.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Still waiting\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead; auto mode is on and work 2043 stands still while todo 2676 is ready \u2014 if work 2043 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2676. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "the boot and wiring tests for the paths-with-spaces, interpreter and\u2026", "meta": {"from": "journal"}}
{"content": "1 new message 15690 - answer by opening your turn with [!reply:15690]", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.; command message reply is slower than its budget \u2014 254ms last (60ms of it working), against a budget of 50ms. Seen 11 times.; request POST /api/run (message reply) is slower than its budget \u2014 269ms last (65ms of it working), against a budget of 50ms. Seen 7 times.", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.", "meta": {"from": "journal"}}
{"content": "todo 2681, Install the refactor here and hand the tunnel back to its service\u2026 \u2014 it is blocked because: waits on the user's word to install the refactor before its pull request is approved. If it is not any more, journal todo unblock 2681. If it waits on a person or a decision, make it a question to them: journal todo ask 2681 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "question 195 completed", "meta": {"from": "journal"}}
{"content": "1 new message 15702 - answer by opening your turn with [!reply:15702]", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 110 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 380ms last (79ms of it working), against a budget of 50ms. Seen 7 times.; 1 new message 15704 - answer by opening your turn with [!reply:15704]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; command message reply is slower than its budget \u2014 211ms last (50ms of it working), against a budget of 50ms. Seen 12 times.; request POST /api/run (message reply) is slower than its budget \u2014 232ms last (60ms of it working), against a budget of 50ms. Seen 8 times.", "meta": {"from": "journal"}}
{"content": "the reply tag does this in one step \u2014 [!reply:N] makes the turn itself the reply; it runs only when it opens the last text of your turn; 1 new message 15705 - answer by opening your turn with [!reply:15705]", "meta": {"from": "journal"}}
{"content": "1 new message 15706 - answer by opening your turn with [!reply:15706]", "meta": {"from": "journal"}}
{"content": "1 new message 15707 - answer by opening your turn with [!reply:15707]", "meta": {"from": "journal"}}
{"content": "1 new message 15708 - answer by opening your turn with [!reply:15708]", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "1 new message 15709 - answer by opening your turn with [!reply:15709]", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 the files changed since the last check (`FeaturePanel.vue`,\u2026 \u2014 Code Commandments \u2014 the files changed since the last check (`FeaturePanel.vue`, `claude.py`, `base.py`, `formatters.py`, `test.py`) breaks a rule. Fix it now, at its SOURCE, while the code is still in front of you: \u00b7 \u2022 switch-case at /Users/jessegall/projects/agent-journal/src/web/src/pages/FeaturePanel.vue:75 \u00b7 LOAD the skill `commandments-frontend-vue-control-flow` before fixing \u2014 load it even if you believe you already have. \u00b7 Run `commandments info <sin>` if a rule is not one you recognise. This check reads a file at a time, so it is not the whole picture \u2014 `judge` still is.", "meta": {"from": "journal"}}
{"content": "work 2045 in hand \u2014 Settings choose which releases install automatically \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 1015ms last (587ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 34 times.", "meta": {"from": "journal"}}
{"content": "message 15715 file Screenshot 2026-10-05 at 18.45.58.png needs tags \u2014 inspect the attachment, then journal message tag 15715 \"Screenshot 2026-10-05 at 18.45.58.png\" \"<a few words describing what it shows>\"; 1 new message 15715 - answer by opening your turn with [!reply:15715]; message 15715 updated", "meta": {"from": "journal"}}
{"content": "law L4 \u2014 Follow-up work goes back to the subagent that did the first part\u2026 \u2014 A subagent that drew a design, wrote the code or ran the research keeps what it learned. When the user asks for a change to its work, continue that subagent with a message rather than dispatching a new one that has to rediscover everything; start fresh only when the earlier one is gone or the new work is unrelated.; law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; rule 53 \u2014 Every new design gets one critique round, and the designer decides \u2014 Message 12800: whenever Dieter (the designer) builds something new, critics use it and critique, and he revises. Message 13475: a single round of critique is enough, not five. He is the designer, so where critics and he disagree his choice stands; the user's own rulings still come first.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 276ms last (53ms of it working), against a budget of 50ms. Seen 85 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15718 - answer by opening your turn with [!reply:15718]", "meta": {"from": "journal"}}
{"content": "1 new message 15719 - answer by opening your turn with [!reply:15719]", "meta": {"from": "journal"}}
{"content": "1 new message 15721 - answer by opening your turn with [!reply:15721]", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Reading it\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "message 15723 file Screenshot 2026-10-05 at 18.55.41.png needs tags \u2014 inspect the attachment, then journal message tag 15723 \"Screenshot 2026-10-05 at 18.55.41.png\" \"<a few words describing what it shows>\"; 1 new message 15723 - answer by opening your turn with [!reply:15723]; message 15723 updated", "meta": {"from": "journal"}}
{"content": "1 new message 15725 - answer by opening your turn with [!reply:15725]", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 113 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/doc is slower than its budget \u2014 90ms last (72ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "1 new message 15726 - answer by opening your turn with [!reply:15726]", "meta": {"from": "journal"}}
{"content": "settings.py names 2 files in the project \u2014 write the path so the chat can link it: src/features/settings.py, src/surfaces/settings.py", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; 1 new message 15730 - answer by opening your turn with [!reply:15730]", "meta": {"from": "journal"}}
{"content": "1 new message 15732 - answer by opening your turn with [!reply:15732]", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.; Is rule 58 a ruling for the whole project? \u2014 rule 58, \"The user tests a design's clickable prototype and approves it before the build\", binds every environment of the project. Keep it only if it is a ruling for all of them, worded as one (\"Always ...\", \"Never ...\"). If it is about this environment or its current work, strike it with journal rule strike 58 --how \"<why>\" and file it here as a fact or a reminder instead.", "meta": {"from": "journal"}}
{"content": "rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "message 15735 file Screenshot 2026-10-05 at 19.02.13.png needs tags \u2014 inspect the attachment, then journal message tag 15735 \"Screenshot 2026-10-05 at 19.02.13.png\" \"<a few words describing what it shows>\"; 1 new message 15735 - answer by opening your turn with [!reply:15735]; message 15735 updated", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "1 new message 15739 - answer by opening your turn with [!reply:15739]", "meta": {"from": "journal"}}
{"content": "1 new message 15740 - answer by opening your turn with [!reply:15740]", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "1 new message 15743 - answer by opening your turn with [!reply:15743]", "meta": {"from": "journal"}}
{"content": "1 new message 15744 - answer by opening your turn with [!reply:15744]", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "1 new message 15745 - answer by opening your turn with [!reply:15745]", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.; rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the auto-update setting the user asked for goes first. journal work resume 2044 picks it up again.", "meta": {"from": "journal"}}
{"content": "journal-sequences changed since you loaded them \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "the hook hit an error \u2014 journal: the hook hit an error and kept going; the last of it is below and the whole of it is in .journal/runtime/engine.log. Fix it, then say so. the hook got no answer from the server 3 times (codes 000)", "meta": {"from": "journal"}}
{"content": "you ran the same check 3 times in a row - ls -la .journal/journal.pyz | awk\u2026 \u2014 if you are waiting for something to change, say journal work await \"<what you wait for>\" and end your turn: you are asked to look again every five minutes, and a background command tells you itself when it ends. Keep checking only if each look moves the work on.; hook POST /api/hook/claude is slower than its budget \u2014 627ms last (156ms of it working, 3ms waiting on locks), against a budget of 50ms. Seen 93 times.; request GET /api/summary is slower than its budget \u2014 3121ms last (913ms of it working, 10ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "the phone address answering through the journal's own tunnel service came back\u2026", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the auto-update setting the user asked for goes first. journal work resume 2044 picks it up again.", "meta": {"from": "journal"}}
{"content": "todo 2697 next", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"the message is answered\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead; 1 new message 15756 - answer by opening your turn with [!reply:15756]", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 610ms last (370ms of it working), against a budget of 50ms. Seen 135 times.", "meta": {"from": "journal"}}
{"content": "1 new phone 8", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its server was restarted", "meta": {"from": "journal"}}
{"content": "phones 8, 9, 10 completed; 3 new phones 9, 10, 11", "meta": {"from": "journal"}}
{"content": "phone 11 completed; 1 new phone 12", "meta": {"from": "journal"}}
{"content": "1 new message 15758 - answer by opening your turn with [!reply:15758]", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 967ms last (73ms of it working), against a budget of 50ms. Seen 36 times.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; hook POST /api/hook/claude is slower than its budget \u2014 222ms last (128ms of it working, 5ms collecting garbage, 1ms waiting on locks), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its server was restarted", "meta": {"from": "journal"}}
{"content": "your message 15761 names 200, 8440, 8441 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15761 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the auto-update setting the user asked for goes first. journal work resume 2044 picks it up again.; fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 114 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 559ms last (62ms of it working), against a budget of 50ms. Seen 93 times.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your wait for the commit check for the message-linking fix, and Ada's coverage\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Lay out the parts \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:73. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 73 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:73.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 2 of 6 - Write each part \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:73. Write the parts one at a time and in order with journal report section 73 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:73.", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; law L2 \u2014 Every subagent is bound to a concrete job; never dispatch a generic\u2026 \u2014 Use the most specific available agent type whose declared purpose matches the assignment. On providers without agent types, give the dispatch a concrete task name and bounded prompt. If no suitable specialization exists, keep the work in the main agent instead of manufacturing an unscoped helper.; law L4 \u2014 Follow-up work goes back to the subagent that did the first part\u2026 \u2014 A subagent that drew a design, wrote the code or ran the research keeps what it learned. When the user asks for a change to its work, continue that subagent with a message rather than dispatching a new one that has to rediscover everything; start fresh only when the earlier one is gone or the new work is unrelated.", "meta": {"from": "journal"}}
{"content": "check 27 passed and e0e281ad A filed row is linked to a message only when that\u2026 \u2014 check 27 passed and e0e281ad A filed row is linked to a message only when that message alone is in hand is committed; then ran boot guard: installs, serves and launches claude, codex in 8.8s", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, is still at step 2 of 6 - carry on with it \u2014 finishing it comes before anything else; do the step now, Write each part: Write the parts one at a time and in order with journal report section 73 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:73.", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the auto-update setting the user asked for goes first. journal work resume 2044 picks it up again.; fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.; hook POST /api/hook/claude is slower than its budget \u2014 190ms last (135ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 50 times.; commit e0e281ad5 closed to-do 2697 and ended work 2047 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 3 of 6 - Put it in a collection \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:73. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:73. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:73.", "meta": {"from": "journal"}}
{"content": "1 new message 15769 - answer by opening your turn with [!reply:15769]", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 6 of 6 - Answer with it \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:73. Say in one or two plain lines what it concludes, then its reference on a line of its own, like doc 41 or report 98, never in backticks. Finish with journal sequence next 10 --about report:73.", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; fact 29 \u2014 Codex's transcript records the end of every exec session, polled or\u2026 \u2014 Seen 2026-10-03 in the Passkey rollout (2026-10-02T22-40-17), Codex 0.160: 118 sessions opened (an exec output carrying \"session_id\":N), 118 item_completed events of type CommandExecution with process_id N, status completed or failed, exit_code and completed_at_ms, including the 7 never polled with write_stdin. A run's end comes from the transcript; no process check is needed.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 108ms last (62ms of it working), against a budget of 50ms. Seen 17 times.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 57ms last (52ms of it working), against a budget of 50ms. Seen 93 times.", "meta": {"from": "journal"}}
{"content": "work 2048 in hand \u2014 A generated test runs every viewer call through the HTTP\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.; rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.; request GET /api/main/agent is slower than its budget \u2014 247ms last (53ms of it working), against a budget of 50ms. Seen 96 times.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 115 times.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the auto-update setting the user asked for goes first. journal work resume 2044 picks it up again.; commit fcfd48e74 closed to-do 2680, to-do 2766 and ended work 2048 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and fcfd48e7 Every call the viewer makes is walked through the\u2026 \u2014 check 27 passed and fcfd48e7 Every call the viewer makes is walked through the HTTP API as the user is committed; then failed boot guard: the journal does not boot, push refused journal claude crashed: journal: port 51492 is still taken after 30s; the viewer moves, and a tab left open on it will not reach this journal Traceback (most recent call last): File \"/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal.py\", line 9, in <module> runpy.run_path(sys.argv[0], run_name=\"__main__\") ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 311, in run_path File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/__main__.py\", line 11, in <module> runpy.run_module(\"journal\", run_name=\"__main__\", alter_sys=True) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 231, in run_module File \"<frozen runpy>\", line 98, in _run_module_code File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/journal.py\", line 9, in <module> sys.exit(run(sys.argv[1:])) ~~~^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/commands/cli.py\", line 129, in run print(query({**ctx, **args}), file=out) ~~~~~^^^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/commands/parser.py\", line 120, in <lambda> add_query(cmds, name, f\"start {name} supervised, on this environment; everything after the word is forwarded to {name}\", lambda ctx, name=name: supervise(ctx, name)) ~~~~~~~~~^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/commands/queries.py\", line 154, in supervise return launch(ctx[\"record\"], agent, ctx[\"args\"]) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/commands/launch.py\", line 193, in launch url = start(record.root, project) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/engine/viewer.py\", line 217, in start return launch(root, project)[0] ~~~~~~^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/engine/viewer.py\", line 228, in launch port = available(root, last(root).port) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-_vby653m/Project builds/.journal/journal-2.251.0-a39ef88a96.pyz/engine/viewer.py\", line 183, in available raise OSError(\"no viewer port available from 8420 through 8439\") OSError: no viewer port available from 8420 through 8439 error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "your message 15786 names 8420, 8439 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15786 \"<the text>\"; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; hook POST /api/hook/claude is slower than its budget \u2014 59ms last (53ms of it working), against a budget of 50ms. Seen 132 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.; request POST /api/run (todo create) is slower than its budget \u2014 310ms last (68ms of it working, 4ms waiting on locks), against a budget of 50ms. Seen 1 time.; request POST /api/run (todo start) is slower than its budget \u2014 219ms last (80ms of it working, 47ms waiting on locks), against a budget of 50ms. Seen 18 times.", "meta": {"from": "journal"}}
{"content": "your message 15793 names 8441 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15793 \"<the text>\"; auto mode is on and work 2049 stands still while todo 2698 is ready \u2014 if work 2049 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2698. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "work 2049 in hand \u2014 A session always runs its own journal's build \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2049 stands still while todo 2676 is ready \u2014 if work 2049 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2676. Stop only when nothing ready is left.; the commit check for the session-build fix and the code check of its files\u2026", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the auto-update setting the user asked for goes first. journal work resume 2044 picks it up again.; commit 180c9b688 closed to-do 2770 and ended work 2049 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 180c9b68 A session always runs its own journal's build is\u2026 \u2014 check 27 passed and 180c9b68 A session always runs its own journal's build is committed; then ran boot guard: installs, serves and launches claude, codex in 8.6s", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 71ms last (59ms of it working), against a budget of 50ms. Seen 37 times.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "journal-messages changed since you loaded them \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 62ms last (56ms of it working), against a budget of 50ms. Seen 168 times.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.; rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "work 2044, journal message reply runs over its 50ms budget, is still parked\u2026 \u2014 it was parked because: the dashboard budget (to-do 2676) is profiled first. journal work resume 2044 picks it up again.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 76ms last (61ms of it working), against a budget of 50ms. Seen 201 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2051 stands still while todo 2686 is ready \u2014 if work 2051 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2686. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "commit 6029a07f3 closed to-do 2679 and ended work 2051 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 6029a07f The hook no longer asks the server over HTTP for\u2026 \u2014 check 27 passed and 6029a07f The hook no longer asks the server over HTTP for the server's own address is committed; then ran boot guard: installs, serves and launches claude, codex in 8.2s", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 246ms last (70ms of it working, 29ms waiting on locks), against a budget of 50ms. Seen 19 times.", "meta": {"from": "journal"}}
{"content": "work 2052 in hand \u2014 An upgrade checks one hook runs and says so loudly when it\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 127ms last (57ms of it working), against a budget of 50ms. Seen 99 times.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.; hook POST /api/hook/claude is slower than its budget \u2014 102ms last (60ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 242 times.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 5 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 5 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you have an OPEN worklist with 2 sins\u2026 \u2014 Code Commandments \u2014 before you wrap up: you have an OPEN worklist with 2 sins still in `.journal/plugin-data/code-commandments/sessions/8951e/sins/sins.md`. Finish it before you stop: work straight down \u2014 fix each at its SOURCE, delete its line \u2014 and do NOT re-run judge, re-scan, or re-verify between fixes. Only when the file is EMPTY, run `judge` again (wave by wave; a clean run deletes it). If you are intentionally pausing here, just say so and carry on.; your message 15823 names 627 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15823 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your wait for the commit check for the install hook check and the down-server\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.; fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.; law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 008fca6d An install checks the hooks it wired, and a hook\u2026 \u2014 check 27 passed and 008fca6d An install checks the hooks it wired, and a hook that finds the server down says so is committed; then ran boot guard: installs, serves and launches claude, codex in 6.2s; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; commit 008fca6da closed to-do 2686, to-do 2727 and ended work 2052 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "the commit check for the install hook check and the down-server log, and\u2026", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 116 times.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 88ms last (55ms of it working), against a budget of 50ms. Seen 275 times.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2054 stands still while todo 2687 is ready \u2014 if work 2054 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2687. Stop only when nothing ready is left.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 5 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 5 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "commit d94734b2c closed to-do 2747, to-do 2749, to-do 2750, to-do 2751 and\u2026 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and d94734b2 The file feed, skill and mode routes are tested\u2026 \u2014 check 27 passed and d94734b2 The file feed, skill and mode routes are tested as the viewer calls them, and clean slate drops dead code is committed; then ran boot guard: installs, serves and launches claude, codex in 7.4s", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 70ms last (62ms of it working), against a budget of 50ms. Seen 299 times.", "meta": {"from": "journal"}}
{"content": "work 2055 in hand \u2014 The viewer shows diagnostics.log \u2014 if this is not what you are doing, end it or park it and start the work you are in; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 11 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 11 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 61c1d45a Settings shows the developer error log is\u2026 \u2014 check 27 passed and 61c1d45a Settings shows the developer error log is committed; then ran boot guard: installs, serves and launches claude, codex in 7.8s", "meta": {"from": "journal"}}
{"content": "commit 61c1d45a3 closed to-do 2687 and ended work 2055 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 163ms last (89ms of it working), against a budget of 50ms. Seen 346 times.", "meta": {"from": "journal"}}
{"content": "rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "todo 2698, transportklok runs about 90 tunler clients from two builds at once\u2026 \u2014 it is blocked because: the fix is to-do 2770; the transportklok session already running on agent-journal's build keeps starting duplicates until the user ends and restarts it. If it is not any more, journal todo unblock 2698. If it waits on a person or a decision, make it a question to them: journal todo ask 2698 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; law L2 \u2014 Every subagent is bound to a concrete job; never dispatch a generic\u2026 \u2014 Use the most specific available agent type whose declared purpose matches the assignment. On providers without agent types, give the dispatch a concrete task name and bounded prompt. If no suitable specialization exists, keep the work in the main agent instead of manufacturing an unscoped helper.; law L4 \u2014 Follow-up work goes back to the subagent that did the first part\u2026 \u2014 A subagent that drew a design, wrote the code or ran the research keeps what it learned. When the user asks for a change to its work, continue that subagent with a message rather than dispatching a new one that has to rediscover everything; start fresh only when the earlier one is gone or the new work is unrelated.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "work 2057 in hand \u2014 Test a failed auto-update install and its backoff \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Still waiting\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "check 27 passed and 4c3b658f Tests for a failed update and its backoff, the\u2026 \u2014 check 27 passed and 4c3b658f Tests for a failed update and its backoff, the update routes, silent agents, lapsed rows on a tick, and quiet drafting boards is committed; then ran boot guard: installs, serves and launches claude, codex in 8.1s", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat; commit 4c3b658fa closed to-do 2740, to-do 2741, to-do 2742, to-do 2745, to-do\u2026 \u2014 The rows and the work are done; take the next one.; hook POST /api/hook/claude is slower than its budget \u2014 496ms last (413ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 382 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 76ms last (53ms of it working), against a budget of 50ms. Seen 20 times.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "commit 89a8773ac closed to-do 2744 and ended work 2058 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 89a8773a Browser control gets its first test is committed\u2026 \u2014 check 27 passed and 89a8773a Browser control gets its first test is committed; then failed boot guard: the journal does not boot, push refused journal claude crashed: journal: port 50049 is still taken after 30s; the viewer moves, and a tab left open on it will not reach this journal Traceback (most recent call last): File \"/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal.py\", line 9, in <module> runpy.run_path(sys.argv[0], run_name=\"__main__\") ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 311, in run_path File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/__main__.py\", line 11, in <module> runpy.run_module(\"journal\", run_name=\"__main__\", alter_sys=True) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 231, in run_module File \"<frozen runpy>\", line 98, in _run_module_code File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/journal.py\", line 9, in <module> sys.exit(run(sys.argv[1:])) ~~~^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/commands/cli.py\", line 129, in run print(query({**ctx, **args}), file=out) ~~~~~^^^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/commands/parser.py\", line 120, in <lambda> add_query(cmds, name, f\"start {name} supervised, on this environment; everything after the word is forwarded to {name}\", lambda ctx, name=name: supervise(ctx, name)) ~~~~~~~~~^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/commands/queries.py\", line 154, in supervise return launch(ctx[\"record\"], agent, ctx[\"args\"]) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/commands/launch.py\", line 193, in launch url = start(record.root, project) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/engine/viewer.py\", line 220, in start return launch(root, project)[0] ~~~~~~^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/engine/viewer.py\", line 231, in launch port = available(root, last(root).port) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-mb2ec2yl/Project builds/.journal/journal-2.251.0-736f01c984.pyz/engine/viewer.py\", line 186, in available raise OSError(\"no viewer port available from 8420 through 8439\") OSError: no viewer port available from 8420 through 8439 error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 532ms last (310ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 404 times.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 5 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 5 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your message 15876 names 627 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15876 \"<the text>\"; your wait for the commit check for the worker guard, then installing it here\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "the await tag does this in one step \u2014 [!await] makes the rest of the turn what you wait for; [!await on=(\"<id>\", \"helper:<n>\")] waits on those; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "check 27 passed and 378117e2 A worker on another journal's build leaves that\u2026 \u2014 check 27 passed and 378117e2 A worker on another journal's build leaves that journal's servers alone, and the plugin preview and terminal poll are tested is committed; then ran boot guard: installs, serves and launches claude, codex in 7.9s", "meta": {"from": "journal"}}
{"content": "commit 378117e27 closed to-do 2698, to-do 2752, to-do 2756 and ended work 2059 \u2014 The rows and the work are done; take the next one.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "work 2060 in hand \u2014 Test the guards on waking an agent for a message \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 111ms last (69ms of it working), against a budget of 50ms. Seen 433 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed bringing up nodes... bringing up nodes... ........................................................................ [ 18%] ......................................................F................. [ 37%] ........................................................................ [ 56%] ........................................................................ [ 75%] ........................................................................ [ 94%] .....................                                                    [100%] =================================== FAILURES =================================== _ test_a_transcript_rewritten_in_place_is_read_again_not_served_from_the_cache _ [gw2] darwin -- Python 3.14.7 /Users/jessegall/projects/agent-journal/.venv/bin/python tmp_path = PosixPath('/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1382/popen-gw2/test_a_transcript_rewritten_in0') def test_a_transcript_rewritten_in_place_is_read_again_not_served_from_the_cache(tmp_path): import json from providers import PROVIDERS from providers.transcript_cache import SHAPED_BY, code_mark claude = PROVIDERS[\"claude\"]() entry = lambda text: json.dumps({\"type\": \"user\", \"timestamp\": \"2026-10-05T10:00:00Z\", \"message\": {\"content\": text}}) + \"\\n\" transcript = tmp_path / \"s-1.jsonl\" >       transcript.write_text(entry(\"the first words\") + entry(\"and more of them\")) ^^^^ E       NameError: name 'said' is not defined src/features/history_searches/test.py:31: NameError =========================== short test summary info ============================ FAILED src/features/history_searches/test.py::test_a_transcript_rewritten_in_place_is_read_again_not_served_from_the_cache 1 failed, 380 passed in 86.53s (0:01:26); check 27 failed - 1 failed, 380 passed in 86.53s (0 -01 -26) \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "your message 15891 names 7429 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 15891 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2060 stands still while todo 2690 is ready \u2014 if work 2060 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2690. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your wait for the commit check for the transcript cache key and the wake\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 8ec03726 The transcript cache is keyed on the code that\u2026 \u2014 check 27 passed and 8ec03726 The transcript cache is keyed on the code that shapes a turn, and the wake, expiry and rewrite cases are tested is committed; then ran boot guard: installs, serves and launches claude, codex in 7.2s", "meta": {"from": "journal"}}
{"content": "commit 8ec037265 closed to-do 2731, to-do 2757, to-do 2758 and ended work 2060 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 you're editing `test_the_gate.py`, a test/stub/fixture that\u2026 \u2014 Code Commandments \u2014 you're editing `test_the_gate.py`, a test/stub/fixture that `judge` never scans, so nothing here will flag a symptom-fix. If you're changing it to make a failing check pass, first ask WHERE the failure is born: a failing test usually means the PRODUCTION code is missing behaviour \u2014 a method, a type, a total value on the real type. Fix it THERE (trace to the source; re-open the relevant skill), and change this file only if the TEST itself is wrong. If this is just legitimate test coverage, carry on.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 93ms last (50ms of it working), against a budget of 50ms. Seen 455 times.; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "work 2061 in hand \u2014 Test restarting an engine that left a message unheard \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Reading them\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2061 stands still while todo 2691 is ready \u2014 if work 2061 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2691. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "commit 25bbe9c47 closed to-do 2732, to-do 2736 and ended work 2061 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 25bbe9c4 Tests for the unheard-message restart, the kill\u2026 \u2014 check 27 passed and 25bbe9c4 Tests for the unheard-message restart, the kill switch and journal verify is committed; then ran boot guard: installs, serves and launches claude, codex in 8.1s", "meta": {"from": "journal"}}
{"content": "command todo comment is slower than its budget \u2014 128ms last (51ms of it working), against a budget of 50ms. Seen 1 time.; request POST /api/run (todo comment) is slower than its budget \u2014 154ms last (75ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 69ms last (50ms of it working), against a budget of 50ms. Seen 21 times.", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "commit 3d001a16a closed to-do 2738 and ended work 2062 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 3d001a16 Claude's status line payload is tested end to end\u2026 \u2014 check 27 passed and 3d001a16 Claude's status line payload is tested end to end is committed; then ran boot guard: installs, serves and launches claude, codex in 7.9s; Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 81ms last (51ms of it working), against a budget of 50ms. Seen 467 times.", "meta": {"from": "journal"}}
{"content": "commit a295b3633 closed to-do 2694 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and a295b363 A refused Finish on a helper says so on its row\u2026 \u2014 check 27 passed and a295b363 A refused Finish on a helper says so on its row is committed; then ran boot guard: installs, serves and launches claude, codex in 7.2s", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "commit a9105555a closed to-do 2759 and ended work 2063 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and a9105555 A trigger's instruct action is tested is\u2026 \u2014 check 27 passed and a9105555 A trigger's instruct action is tested is committed; then ran boot guard: installs, serves and launches claude, codex in 13.2s", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2064 stands still while todo 2692 is ready \u2014 if work 2064 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2692. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "the scenario-script lesson after the demo driver fix, and the boot tests with\u2026", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "1 new comment 2764; todo 2692 commented", "meta": {"from": "journal"}}
{"content": "check 27 passed and 82170c5b The boot tests wire Codex as well, and installing\u2026 \u2014 check 27 passed and 82170c5b The boot tests wire Codex as well, and installing twice keeps one hook per event is committed; then ran boot guard: installs, serves and launches claude, codex in 7.6s", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "commit 82170c5b3 closed to-do 2726, to-do 2739 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 81ms last (56ms of it working), against a budget of 50ms. Seen 11 times.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2064 stands still while todo 2692 is ready \u2014 if work 2064 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2692. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "the scenario-script lesson rerun with its full trace came back - Rerun the\u2026; 1 new message 15943 - answer by opening your turn with [!reply:15943]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; hook POST /api/hook/claude is slower than its budget \u2014 64ms last (54ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 529 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 63ms last (54ms of it working), against a budget of 50ms. Seen 8 times.; 1 new message 15944 - answer by opening your turn with [!reply:15944]", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2064 stands still while todo 2693 is ready \u2014 if work 2064 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2693. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "waiting: 1 unread worktree 52", "meta": {"from": "journal"}}
{"content": "1 new message 15947 - answer by opening your turn with [!reply:15947]; message 15947 updated", "meta": {"from": "journal"}}
{"content": "the scenario-script lesson with a diagnostic on the helper's say came back\u2026", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 145ms last (57ms of it working, 7ms collecting garbage), against a budget of 50ms. Seen 105 times.", "meta": {"from": "journal"}}
{"content": "the reply tag does this in one step \u2014 [!reply:N] makes the turn itself the reply; it runs only when it opens the last text of your turn; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2064 stands still while todo 2693 is ready \u2014 if work 2064 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2693. Stop only when nothing ready is left.; controller.py names 18 files in the project \u2014 write the path so the chat can link it: src/features/boards/controller.py, src/features/browser_control/controller.py, src/features/checks/controller.py, src/features/collections/controller.py, src/features/critique/controller.py", "meta": {"from": "journal"}}
{"content": "1 new message 15952 - answer by opening your turn with [!reply:15952]", "meta": {"from": "journal"}}
{"content": "the scenario-script lesson with the demo's delivery stub fixed came back\u2026", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 195ms last (57ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 613 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15959 - answer by opening your turn with [!reply:15959]", "meta": {"from": "journal"}}
{"content": "report 73 updated", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "check 27 passed and c05e88b3 The agent is told when it talks about the user\u2026 \u2014 check 27 passed and c05e88b3 The agent is told when it talks about the user instead of to them is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 30.4s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "the docs and memory recording, and the commit check for the third-person rule\u2026", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 119 times.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2064 stands still while todo 2693 is ready \u2014 if work 2064 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2693. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "message 15605 file Screenshot 2026-10-05 at 17.40.44.png needs tags \u2014 inspect the attachment, then journal message tag 15605 \"Screenshot 2026-10-05 at 17.40.44.png\" \"<a few words describing what it shows>\"; message 15947 file Screenshot 2026-10-05 at 20.52.27.png needs tags \u2014 inspect the attachment, then journal message tag 15947 \"Screenshot 2026-10-05 at 20.52.27.png\" \"<a few words describing what it shows>\"", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.; rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 825ms last (86ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 54 times.", "meta": {"from": "journal"}}
{"content": "Code Commandments found 12 sins across 5 skills. \u2014 journal check show 29 says why; fix it, then journal check run 29", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 74ms last (66ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 689 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15985 - answer by opening your turn with [!reply:15985]", "meta": {"from": "journal"}}
{"content": "1 new message 15986 - answer by opening your turn with [!reply:15986]", "meta": {"from": "journal"}}
{"content": "1 new message 15987 - answer by opening your turn with [!reply:15987]", "meta": {"from": "journal"}}
{"content": "1 new message 15988 - answer by opening your turn with [!reply:15988]", "meta": {"from": "journal"}}
{"content": "1 new message 15989 - answer by opening your turn with [!reply:15989]", "meta": {"from": "journal"}}
{"content": "the demo lessons run before the gate came back - Run the demo lessons\u2026", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "1 new comment 2771; todo 2692 commented", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "1 new message 15992 - answer by opening your turn with [!reply:15992]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 129 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 384ms last (114ms of it working), against a budget of 50ms. Seen 754 times.", "meta": {"from": "journal"}}
{"content": "1 new message 15993 - answer by opening your turn with [!reply:15993]", "meta": {"from": "journal"}}
{"content": "message 15993 file Screenshot 2026-10-05 at 21.21.45.png needs tags \u2014 inspect the attachment, then journal message tag 15993 \"Screenshot 2026-10-05 at 21.21.45.png\" \"<a few words describing what it shows>\"; message 15993 updated", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; law L4 \u2014 Follow-up work goes back to the subagent that did the first part\u2026 \u2014 A subagent that drew a design, wrote the code or ran the research keeps what it learned. When the user asks for a change to its work, continue that subagent with a message rather than dispatching a new one that has to rediscover everything; start fresh only when the earlier one is gone or the new work is unrelated.", "meta": {"from": "journal"}}
{"content": "the viewer asked GET /api/main-mies-buildwright/dashboard for more than a page \u2014 GET /api/main-mies-buildwright/dashboard asked for 80 rows GET /api/main-mies-buildwright/dashboard Seen 1 time.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "1 new message 16001 - answer by opening your turn with [!reply:16001]", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "1 new message 16002 - answer by opening your turn with [!reply:16002]", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 176ms last (80ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 779 times.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 138 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 170ms last (61ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 119 times.", "meta": {"from": "journal"}}
{"content": "message 16004 file Screenshot 2026-10-05 at 21.32.13.png needs tags \u2014 inspect the attachment, then journal message tag 16004 \"Screenshot 2026-10-05 at 21.32.13.png\" \"<a few words describing what it shows>\"; request POST /api/main/message is slower than its budget \u2014 228ms last (67ms of it working), against a budget of 50ms. Seen 16 times.; request GET /api/main/dashboard is slower than its budget \u2014 240ms last (69ms of it working), against a budget of 50ms. Seen 58 times.; 1 new message 16004 - answer by opening your turn with [!reply:16004]; message 16004 updated", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Lay out the parts \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:74. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 74 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:74.", "meta": {"from": "journal"}}
{"content": "rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 2 of 6 - Write each part \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:74. Write the parts one at a time and in order with journal report section 74 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:74.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16008 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 3 of 6 - Put it in a collection \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:74. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:74. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:74.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 4 of 6 - Link what it relates to \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:74. Link the rows it answers or was built on, such as the to-dos, plans, documents, reports or messages it is about, with journal report link 74 \"<row>\" for each. Leave out rows it only mentions in passing. Then journal sequence next 10 --about report:74.; fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 5 of 6 - Offer the next step \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:74. If it asks the user to decide or approve something, give it buttons: journal report update 74 --set buttons='[{\"label\": \"Accept this proposal\", \"say\": \"I accept this proposal\", \"choice\": \"answer\"}, {\"label\": \"Change it first\", \"say\": \"I want changes first\", \"choice\": \"answer\"}]'. A button with say sends those words to you as the user's message; one naming a type, n and action runs that command. Buttons of one decision share a choice, so the others go once one is pressed. Skip this when nothing waits on the user. Then journal sequence next 10 --about report:74.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 6 of 6 - Answer with it \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:74. Say in one or two plain lines what it concludes, then its reference on a line of its own, like doc 41 or report 98, never in backticks. Finish with journal sequence next 10 --about report:74.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 270ms last (110ms of it working), against a budget of 50ms. Seen 864 times.; hook POST /api/hook/claude is slower than its budget \u2014 513ms last (150ms of it working, 6ms collecting garbage), against a budget of 50ms. Seen 864 times.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 145 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 154ms last (77ms of it working), against a budget of 50ms. Seen 17 times.; 1 new message 16012 - answer by opening your turn with [!reply:16012]; report 74 updated", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.; request GET /api/main/agent is slower than its budget \u2014 183ms last (60ms of it working), against a budget of 50ms. Seen 122 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16013 - answer by opening your turn with [!reply:16013]", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo create) is slower than its budget \u2014 2936ms last (90ms of it working, 28ms waiting on locks), against a budget of 50ms. Seen 2 times.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "1 new message 16019 - answer by opening your turn with [!reply:16019]", "meta": {"from": "journal"}}
{"content": "rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 149 times.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 310ms last (92ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 935 times.", "meta": {"from": "journal"}}
{"content": "commit c5fa6b0b8 closed to-do 2696 and ended work 2064 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 658ms last (54ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 129 times.; fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "check 27 passed and c5fa6b0b The docs and memory lessons are recorded and\u2026 \u2014 check 27 passed and c5fa6b0b The docs and memory lessons are recorded and listed in the demo, and the layout preset for the developer reads shorter is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 36.3s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 202ms last (65ms of it working, 5ms waiting on locks), against a budget of 50ms. Seen 22 times.", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "commit 5068f90a1 closed to-do 2786 and ended work 2065 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 5068f90a The hourly tidy removes spent helper launch logs\u2026 \u2014 check 27 passed and 5068f90a The hourly tidy removes spent helper launch logs and ended sessions' terminal captures, and an upgrade keeps one backup is committed; then failed boot guard: the journal does not boot, push refused journal claude crashed: journal: port 57456 is still taken after 30s; the viewer moves, and a tab left open on it will not reach this journal Traceback (most recent call last): File \"/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal.py\", line 9, in <module> runpy.run_path(sys.argv[0], run_name=\"__main__\") ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 311, in run_path File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/__main__.py\", line 11, in <module> runpy.run_module(\"journal\", run_name=\"__main__\", alter_sys=True) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 231, in run_module File \"<frozen runpy>\", line 98, in _run_module_code File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/journal.py\", line 9, in <module> sys.exit(run(sys.argv[1:])) ~~~^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/commands/cli.py\", line 129, in run print(query({**ctx, **args}), file=out) ~~~~~^^^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/commands/parser.py\", line 120, in <lambda> add_query(cmds, name, f\"start {name} supervised, on this environment; everything after the word is forwarded to {name}\", lambda ctx, name=name: supervise(ctx, name)) ~~~~~~~~~^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/commands/queries.py\", line 154, in supervise return launch(ctx[\"record\"], agent, ctx[\"args\"]) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/commands/launch.py\", line 193, in launch url = start(record.root, project) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/engine/viewer.py\", line 220, in start return launch(root, project)[0] ~~~~~~^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/engine/viewer.py\", line 231, in launch port = available(root, last(root).port) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-23i_htr4/Project builds/.journal/journal-2.251.0-523e3563ad.pyz/engine/viewer.py\", line 186, in available raise OSError(\"no viewer port available from 8420 through 8439\") OSError: no viewer port available from 8420 through 8439 error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "todo 2693 next", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 71ms last (64ms of it working), against a budget of 50ms. Seen 990 times.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 155 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 92ms last (51ms of it working), against a budget of 50ms. Seen 136 times.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "the await tag does this in one step \u2014 [!await] makes the rest of the turn what you wait for; [!await on=(\"<id>\", \"helper:<n>\")] waits on those; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "the boot guard and push of the demo and tidy commits came back - Push through\u2026", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "helper 75, Grace Dumpsworth, reported in message 16037 \u2014 read it, then journal helper finish 75 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the push of the three waiting commits through the boot guard came back - Retry\u2026", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo create) is slower than its budget \u2014 136ms last (52ms of it working), against a budget of 50ms. Seen 3 times.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16040 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 115ms last (75ms of it working), against a budget of 50ms. Seen 1040 times.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16041 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 185ms last (51ms of it working), against a budget of 50ms. Seen 139 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 1089ms last (689ms of it working, 8ms collecting garbage), against a budget of 50ms. Seen 59 times.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent/1/edits is slower than its budget \u2014 613ms last (57ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 156 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 115ms last (72ms of it working), against a budget of 50ms. Seen 1082 times.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "work 2067 in hand \u2014 The demo chat box cannot be typed in \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 12 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 12 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Still waiting\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "the outline check and the commandments judge of the changed files came back\u2026", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 302ms last (54ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 142 times.", "meta": {"from": "journal"}}
{"content": "the outline check across the memory, bakery and dump lessons came back - Rerun\u2026", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 84ms last (50ms of it working), against a budget of 50ms. Seen 1132 times.", "meta": {"from": "journal"}}
{"content": "the outline recheck across the memory, bakery and dump lessons came back\u2026", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "the outline check across the memory and bakery lessons came back - Recheck\u2026", "meta": {"from": "journal"}}
{"content": "rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 119a2ab5 The demo's chat box keeps the recorded message\u2026 \u2014 check 27 passed and 119a2ab5 The demo's chat box keeps the recorded message, and the button to press next is outlined as in the tour is committed; then ran boot guard: installs, serves and launches claude, codex in 10.4s", "meta": {"from": "journal"}}
{"content": "work 2066, The builder, orchestrator and solo buttons each get their own\u2026 \u2014 it was parked because: waits for Mies's commits, which touch the same Segmented and StatusBar. journal work resume 2066 picks it up again.; todo 2692, Recommend plugins that fit the project's languages, is still\u2026 \u2014 it is blocked because: waits on the user testing the recommendations in the prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2692. If it waits on a person or a decision, make it a question to them: journal todo ask 2692 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2777, Pick how the agent behaves from four personality profiles, is still\u2026 \u2014 it is blocked because: waits until the current work is done and the profiles are discussed with the user (message 15959). If it is not any more, journal todo unblock 2777. If it waits on a person or a decision, make it a question to them: journal todo ask 2777 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; commit 119a2ab5d closed to-do 2781, to-do 2782 and ended work 2067 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 86ms last (61ms of it working), against a budget of 50ms. Seen 60 times.", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 1315ms last (475ms of it working, 10ms waiting on locks), against a budget of 50ms. Seen 1204 times.", "meta": {"from": "journal"}}
{"content": "the user put \u2764\ufe0f on comment 2770 - act on it if it asks for something, such as a go-ahead. It needs no reply, and the chat never mentions it; commit d308c2ba4 closed to-do 2779 \u2014 The rows and the work are done; take the next one.; request GET /api/main/agent is slower than its budget \u2014 151ms last (54ms of it working), against a budget of 50ms. Seen 148 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and d308c2ba Builder, Orchestrator and Solo each show what\u2026 \u2014 check 27 passed and d308c2ba Builder, Orchestrator and Solo each show what that mode does when hovered is committed; then ran boot guard: installs, serves and launches claude, codex in 9.9s", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; request POST /api/run (comment show) is slower than its budget \u2014 721ms last (373ms of it working, 9ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 8352e81a A read item in the dump pile reads '\u2713 read' once\u2026 \u2014 check 27 passed and 8352e81a A read item in the dump pile reads '\u2713 read' once is committed; then ran boot guard: installs, serves and launches claude, codex in 10.0s", "meta": {"from": "journal"}}
{"content": "commit 8352e81ac closed to-do 2789 and ended work 2068 \u2014 The rows and the work are done; take the next one.; the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 157 times.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 88ms last (62ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 19 times.; 1 new message 16074 - answer by opening your turn with [!reply:16074]", "meta": {"from": "journal"}}
{"content": "the end tag does this in one step \u2014 [!end:N] makes the turn itself what landed; it runs only when it opens the last text of your turn; journal-ask-questions, journal-memory changed since you loaded them \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "work 2069 in hand \u2014 The stop button moves off the pause button into the agent\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16081 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed ........................................................................ [ 93%] .................F.....                                                  [100%] =================================== FAILURES =================================== ______________ test_a_killed_server_is_reaped_so_a_new_one_starts ______________ [gw3] darwin -- Python 3.14.7 /Users/jessegall/projects/agent-journal/.venv/bin/python tmp_path = PosixPath('/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1422/popen-gw3/test_a_killed_server_is_reaped0') monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x119682c90> def test_a_killed_server_is_reaped_so_a_new_one_starts(tmp_path, monkeypatch): import signal from engine import viewer from engine.sessions import alive root = installed(tmp_path) monkeypatch.setenv(\"HOME\", str(tmp_path / \"home\")) starter = subprocess.Popen([sys.executable, \"-c\", f\"import sys, time; sys.path.insert(0, {str(CODE)!r}); from pathlib import Path; from engine import viewer; \" f\"root = Path({str(root)!r}); print(viewer.launch(root, root.parent)[0], flush=True); time.sleep({WAIT})\"], stdout=subprocess.PIPE, text=True) try: assert starter.stdout.readline().strip(), \"the server answers\" killed = viewer.last(root).pid os.kill(killed, signal.SIGKILL) began = time.time() while alive(killed) and time.time() - began < WAIT / 3: time.sleep(0.1) assert not alive(killed), \"the process that started the server reaps it, so it is not left a zombie that looks alive\" finally: starter.kill() starter.wait(WAIT) url = viewer.launch(root, root.parent)[0] try: assert url, \"a new server starts where the killed one was\" finally: >           os.kill(viewer.last(root).pid, signal.SIGTERM) E           ProcessLookupError: [Errno 3] No such process tests/test_it_boots.py:231: ProcessLookupError =========================== short test summary info ============================ FAILED tests/test_it_boots.py::test_a_killed_server_is_reaped_so_a_new_one_starts 1 failed, 382 passed in 111.35s (0:01:51); check 27 failed - 1 failed, 382 passed in 111.35s (0 -01 -51) \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 119ms last (83ms of it working), against a budget of 50ms. Seen 1257 times.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16084 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "1 new message 16086 - answer by opening your turn with [!reply:16086]", "meta": {"from": "journal"}}
{"content": "the viewer asked GET /api/main-dieter-pollwright/dashboard for more than a page \u2014 GET /api/main-dieter-pollwright/dashboard asked for 80 rows GET /api/main-dieter-pollwright/dashboard Seen 1 time.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 154ms last (57ms of it working), against a budget of 50ms. Seen 150 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 433ms last (97ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 1 time.; request GET /api/main/dashboard is slower than its budget \u2014 2201ms last (1116ms of it working, 18ms collecting garbage), against a budget of 50ms. Seen 61 times.; request POST /api/main/settings is slower than its budget \u2014 375ms last (57ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "journal-memory changed since you loaded them \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "question 196 completed", "meta": {"from": "journal"}}
{"content": "check 27 passed and 12b4c550 The agent is stopped from its name in the agent\u2026 \u2014 check 27 passed and 12b4c550 The agent is stopped from its name in the agent bar, not from a square button beside pause is committed; then ran boot guard: installs, serves and launches claude, codex in 9.9s", "meta": {"from": "journal"}}
{"content": "1 new message 16091 - answer by opening your turn with [!reply:16091]", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; commit 12b4c5506 closed to-do 2784 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (check create) is slower than its budget \u2014 827ms last (502ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 7 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16093 - answer by opening your turn with [!reply:16093]", "meta": {"from": "journal"}}
{"content": "message 16093 file Screenshot 2026-10-05 at 22.40.07.png needs tags \u2014 inspect the attachment, then journal message tag 16093 \"Screenshot 2026-10-05 at 22.40.07.png\" \"<a few words describing what it shows>\"; request POST /api/main/message is slower than its budget \u2014 356ms last (88ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 20 times.; message 16093 updated", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; request POST /api/run (todo create) is slower than its budget \u2014 250ms last (67ms of it working, 2ms waiting on locks), against a budget of 50ms. Seen 5 times.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "command message search is slower than its budget \u2014 2447ms last (2181ms of it working, 75ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 111ms last (82ms of it working), against a budget of 50ms. Seen 1318 times.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 124ms last (51ms of it working, 4ms collecting garbage), against a budget of 50ms. Seen 164 times.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 159 times.", "meta": {"from": "journal"}}
{"content": "rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.; request GET /api/main/dashboard is slower than its budget \u2014 133ms last (66ms of it working), against a budget of 50ms. Seen 70 times.; commit 6d744c4a6 closed to-do 2823 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 6d744c4a A row is linked to a message only when the agent\u2026 \u2014 check 27 passed and 6d744c4a A row is linked to a message only when the agent processes the message into it, never by guessing is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 16.0s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 457ms last (82ms of it working, 4ms collecting garbage), against a budget of 50ms. Seen 5 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "work 2070 in hand \u2014 The tour and the demo outline share one followed-box\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 the files changed since the last check (`payload.py`, `inte\u2026 \u2014 Code Commandments \u2014 the files changed since the last check (`payload.py`, `interceptors.py`, `feature.py`, `details.py`) breaks a rule. Fix it now, at its SOURCE, while the code is still in front of you: \u00b7 \u2022 python-dict-bag at /Users/jessegall/projects/agent-journal/src/features/ask_questions/interceptors.py:21 \u00b7 LOAD the skill `commandments-python-value-objects` before fixing \u2014 load it even if you believe you already have. \u00b7 Run `commandments info <sin>` if a rule is not one you recognise. This check reads a file at a time, so it is not the whole picture \u2014 `judge` still is.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 257ms last (100ms of it working, 12ms waiting on locks), against a budget of 50ms. Seen 1410 times.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.; request POST /api/run (work end) is slower than its budget \u2014 780ms last (474ms of it working, 5ms collecting garbage), against a budget of 50ms. Seen 2 times.; request POST /api/run (todo start) is slower than its budget \u2014 109ms last (83ms of it working), against a budget of 50ms. Seen 23 times.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "the viewer asked GET /api/main-edwin-lastlook/notice for more than a page \u2014 GET /api/main-edwin-lastlook/notice asked for 100 rows GET /api/main-edwin-lastlook/notice Seen 1 time.; the viewer asked GET /api/main-edwin-lastlook/comment for more than a page \u2014 GET /api/main-edwin-lastlook/comment asked for 100 rows GET /api/main-edwin-lastlook/comment Seen 1 time.; the viewer asked GET /api/main-edwin-lastlook/message for more than a page \u2014 GET /api/main-edwin-lastlook/message asked for 100 rows GET /api/main-edwin-lastlook/message Seen 1 time.; the viewer asked GET /api/main-edwin-lastlook/work for more than a page \u2014 GET /api/main-edwin-lastlook/work asked for 100 rows GET /api/main-edwin-lastlook/work Seen 1 time.; the viewer asked GET /api/main-edwin-lastlook/nudge for more than a page \u2014 GET /api/main-edwin-lastlook/nudge asked for 100 rows GET /api/main-edwin-lastlook/nudge Seen 1 time.", "meta": {"from": "journal"}}
{"content": "the viewer asked GET /api/main-hedy-lamarr/nudge for more than a page \u2014 GET /api/main-hedy-lamarr/nudge asked for 100 rows GET /api/main-hedy-lamarr/nudge Seen 1 time.; the viewer asked GET /api/main-hedy-lamarr/notice for more than a page \u2014 GET /api/main-hedy-lamarr/notice asked for 100 rows GET /api/main-hedy-lamarr/notice Seen 1 time.; the viewer asked GET /api/main-hedy-lamarr/message for more than a page \u2014 GET /api/main-hedy-lamarr/message asked for 100 rows GET /api/main-hedy-lamarr/message Seen 1 time.; the viewer asked GET /api/main-hedy-lamarr/work for more than a page \u2014 GET /api/main-hedy-lamarr/work asked for 100 rows GET /api/main-hedy-lamarr/work Seen 1 time.; the viewer asked GET /api/main-hedy-lamarr/comment for more than a page \u2014 GET /api/main-hedy-lamarr/comment asked for 100 rows GET /api/main-hedy-lamarr/comment Seen 1 time.; the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 160 times.; the viewer asked GET /api/main-margaret-hamilton/work for more than a page \u2014 GET /api/main-margaret-hamilton/work asked for 100 rows GET /api/main-margaret-hamilton/work Seen 1 time.; the viewer asked GET /api/main-margaret-hamilton/message for more than a page \u2014 GET /api/main-margaret-hamilton/message asked for 100 rows GET /api/main-margaret-hamilton/message Seen 1 time.; the viewer asked GET /api/main-margaret-hamilton/nudge for more than a page \u2014 GET /api/main-margaret-hamilton/nudge asked for 100 rows GET /api/main-margaret-hamilton/nudge Seen 1 time.; the viewer asked GET /api/main-margaret-hamilton/comment for more than a page \u2014 GET /api/main-margaret-hamilton/comment asked for 100 rows GET /api/main-margaret-hamilton/comment Seen 1 time.; the viewer asked GET /api/main-margaret-hamilton/notice for more than a page \u2014 GET /api/main-margaret-hamilton/notice asked for 100 rows GET /api/main-margaret-hamilton/notice Seen 1 time.", "meta": {"from": "journal"}}
{"content": "rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed filler = Sequences(record, actor=AGENT, agent=\"board-filler\") filler.follow(exploring.n, about=made.ref) assert list(filler.load(exploring.n).runs.values())[0][\"agent\"] == \"board-filler\" and Sequences(record, actor=AGENT).in_hand() is None, \\ \"the filler's run is its own: the main agent never has it in hand\" >       asked = Boards(record, actor=AGENT, agent=\"board-filler\").ask(board.n, \"Which goal?\", options=[{\"title\": \"A\"}, {\"title\": \"B\"}]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ src/features/boards/test.py:47: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ src/features/boards/requests.py:26: in ask return asking.create(question, abstract, about=board.ref, hidden=True, **data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ src/controllers/questions.py:24: in create return super().create(title, abstract, brief, **data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ src/controllers/base.py:172: in create taken = self._handled(\"create\", title=title, abstract=abstract, brief=brief, **data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ src/controllers/base.py:142: in _handled taken = fn(self, **args) ^^^^^^^^^^^^^^^^ src/features/wiring.py:151: in <lambda> lambda controller, **args: interceptor.intercept(Context.of(feature, controller.record), controller, **args) if feature.enabled(controller.record) else None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ src/features/ask_questions/interceptors.py:57: in intercept controller._refuse(f\"name the option you would pick with --set pick=<1 to {len(titles)}>: the card marks it as the agent's pick\") _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <controllers.questions.Questions object at 0x110ff4550> why = \"name the option you would pick with --set pick=<1 to 2>: the card marks it as the agent's pick\" def _refuse(self, why: str) -> None: if not self.force: >           raise Refused(why) E           resources.base.Refused: name the option you would pick with --set pick=<1 to 2>: the card marks it as the agent's pick src/controllers/base.py:77: Refused =========================== short test summary info ============================ FAILED src/features/boards/test.py::test_a_request_opens_a_session_that_cancel_closes 1 failed, 381 passed in 167.90s (0:02:47)", "meta": {"from": "journal"}}
{"content": "check 27 failed - 1 failed, 381 passed in 167.90s (0 -02 -47) \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 318ms last (50ms of it working), against a budget of 50ms. Seen 173 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 364ms last (73ms of it working, 3ms waiting on locks), against a budget of 50ms. Seen 1468 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 382ms last (93ms of it working), against a budget of 50ms. Seen 74 times.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 12 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 12 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "waiting: 1 unread worktree 54", "meta": {"from": "journal"}}
{"content": "1 new comment 2788; todo 2777 commented", "meta": {"from": "journal"}}
{"content": "todo 2777 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16122 - answer by opening your turn with [!reply:16122]", "meta": {"from": "journal"}}
{"content": "the commandments judge of the changed files, and the gate for the agent's pick\u2026", "meta": {"from": "journal"}}
{"content": "sequence 2, Building a plan, step 1 of 4 - Name the goal \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 2 --about plan:26. Settle with the user what is true when the plan is done, and set it as the plan's goal. When it is done: journal sequence next 2 --about plan:26.; plan 26 is building - add its phases \u2014 journal plan phase 26 \"<title>\" --when \"<complete when>\" for each phase, --checkpoint where the user should look; then journal plan stage 26 todos; commit ac15cb91b closed to-do 2824 and ended work 2071 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and ac15cb91 Every question the agent asks with options names\u2026 \u2014 check 27 passed and ac15cb91 Every question the agent asks with options names its pick, which the card marks is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 17.2s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "sequence 2, Building a plan, step 2 of 4 - Add the phases \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 2 --about plan:26. Add every phase in order with journal plan phase 26 \"<title>\" --when \"<complete when>\", and --checkpoint where the user should look before it goes on. When it is done: journal sequence next 2 --about plan:26.", "meta": {"from": "journal"}}
{"content": "sequence 2, Building a plan, step 3 of 4 - File the rows \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 2 --about plan:26. journal plan stage 26 todos, then file the to-dos and put each under its phase with journal plan todos 26 <phase> <rows>. When it is done: journal sequence next 2 --about plan:26.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "plan 26 is at its to-dos \u2014 file each phase's rows and put them under it with journal plan todos 26 <phase> <rows...>; when every phase has rows, journal plan ready 26", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn; request POST /api/run (todo create) is slower than its budget \u2014 593ms last (89ms of it working, 11ms waiting on locks), against a budget of 50ms. Seen 6 times.; every phase of plan 26 has its to-dos \u2014 journal plan ready 26 hands it to the user, who approves it", "meta": {"from": "journal"}}
{"content": "sequence 2, Building a plan, step 4 of 4 - Hand it over \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 2 --about plan:26. When every phase has rows, journal plan ready 26. Only the user approves it; you start it when they have. When it is done: journal sequence next 2 --about plan:26.", "meta": {"from": "journal"}}
{"content": "command message reply is slower than its budget \u2014 295ms last (69ms of it working), against a budget of 50ms. Seen 2 times.; request POST /api/run (message reply) is slower than its budget \u2014 314ms last (84ms of it working), against a budget of 50ms. Seen 9 times.; request GET /api/main/agent is slower than its budget \u2014 188ms last (59ms of it working, 11ms collecting garbage), against a budget of 50ms. Seen 191 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 167ms last (107ms of it working), against a budget of 50ms. Seen 1555 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 350ms last (100ms of it working), against a budget of 50ms. Seen 24 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 555ms last (92ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 21 times.; 1 new message 16129 - answer by opening your turn with [!reply:16129]", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 164 times.", "meta": {"from": "journal"}}
{"content": "the viewer asked GET /api/main-dieter-pollwright/dashboard for more than a page \u2014 GET /api/main-dieter-pollwright/dashboard asked for 80 rows GET /api/main-dieter-pollwright/dashboard Seen 2 times.", "meta": {"from": "journal"}}
{"content": "your message 16133 names 100 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16133 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "1 new message 16135 - answer by opening your turn with [!reply:16135]", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; commit b7307c7e6 closed to-do 2788 and ended work 2072 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "answer message 16135 before you write anything \u2014 answer by opening your turn with [!reply:16135]. a reply, a reaction, or journal message processed <n>; todo 2693 next; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "1 new message 16136 - answer by opening your turn with [!reply:16136]", "meta": {"from": "journal"}}
{"content": "check 27 passed and b7307c7e A sequence's page shows the steps an included\u2026 \u2014 check 27 passed and b7307c7e A sequence's page shows the steps an included sequence hands out is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 58.7s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "the journal is ready on main \u2014 say hello in the chat in plain words, so the journal's messages reach you", "meta": {"from": "journal"}}
{"content": "1 new message 16141 - answer by opening your turn with [!reply:16141]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "the user put \ud83d\udc4d on comment 2793 - act on it if it asks for something, such as a go-ahead. It needs no reply, and the chat never mentions it", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Lay out the parts \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:75. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 75 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:75.; fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; hook POST /api/hook/claude is slower than its budget \u2014 628ms last (151ms of it working, 8ms waiting on locks), against a budget of 50ms. Seen 1587 times.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (report create) is slower than its budget \u2014 576ms last (129ms of it working, 195ms waiting on locks), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 2 of 6 - Write each part \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:75. Write the parts one at a time and in order with journal report section 75 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:75.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 3 of 6 - Put it in a collection \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:75. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:75. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:75.", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 166 times.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, is still at step 3 of 6 - carry on with it \u2014 finishing it comes before anything else; do the step now, Put it in a collection: If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:75. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:75.", "meta": {"from": "journal"}}
{"content": "1 new message 16144 - answer by opening your turn with [!reply:16144]", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed ........................................................................ [ 37%] ........................................................................ [ 56%] ........................................................................ [ 75%] .................F...................................................... [ 94%] ......................                                                   [100%] =================================== FAILURES =================================== ________ test_a_row_named_by_a_bare_number_is_named_back_with_its_type _________ [gw2] darwin -- Python 3.14.7 /Users/jessegall/projects/agent-journal/.venv/bin/python def test_a_row_named_by_a_bare_number_is_named_back_with_its_type(): from engine import chat record = fresh() report(record, \"working\", \"PreToolUse\") asked, filed = [Messages(record, actor=\"user\").create(f\"hi {i}\") for i in range(2)][-1], Works(record, actor=AGENT).create(\"a job\") chat.send(record, Agents(record, actor=\"system\").by_session(\"claude-1\"), f\"Answered {asked.n}, parked {filed.n}, then work {filed.n}; the suite ({asked.n}) and \\\"finished {filed.n}\\\" pass\") lines = [n for n in nudges(record) if \"without saying what they are\" in n] assert len(lines) == 1 and f\"names {asked.n}, {filed.n} \" in lines[0], \"the bare numbers of real rows are named back, versions and counts are not\" chat.send(record, Agents(record, actor=\"system\").by_session(\"claude-1\"), f\"My reply to {asked.n} went through; parking {filed.n}, 2 revisions left, released 2.84.63\") lines = [n for n in nudges(record) if \"without saying what they are\" in n] assert f\"names {asked.n}, {filed.n} \" in lines[-1], \"any bare reference is named back, whatever word comes before it\" from features.messages.prose import bare assert bare(f\"Two steps:\\n{asked.n}. first\\n{filed.n}) second\") == [], \"the numbers of a numbered list are not row numbers\" assert bare(f\"down from 980 loose files to {asked.n}; it waited {filed.n} before\") == [], \"a small number with no handling verb before it is a count\" assert bare(\"a number under 250 is a count, and so is more than 300\") == [], \"a quantity word before a number makes it a count\" assert formatted(\"a journal question with options; journal question ask\", record, VIEWER) == \"a journal question with options; `journal question ask`\", \"only a real command is code\" shown = formatted(\"run python3 journal.py --root .journal upgrade, or pass --why\", record, VIEWER) assert shown.endswith(\" --root .journal upgrade, or pass `--why`\"), \"a flag of another program and a .journal path stay plain text\" long = \"see src/a.py and docs/b.md --flag \" * 4000 began = time.perf_counter() formatted(long, record, VIEWER) >       assert time.perf_counter() - began < 1.0, \"a long text with many paths and flags formats in linear time\" E       AssertionError: a long text with many paths and flags formats in linear time E       assert (747178.730775291 - 747177.6223465) < 1.0 E        +  where 747178.730775291 = <built-in function perf_counter>() E        +    where <built-in function perf_counter> = time.perf_counter src/features/messages/test.py:191: AssertionError =========================== short test summary info ============================ FAILED src/features/messages/test.py::test_a_row_named_by_a_bare_number_is_named_back_with_its_type 1 failed, 381 passed in 188.82s (0:03:08); check 27 failed - 1 failed, 381 passed in 188.82s (0 -03 -08) \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, is still at step 3 of 6 - carry on with it \u2014 finishing it comes before anything else; do the step now, Put it in a collection: If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:75. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:75.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 4 of 6 - Link what it relates to \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:75. Link the rows it answers or was built on, such as the to-dos, plans, documents, reports or messages it is about, with journal report link 75 \"<row>\" for each. Leave out rows it only mentions in passing. Then journal sequence next 10 --about report:75.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 6 of 6 - Answer with it \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:75. Say in one or two plain lines what it concludes, then its reference on a line of its own, like doc 41 or report 98, never in backticks. Finish with journal sequence next 10 --about report:75.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "command message reply is slower than its budget \u2014 151ms last (52ms of it working), against a budget of 50ms. Seen 4 times.; request POST /api/run (message reply) is slower than its budget \u2014 172ms last (70ms of it working), against a budget of 50ms. Seen 11 times.", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "1 new message 16148 - answer by opening your turn with [!reply:16148]", "meta": {"from": "journal"}}
{"content": "1 new message 16149 - answer by opening your turn with [!reply:16149]; report 75 updated", "meta": {"from": "journal"}}
{"content": "commit 8f14c3f92 closed to-do 2825 and ended work 2073 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "the user approved plan 26, Personality profiles - start it \u2014 journal plan start 26 makes it active and parks a plan that runs; then work its first phase's rows in order; plan 26 updated", "meta": {"from": "journal"}}
{"content": "check 27 passed and 8f14c3f9 Inspectors ask another environment for one page\u2026 \u2014 check 27 passed and 8f14c3f9 Inspectors ask another environment for one page of rows, not 80 or 100 is committed; then ran boot guard: installs, serves and launches claude, codex in 12.9s", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.; request POST /api/run (todo start) is slower than its budget \u2014 649ms last (140ms of it working, 16ms waiting on locks), against a budget of 50ms. Seen 25 times.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16155 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the viewer sent GET /api/summary twice at once \u2014 two requests to GET /api/summary were in flight at once GET /api/summary Seen 170 times.", "meta": {"from": "journal"}}
{"content": "todo 2692, Recommend plugins that fit the project's languages, is still\u2026 \u2014 it is blocked because: waits on the user testing the recommendations in the prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2692. If it waits on a person or a decision, make it a question to them: journal todo ask 2692 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2690, Settings split into feature and system tabs, named in plain\u2026 \u2014 journal todo start 2690 when it is next", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 59ms last (50ms of it working), against a budget of 50ms. Seen 1653 times.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16162 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "work 2074 in hand \u2014 The profile setting starts empty and Butler stands in \u2014 if this is not what you are doing, end it or park it and start the work you are in; fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "your message 16168 names 2812, 2815, 2817 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16168 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 7 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 7 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 82bd5289 The agent talks in the voice of the profile you\u2026 \u2014 check 27 passed and 82bd5289 The agent talks in the voice of the profile you choose: Butler, Homie, Colleague or Coach is committed; then ran boot guard: installs, serves and launches claude, codex in 9.3s", "meta": {"from": "journal"}}
{"content": "commit 82bd5289e closed to-do 2827, to-do 2828 and ended work 2074 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; hook POST /api/hook/claude is slower than its budget \u2014 103ms last (55ms of it working, 1ms waiting on locks), against a budget of 50ms. Seen 1702 times.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "journal-ask-questions, journal-memory, journal-plans changed since you loaded\u2026 \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 276ms last (100ms of it working), against a budget of 50ms. Seen 26 times.", "meta": {"from": "journal"}}
{"content": "commit 8c3523f91 closed to-do 2812, to-do 2815, to-do 2817 and ended work 2075 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 8c3523f9 Three review findings - the tidy test is two\u2026 \u2014 check 27 passed and 8c3523f9 Three review findings: the tidy test is two tests, the agent menu reads its environment once, and the third-person check is named for what it is is committed; then ran boot guard: installs, serves and launches claude, codex in 9.5s", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 89ms last (71ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 1752 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 316ms last (74ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 200 times.", "meta": {"from": "journal"}}
{"content": "commandments-python-value-objects, journal-ask-questions, journal-memory\u2026 \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "the log tag does this in one step \u2014 [!log:N] makes the turn itself the log entry; it runs only when it opens the last text of your turn; request GET /api/main/dashboard is slower than its budget \u2014 128ms last (51ms of it working), against a budget of 50ms. Seen 77 times.; request POST /api/run (work log) is slower than its budget \u2014 202ms last (64ms of it working), against a budget of 50ms. Seen 9 times.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "helper 76, Studs Terkelwright, reported in message 16184 \u2014 read it, then journal helper finish 76 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 7 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 7 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and ecbd230c A plan under review waits for its reviewers'\u2026 \u2014 check 27 passed and ecbd230c A plan under review waits for its reviewers' report before it can be approved is committed; then ran boot guard: installs, serves and launches claude, codex in 8.5s", "meta": {"from": "journal"}}
{"content": "the commandments judge of the plan review changes, and the gate came back\u2026", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "helper 76, Studs Terkelwright, reported in message 16188 \u2014 read it, then journal helper finish 76 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (helper finish) is slower than its budget \u2014 438ms last (110ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 70ms last (52ms of it working), against a budget of 50ms. Seen 79 times.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.; request POST /api/run (work end) is slower than its budget \u2014 277ms last (215ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 3 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 61ms last (55ms of it working), against a budget of 50ms. Seen 27 times.", "meta": {"from": "journal"}}
{"content": "Code Commandments found 10 sins across 4 skills. \u2014 journal check show 29 says why; fix it, then journal check run 29", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 64ms last (60ms of it working), against a budget of 50ms. Seen 1844 times.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 12 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 12 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "A full judge is already running; it covers these files.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you have an OPEN worklist with 1 sin s\u2026 \u2014 Code Commandments \u2014 before you wrap up: you have an OPEN worklist with 1 sin still in `.journal/plugin-data/code-commandments/sessions/8951e/sins/sins.md`. Finish it before you stop: work straight down \u2014 fix each at its SOURCE, delete its line \u2014 and do NOT re-run judge, re-scan, or re-verify between fixes. Only when the file is EMPTY, run `judge` again (wave by wave; a clean run deletes it). If you are intentionally pausing here, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your wait for While the gate runs I've planned Pip's eight findings. One\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting; your message 16204 names 633 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16204 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16206 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "commit baae65fd9 closed to-do 2778 and ended work 2080 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and baae65fd The code commandments check is clean but for\u2026 \u2014 check 27 passed and baae65fd The code commandments check is clean but for Resource, which is the journal's entity is committed; then ran boot guard: installs, serves and launches claude, codex in 6.9s", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 83ms last (53ms of it working, 4ms collecting garbage), against a budget of 50ms. Seen 1877 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16212 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "work 2081, Critique round on Dieter's three prototypes, is still parked - can\u2026 \u2014 it was parked because: critics running; Pip's findings meanwhile. journal work resume 2081 picks it up again.", "meta": {"from": "journal"}}
{"content": "rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 10 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 10 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 81cdee73 The second review pass - a voice registry, one\u2026 \u2014 check 27 passed and 81cdee73 The second review pass: a voice registry, one question guard and pick rule, and plainer helpers is committed; then ran boot guard: installs, serves and launches claude, codex in 6.3s", "meta": {"from": "journal"}}
{"content": "work 2081, Critique round on Dieter's three prototypes, is still parked - can\u2026 \u2014 it was parked because: critics running; Pip's findings meanwhile. journal work resume 2081 picks it up again.; todo 2692, Recommend plugins that fit the project's languages, is still\u2026 \u2014 it is blocked because: waits on the user testing the recommendations in the prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2692. If it waits on a person or a decision, make it a question to them: journal todo ask 2692 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; commit 81cdee738 closed to-do 2837, to-do 2838, to-do 2839, to-do 2840, to-do\u2026 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Still waiting\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and d3c2f558 The server reloads when an upgrade points\u2026 \u2014 check 27 passed and d3c2f558 The server reloads when an upgrade points journal.pyz at a new build is committed; then ran boot guard: installs, serves and launches claude, codex in 6.0s", "meta": {"from": "journal"}}
{"content": "commit d3c2f5586 closed to-do 2846 and ended work 2084 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fresh hook timings from the restarted server came back - Start 2774 and look\u2026", "meta": {"from": "journal"}}
{"content": "check live hook timings on the restarted server now - you have waited 5 min \u2014 look at the thing itself: the background shell's output, the process, the run's status. If it is still going, say journal work await \"<what you wait for>\" again and wait. If it finished or failed, journal work log what came of it and take the next step. Never wait for something you can do without.", "meta": {"from": "journal"}}
{"content": "the log tag does this in one step \u2014 [!log:N] makes the turn itself the log entry; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.; hook POST /api/hook/claude is slower than its budget \u2014 89ms last (79ms of it working), against a budget of 50ms. Seen 1965 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.; rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (todo start) is slower than its budget \u2014 131ms last (131ms of it working), against a budget of 50ms. Seen 28 times.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 261ms last (164ms of it working), against a budget of 50ms. Seen 2018 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 3cbaa9dc A helper's tool calls stop running git once its\u2026 \u2014 check 27 passed and 3cbaa9dc A helper's tool calls stop running git once its worktree is up to date is committed; then ran boot guard: installs, serves and launches claude, codex in 5.7s", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "the install of the helper drift fix came back - Install the drift fix\u2026", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "helper 77, Margaret Testwright, reported in message 16239 \u2014 read it, then journal helper finish 77 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "the server reloading itself onto the new build came back - Check the server\u2026", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "todo 2692, Recommend plugins that fit the project's languages, is still\u2026 \u2014 it is blocked because: waits on the user testing the recommendations in the prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2692. If it waits on a person or a decision, make it a question to them: journal todo ask 2692 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "your message 16241 names 500 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16241 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed launched.kill() launched.wait(WAIT) if alone: cleared(place) (place / \"bin\" / f\"{name}.quit\").unlink(missing_ok=True) >       assert \"Traceback\" not in text, f\"journal {name} crashed:\\n{text}\" ^^^^^^^^^^^^^^^^^^^^^^^ E       AssertionError: journal claude crashed: E       Traceback (most recent call last): E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal.py\", line 9, in <module> E           runpy.run_path(sys.argv[0], run_name=\"__main__\") E           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E         File \"<frozen runpy>\", line 311, in run_path E         File \"<frozen runpy>\", line 88, in _run_code E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal-2.251.0-c32346d7da.pyz/__main__.py\", line 11, in <module> E           runpy.run_module(\"journal\", run_name=\"__main__\", alter_sys=True) E           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E         File \"<frozen runpy>\", line 231, in run_module E         File \"<frozen runpy>\", line 98, in _run_module_code E         File \"<frozen runpy>\", line 88, in _run_code E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal-2.251.0-c32346d7da.pyz/journal.py\", line 9, in <module> E           sys.exit(run(sys.argv[1:])) E                    ~~~^^^^^^^^^^^^^^ E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal-2.251.0-c32346d7da.pyz/commands/cli.py\", line 129, in run E           print(query({**ctx, **args}), file=out) E                 ~~~~~^^^^^^^^^^^^^^^^^ E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal-2.251.0-c32346d7da.pyz/commands/parser.py\", line 120, in <lambda> E           add_query(cmds, name, f\"start {name} supervised, on this environment; everything after the word is forwarded to {name}\", lambda ctx, name=name: supervise(ctx, name)) E                                                                                                                                                           ~~~~~~~~~^^^^^^^^^^^ E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal-2.251.0-c32346d7da.pyz/commands/queries.py\", line 154, in supervise E           return launch(ctx[\"record\"], agent, ctx[\"args\"]) E         File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1458/popen-gw0/test_every_agent_launches_from0/Project builds/.journal/journal-2.251.0-c32346d7da.pyz/commands/launch.py\", line 179, in launch E           args = driver.within(args, env) if env in linked(project) or driver.asks_worktree(args) else args E                                                     ~~~~~~^^^^^^^^^ E       TypeError: linked() missing 1 required positional argument: 'folder' scripts/boot_guard.py:54: AssertionError =========================== short test summary info ============================ FAILED tests/test_it_boots.py::test_every_agent_launches_from_an_installed_zip 1 failed, 387 passed in 58.49s; check 27 failed - 1 failed, 387 passed in 58.49s \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "your message 16244 names 400 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16244 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your message 16245 names 400 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16245 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 6 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 6 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed from resources.base import SYSTEM record = fresh() Environments(record, actor=SYSTEM).create(record.env) Sessions(record.root).bind(\"codex-x\", record.env, pid=os.getpid(), provider=\"codex\") answers = iter([\"\", \"side\", \"1\", \"1\"]) ask = lambda _=\"\": next(answers) >       assert asked_for(record, ask=ask, answering=True) == \"side\", \"Enter takes a free choice, here a new environment\" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ src/features/agent_sessions/test.py:230: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ record = <engine.record.Record object at 0x10e59c3c0>, worktree = '' ask = <function test_the_start_question_never_offers_a_busy_environment_on_enter.<locals>.<lambda> at 0x10e59d7a0> answering = True def asked_for(record: Record, worktree: str = \"\", ask=input, answering=None) -> str: answering = sys.stdin.isatty() if answering is None else answering if \"--env\" in sys.argv or worktree: return record.env from controllers.types import Environments from engine.sessions import Sessions from engine.worktree import linked names = [r[\"title\"] for r in Environments(record, actor=SYSTEM).rows.summaries() if not r[\"deleted\"] and not r[\"completed\"]] if not answering: sessions = Sessions(record.root) return record.env if not sessions.holder(record.env) else next((name for name in names if not sessions.holder(name)), record.env) if not names: return record.env sessions = Sessions(record.root) >       worktrees = linked(record.root.parent) ^^^^^^^^^^^^^^^^^^^^^^^^^^ E       TypeError: linked() missing 1 required positional argument: 'folder' src/commands/launch.py:33: TypeError =========================== short test summary info ============================ FAILED tests/test_it_boots.py::test_every_import_in_the_package_resolves - As... FAILED src/features/worktrees/test.py::test_journal_claude_with_a_worktree_makes_it_itself_and_starts_claude_inside_it FAILED src/features/agent_sessions/test.py::test_the_start_question_never_offers_a_busy_environment_on_enter 3 failed, 385 passed in 80.98s (0:01:20)", "meta": {"from": "journal"}}
{"content": "That failure was the worktree-links gate, which ran before the rename. The\u2026", "meta": {"from": "journal"}}
{"content": "check 27 passed and 0f0c6ea9 An action that takes no row, posted to a row's\u2026 \u2014 check 27 passed and 0f0c6ea9 An action that takes no row, posted to a row's route, is refused with a 400 is committed; then ran boot guard: installs, serves and launches claude, codex in 6.8s", "meta": {"from": "journal"}}
{"content": "commit 0f0c6ea94 closed to-do 2848 and ended work 2089 \u2014 The rows and the work are done; take the next one.; hook POST /api/hook/claude is slower than its budget \u2014 208ms last (158ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 2051 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and a37e6de6 A helper's worktree links the folders\u2026 \u2014 check 27 passed and a37e6de6 A helper's worktree links the folders .worktreelinks names, so it can run the viewer-calls walk is committed; then ran boot guard: installs, serves and launches claude, codex in 6.3s", "meta": {"from": "journal"}}
{"content": "The worktree links are pushed (a37e6de6).", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.; rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 384fce20 A worktree's last checked tip is stored as\u2026 \u2014 check 27 passed and 384fce20 A worktree's last checked tip is stored as checked_tip is committed; then ran boot guard: installs, serves and launches claude, codex in 6.6s", "meta": {"from": "journal"}}
{"content": "commit 384fce206 closed to-do 2847 and ended work 2091 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "the judge and the gate for formatted feature help came back - Judge the\u2026", "meta": {"from": "journal"}}
{"content": "check 27 passed and 3c06bd66 A feature's help reaches the viewer through the\u2026 \u2014 check 27 passed and 3c06bd66 A feature's help reaches the viewer through the formatters, with its commands set as code is committed; then ran boot guard: installs, serves and launches claude, codex in 6.8s", "meta": {"from": "journal"}}
{"content": "commit 3c06bd669 closed to-do 2836 and ended work 2092 \u2014 The rows and the work are done; take the next one.; hook POST /api/hook/claude is slower than its budget \u2014 123ms last (100ms of it working), against a budget of 50ms. Seen 2079 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "That file is a throwaway check script I delete after it runs, so it doesn't\u2026", "meta": {"from": "journal"}}
{"content": "the end tag does this in one step \u2014 [!end:N] makes the turn itself what landed; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "law L2 \u2014 Every subagent is bound to a concrete job; never dispatch a generic\u2026 \u2014 Use the most specific available agent type whose declared purpose matches the assignment. On providers without agent types, give the dispatch a concrete task name and bounded prompt. If no suitable specialization exists, keep the work in the main agent instead of manufacturing an unscoped helper.; law L4 \u2014 Follow-up work goes back to the subagent that did the first part\u2026 \u2014 A subagent that drew a design, wrote the code or ran the research keeps what it learned. When the user asks for a change to its work, continue that subagent with a message rather than dispatching a new one that has to rediscover everything; start fresh only when the earlier one is gone or the new work is unrelated.; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 80ms last (55ms of it working), against a budget of 50ms. Seen 80 times.", "meta": {"from": "journal"}}
{"content": "waiting: 2 unread worktrees 56, 57", "meta": {"from": "journal"}}
{"content": "helper 79, Barbara Rowsmith, reported in message 16260 \u2014 read it, then journal helper finish 79 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "helper 78, Edsger Routewright, reported in message 16263 \u2014 read it, then journal helper finish 78 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 229ms last (143ms of it working), against a budget of 50ms. Seen 2181 times.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "your message 16266 names 400, 418 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16266 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed def pushing(*given): try: channel.push(*given) except Stop: pass class Clock: @staticmethod def sleep(seconds: float) -> None: if stopped.is_set(): raise Stop time.sleep(0.01) monkeypatch.setattr(channel, \"time\", Clock) monkeypatch.setattr(channel, \"say\", sent.append) root = tmp_path / \".journal\" queue = runtime.channel_queue(root, 4242) queue.parent.mkdir(parents=True) thread = threading.Thread(target=pushing, args=(root, 4242), daemon=True) thread.start() queue.write_text(json.dumps({\"content\": \"first\"}) + \"\\n\") waited = time.monotonic() while len(sent) < 1 and time.monotonic() - waited < 10: time.sleep(0.02) queue.write_text(queue.read_text() + json.dumps({\"content\": \"second\"}) + \"\\n\") while len(sent) < 2 and time.monotonic() - waited < 10: time.sleep(0.02) stopped.set() thread.join(timeout=10) >       assert [message[\"params\"][\"content\"] for message in sent] == [\"first\", \"second\"], \"a line appended to the queue arrives as one notification, each once\" E       AssertionError: a line appended to the queue arrives as one notification, each once E       assert ['first', 'first; second'] == ['first', 'second'] E E         At index 1 diff: 'first; second' != 'second' E         Use -v to get more diff tests/test_the_gate.py:348: AssertionError =========================== short test summary info ============================ FAILED tests/test_the_gate.py::test_the_claude_channel_answers_its_handshake_and_delivers_each_queued_line_once 1 failed, 405 passed in 91.92s (0:01:31)", "meta": {"from": "journal"}}
{"content": "your wait for These two are the WebP fix and its test assertion, a three-line\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "check 27 passed and 68b342c3 A WebP picture cut short has no dimensions\u2026 \u2014 check 27 passed and 68b342c3 A WebP picture cut short has no dimensions instead of raising, and the channel test appends to its queue is committed; then ran boot guard: installs, serves and launches claude, codex in 7.9s", "meta": {"from": "journal"}}
{"content": "commit 68b342c3c closed to-do 2851 and ended work 2095 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.", "meta": {"from": "journal"}}
{"content": "helper 78, Edsger Routewright, reported in message 16275 \u2014 read it, then journal helper finish 78 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "work 2097 in hand \u2014 A first SessionStart posted through dispatch answers with\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in; commit b0eaa80d4 closed to-do 2853 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 568ms last (309ms of it working), against a budget of 50ms. Seen 2206 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and b0eaa80d An unknown --as actor is refused in words naming\u2026 \u2014 check 27 passed and b0eaa80d An unknown --as actor is refused in words naming the actors there are is committed; then ran boot guard: installs, serves and launches claude, codex in 11.6s; law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "Edsger's rebase, and the briefing gate came back - Edsger Routewright\u2026", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; Edsger's rebase, and the briefing gate came back - Edsger Routewright\u2026", "meta": {"from": "journal"}}
{"content": "todo 2692, Recommend plugins that fit the project's languages, is still\u2026 \u2014 it is blocked because: waits on the user testing the recommendations in the prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2692. If it waits on a person or a decision, make it a question to them: journal todo ask 2692 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2783, Redesign the inspectors and editors for triggers on rules, facts\u2026 \u2014 it is blocked because: waits on the user testing Dieter's revised prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2783. If it waits on a person or a decision, make it a question to them: journal todo ask 2783 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2787, Redesign the choice buttons at the top of a document or report, is\u2026 \u2014 it is blocked because: waits on the user testing Dieter's revised prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2787. If it waits on a person or a decision, make it a question to them: journal todo ask 2787 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2833, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it is blocked because: waits on the user testing Dieter's revised prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2833. If it waits on a person or a decision, make it a question to them: journal todo ask 2833 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; request POST /api/run (todo done) is slower than its budget \u2014 677ms last (61ms of it working), against a budget of 50ms. Seen 3 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (helper finish) is slower than its budget \u2014 260ms last (145ms of it working), against a budget of 50ms. Seen 2 times.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "commit a914ece1c closed to-do 2852 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and a914ece1 A first session start answers with its briefing\u2026 \u2014 check 27 passed and a914ece1 A first session start answers with its briefing even inside a request is committed; then ran boot guard: installs, serves and launches claude, codex in 10.2s", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your message 16291 names 400 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16291 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "waiting: 1 unread worktree 58", "meta": {"from": "journal"}}
{"content": "todo 2854 next", "meta": {"from": "journal"}}
{"content": "check 27 passed and 0a6dab81 A Codex child thread's hooks take the subagent\u2026 \u2014 check 27 passed and 0a6dab81 A Codex child thread's hooks take the subagent path, as a Claude subagent's do is committed; then ran boot guard: installs, serves and launches claude, codex in 9.8s", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "your message 16293 names 2856 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16293 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "your wait for Kent has that one (to-do 2854); I'm waiting on the Codex gate\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "your message 16297 names 400 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16297 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 10 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 10 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 351d79a7 The third review's production points - one\u2026 \u2014 check 27 passed and 351d79a7 The third review's production points: one manifest shape, bounded caches, refusals in words, and stamps where rows are born is committed; then ran boot guard: installs, serves and launches claude, codex in 6.9s", "meta": {"from": "journal"}}
{"content": "the hook hit an error \u2014 journal: the hook hit an error and kept going; the last of it is below and the whole of it is in .journal/runtime/engine.log. Fix it, then say so. the hook got no answer from the server 1 times (codes 000)", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 90717b3e A hook keeps retrying while the server restarts\u2026 \u2014 check 27 passed and 90717b3e A hook keeps retrying while the server restarts on a new build is committed; then ran boot guard: installs, serves and launches claude, codex in 8.3s", "meta": {"from": "journal"}}
{"content": "helper 80, Kent Foldbeck, reported in message 16305 \u2014 read it, then journal helper finish 80 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "Installed. Kent's test fold is the last piece of work in flight; everything\u2026", "meta": {"from": "journal"}}
{"content": "todo 2692, Recommend plugins that fit the project's languages, is still\u2026 \u2014 it is blocked because: waits on the user testing the recommendations in the prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2692. If it waits on a person or a decision, make it a question to them: journal todo ask 2692 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2783, Redesign the inspectors and editors for triggers on rules, facts\u2026 \u2014 it is blocked because: waits on the user testing Dieter's revised prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2783. If it waits on a person or a decision, make it a question to them: journal todo ask 2783 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2787, Redesign the choice buttons at the top of a document or report, is\u2026 \u2014 it is blocked because: waits on the user testing Dieter's revised prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2787. If it waits on a person or a decision, make it a question to them: journal todo ask 2787 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.; todo 2833, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it is blocked because: waits on the user testing Dieter's revised prototype and giving the go (rule 58). If it is not any more, journal todo unblock 2833. If it waits on a person or a decision, make it a question to them: journal todo ask 2833 \"<who decides what>\", and the row waits on their answer. Otherwise tell the user in the chat what it waits on, in their terms, and propose how to clear it.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 `stored.py`, changed since the last check, breaks a rule. F\u2026 \u2014 Code Commandments \u2014 `stored.py`, changed since the last check, breaks a rule. Fix it now, at its SOURCE, while the code is still in front of you: \u00b7 \u2022 python-positional-tuple-return at /Users/jessegall/projects/agent-journal/src/controllers/stored.py:171 \u00b7 LOAD the skill `commandments-python-value-objects` before fixing \u2014 load it even if you believe you already have. \u00b7 Run `commandments info <sin>` if a rule is not one you recognise. This check reads a file at a time, so it is not the whole picture \u2014 `judge` still is.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed write_text(path, \"written\") ~~~~~~~~~~^^^^^^^^^^^^^^^^^ File \"/Users/jessegall/projects/agent-journal/src/engine/stored.py\", line 14, in write_text write_unlocked(path, text) ~~~~~~~~~~~~~~^^^^^^^^^^^^ File \"/Users/jessegall/projects/agent-journal/src/engine/stored.py\", line 25, in write_unlocked replace(path, text.encode()) ~~~~~~~^^^^^^^^^^^^^^^^^^^^^ File \"/Users/jessegall/projects/agent-journal/src/engine/disk.py\", line 50, in replace File \"/opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/pathlib/__init__.py\", line 796, in write_bytes with self.open(mode='wb') as f: ~~~~~~~~~^^^^^^^^^^^ File \"/opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/pathlib/__init__.py\", line 771, in open return io.open(self, mode, buffering, encoding, errors, newline) ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OSError: [Errno 24] Too many open files: '/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/pytest-of-jessegall/pytest-1528/popen-gw3/test_a_held_record_lock_lets_t0/.journal/runtime/.flag.51357.6164787200' Enable tracemalloc to get traceback where the object was allocated. See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info. warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html =========================== short test summary info ============================ FAILED tests/test_every_action.py::test_a_row_of_every_type_written_behind_the_stores_back_shows_up_in_lists_fresh_stale_or_in_bulk FAILED tests/test_every_action.py::test_search_finds_a_row_of_every_type_by_its_words_and_sees_an_edit_that_kept_its_updated_stamp FAILED tests/test_every_action.py::test_an_upload_and_the_file_route_stay_inside_the_rows_folder_for_every_type FAILED tests/test_it_boots.py::test_every_agent_launches_under_the_journal_and_exits_cleanly FAILED tests/test_it_boots.py::test_a_restart_brings_the_agent_back_under_the_same_supervisor FAILED tests/test_it_boots.py::test_every_agent_launches_from_an_installed_zip FAILED tests/test_it_boots.py::test_a_running_server_restarts_on_a_new_build_and_exits_when_asked_to_stop FAILED tests/test_it_boots.py::test_a_supervisor_relays_the_agent_resizes_it_heals_a_crashing_worker_and_leaves_nothing_running FAILED tests/test_the_gate.py::test_a_write_is_refused_until_work_is_open_for_every_provider FAILED tests/test_it_boots.py::test_a_killed_server_is_reaped_so_a_new_one_starts FAILED tests/test_it_boots.py::test_a_message_shown_while_the_server_is_down_reaches_the_chat_once_it_is_back FAILED tests/test_it_boots.py::test_a_migration_that_fails_leaves_the_record_as_it_was FAILED tests/test_every_action.py::test_every_type_with_its_own_word_for_create_is_created_over_http FAILED src/features/agent_sessions/test.py::test_the_start_question_never_offers_a_busy_environment_on_enter FAILED src/features/agent_sessions/test.py::test_stop_in_the_viewer_tells_the_agent_to_stop_that_task_in_its_providers_words FAILED tests/test_it_boots.py::test_a_held_record_lock_lets_the_runtime_folder_write_and_times_out_every_other_write 16 failed, 403 passed, 122 warnings in 61.42s (0:01:01)", "meta": {"from": "journal"}}
{"content": "your wait for No sins. \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "the full suite with the bounded archive cache came back - Bound the cache and\u2026", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 056d9fd7 Search sees a doc edited outside the journal, and\u2026 \u2014 check 27 passed and 056d9fd7 Search sees a doc edited outside the journal, and the store keeps a bounded number of archives open is committed; then failed boot guard: the journal does not boot, push refused journal claude crashed: journal: port 54653 is still taken after 30s; the viewer moves, and a tab left open on it will not reach this journal Traceback (most recent call last): File \"/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal.py\", line 9, in <module> runpy.run_path(sys.argv[0], run_name=\"__main__\") ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 311, in run_path File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/__main__.py\", line 11, in <module> runpy.run_module(\"journal\", run_name=\"__main__\", alter_sys=True) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"<frozen runpy>\", line 231, in run_module File \"<frozen runpy>\", line 98, in _run_module_code File \"<frozen runpy>\", line 88, in _run_code File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/journal.py\", line 9, in <module> sys.exit(run(sys.argv[1:])) ~~~^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/commands/cli.py\", line 129, in run print(query({**ctx, **args}), file=out) ~~~~~^^^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/commands/parser.py\", line 120, in <lambda> add_query(cmds, name, f\"start {name} supervised, on this environment; everything after the word is forwarded to {name}\", lambda ctx, name=name: supervise(ctx, name)) ~~~~~~~~~^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/commands/queries.py\", line 154, in supervise return launch(ctx[\"record\"], agent, ctx[\"args\"]) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/commands/launch.py\", line 193, in launch url = start(record.root, project) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/engine/viewer.py\", line 220, in start return launch(root, project)[0] ~~~~~~^^^^^^^^^^^^^^^ File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/engine/viewer.py\", line 231, in launch port = available(root, last(root).port) File \"/private/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-5unzbe7l/Project builds/.journal/journal-2.251.0-f8a548f972.pyz/engine/viewer.py\", line 186, in available raise OSError(\"no viewer port available from 8420 through 8439\") OSError: no viewer port available from 8420 through 8439 error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "commit 056d9fd7e closed to-do 2865 and ended work 2101 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed src/engine/locks.py:102: in writing with shared_writes(roots[0]).joined(): ^^^^^^^^^^^^^^^^^^^^^^^ src/engine/locks.py:51: in shared_writes SHARED[root] = SharedWrites((root / MIGRATION_LOCK).open(\"a\")) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = PosixPath('/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/agent-journal-6eff8ad3161b4d619b82971268a2a8d5/gw3/tmp9yhh6uii/.journal/.migrations.lock') mode = 'a', buffering = -1, encoding = 'locale', errors = None, newline = None def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): \"\"\" Open the file pointed to by this path and return a file object, as the built-in open() function does. \"\"\" if \"b\" not in mode: encoding = io.text_encoding(encoding) >       return io.open(self, mode, buffering, encoding, errors, newline) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E       OSError: [Errno 24] Too many open files: '/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/agent-journal-6eff8ad3161b4d619b82971268a2a8d5/gw3/tmp9yhh6uii/.journal/.migrations.lock' /opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/pathlib/__init__.py:771: OSError =========================== short test summary info ============================ FAILED tests/test_every_action.py::test_a_row_of_every_type_written_behind_the_stores_back_shows_up_in_lists_fresh_stale_or_in_bulk FAILED tests/test_every_action.py::test_search_finds_a_row_of_every_type_by_its_words_and_sees_an_edit_that_kept_its_updated_stamp FAILED tests/test_it_boots.py::test_a_killed_server_is_reaped_so_a_new_one_starts FAILED tests/test_it_boots.py::test_a_message_shown_while_the_server_is_down_reaches_the_chat_once_it_is_back FAILED tests/test_it_boots.py::test_a_migration_that_fails_leaves_the_record_as_it_was FAILED tests/test_it_boots.py::test_a_restart_brings_the_agent_back_under_the_same_supervisor FAILED tests/test_it_boots.py::test_every_agent_launches_from_an_installed_zip FAILED tests/test_it_boots.py::test_the_event_stream_opens_carries_an_event_and_frees_its_watcher_on_disconnect FAILED tests/test_it_boots.py::test_a_held_record_lock_lets_the_runtime_folder_write_and_times_out_every_other_write FAILED tests/test_it_boots.py::test_a_supervisor_relays_the_agent_resizes_it_heals_a_crashing_worker_and_leaves_nothing_running FAILED tests/test_the_gate.py::test_a_write_is_refused_until_work_is_open_for_every_provider FAILED src/features/agent_sessions/test.py::test_the_start_question_never_offers_a_busy_environment_on_enter FAILED src/features/agent_sessions/test.py::test_stop_in_the_viewer_tells_the_agent_to_stop_that_task_in_its_providers_words ERROR tests/test_it_boots.py::test_a_held_record_lock_lets_the_runtime_folder_write_and_times_out_every_other_write 13 failed, 406 passed, 1 error in 52.64s", "meta": {"from": "journal"}}
{"content": "your wait for It's a three-line cleanup in one test, so no separate scan. \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "the full suite with handles closed after each test came back - Close cached\u2026", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "each server-touching test run alone, to catch the one that leaks a server came\u2026", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 293ms last (189ms of it working), against a budget of 50ms. Seen 2361 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 5454ab3a The test suite closes what it opens - file\u2026 \u2014 check 27 passed and 5454ab3a The test suite closes what it opens: file handles after each test, and stray servers at the end is committed; then ran boot guard: installs, serves and launches claude, codex in 6.5s", "meta": {"from": "journal"}}
{"content": "the await tag does this in one step \u2014 [!await] makes the rest of the turn what you wait for; [!await on=(\"<id>\", \"helper:<n>\")] waits on those; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 5 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 5 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "No sins. The suite is still running.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "check 27 passed and e9c87c16 The last review's points - plain asserts, named\u2026 \u2014 check 27 passed and e9c87c16 The last review's points: plain asserts, named stamps and limits, and caches that close their own handles is committed; then ran boot guard: installs, serves and launches claude, codex in 5.9s", "meta": {"from": "journal"}}
{"content": "check the gate for the last review batch now - you have waited 5 min \u2014 look at the thing itself: the background shell's output, the process, the run's status. If it is still going, say journal work await \"<what you wait for>\" again and wait. If it finished or failed, journal work log what came of it and take the next step. Never wait for something you can do without.", "meta": {"from": "journal"}}
{"content": "nothing is ready - every open row waits \u2014 to-do 2692, Recommend plugins that fit the project's languages; to-do 2783, Redesign the inspectors and editors for triggers on rules, facts and sequences; to-do 2787, Redesign the choice buttons at the top of a document or report; to-do 2833, Redesign the agent inspector, maybe as a large dialog. For each that waits on a person or a decision, put it to them now with journal todo ask <n> \"<who decides what>\"; unblock any that can go on and work it. Stop only when each one waits on a question.", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.; rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Nothing else is open\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead; hook POST /api/hook/claude is slower than its budget \u2014 217ms last (100ms of it working), against a budget of 50ms. Seen 2387 times.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "question 201 completed", "meta": {"from": "journal"}}
{"content": "a fresh hook profile on the current build came back - Profile a hook on the\u2026", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "the end tag does this in one step \u2014 [!end:N] makes the turn itself what landed; it runs only when it opens the last text of your turn; fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "question 200 completed", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; question 199 completed", "meta": {"from": "journal"}}
{"content": "question 198 completed", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 200ms last (85ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "1 new message 16336 - answer by opening your turn with [!reply:16336]", "meta": {"from": "journal"}}
{"content": "the user started plan 26, Personality profiles - it is active now \u2014 work the rows of its current phase, phase 3, in order; a plan that ran is parked and its rows wait; plan 26 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16337 - answer by opening your turn with [!reply:16337]", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 337ms last (303ms of it working, 5ms collecting garbage), against a budget of 50ms. Seen 83 times.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 224ms last (52ms of it working), against a budget of 50ms. Seen 6 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16338 - answer by opening your turn with [!reply:16338]", "meta": {"from": "journal"}}
{"content": "1 new message 16339 - answer by opening your turn with [!reply:16339]", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 323ms last (199ms of it working), against a budget of 50ms. Seen 2436 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (environment all) is slower than its budget \u2014 503ms last (292ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (work log) is slower than its budget \u2014 143ms last (139ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 10 times.", "meta": {"from": "journal"}}
{"content": "answer message 16338, message 16339 before you write anything \u2014 answer each by opening a turn with [!reply:<n>]. a reply, a reaction, or journal message processed <n>", "meta": {"from": "journal"}}
{"content": "todo 2826 next", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 83ms last (60ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 3 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 438ms last (236ms of it working), against a budget of 50ms. Seen 2462 times.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 579cd531 Every open environment is listed, so main shows\u2026 \u2014 check 27 passed and 579cd531 Every open environment is listed, so main shows in the sidebar again is committed; then ran boot guard: installs, serves and launches claude, codex in 5.8s", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (helper finish) is slower than its budget \u2014 358ms last (286ms of it working), against a budget of 50ms. Seen 3 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (worktree show) is slower than its budget \u2014 115ms last (113ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (worktree drop) is slower than its budget \u2014 774ms last (251ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 301ms last (215ms of it working), against a budget of 50ms. Seen 2487 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 108ms last (84ms of it working), against a budget of 50ms. Seen 4 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (work end) is slower than its budget \u2014 90ms last (89ms of it working), against a budget of 50ms. Seen 4 times.", "meta": {"from": "journal"}}
{"content": "helper 81, Massimo Wordwright, reported in message 16355 \u2014 read it, then journal helper finish 81 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Reading it first\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "todo 2826 next", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 785ms last (440ms of it working, 8ms collecting garbage), against a budget of 50ms. Seen 87 times.", "meta": {"from": "journal"}}
{"content": "message 16364 file Screenshot 2026-10-06 at 07.25.47.png needs tags \u2014 inspect the attachment, then journal message tag 16364 \"Screenshot 2026-10-06 at 07.25.47.png\" \"<a few words describing what it shows>\"; 1 new message 16364 - answer by opening your turn with [!reply:16364]; message 16364 updated", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"I'm reading it\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.; rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 53ms last (50ms of it working), against a budget of 50ms. Seen 2509 times.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "1 new message 16374 - answer by opening your turn with [!reply:16374]", "meta": {"from": "journal"}}
{"content": "check 27 passed and 2aa9a382 The help catalogue is warmed at boot, and a hook\u2026 \u2014 check 27 passed and 2aa9a382 The help catalogue is warmed at boot, and a hook looks up its agent's pid only when it seats or moves a session is committed; then ran boot guard: installs, serves and launches claude, codex in 7.9s", "meta": {"from": "journal"}}
{"content": "1 new message 16375 - answer by opening your turn with [!reply:16375]", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 191ms last (61ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "1 new message 16376 - answer by opening your turn with [!reply:16376]", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.; commit 2aa9a382c closed to-do 2872, to-do 2873 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 358ms last (59ms of it working), against a budget of 50ms. Seen 201 times.; 1 new message 16377 - answer by opening your turn with [!reply:16377]", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 157ms last (121ms of it working, 1ms waiting on locks), against a budget of 50ms. Seen 2528 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16378 - answer by opening your turn with [!reply:16378]", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "message 16379 file Screenshot 2026-10-06 at 07.33.18.png needs tags \u2014 inspect the attachment, then journal message tag 16379 \"Screenshot 2026-10-06 at 07.33.18.png\" \"<a few words describing what it shows>\"; 1 new message 16379 - answer by opening your turn with [!reply:16379]; message 16379 updated", "meta": {"from": "journal"}}
{"content": "message 16380 file Screenshot 2026-10-06 at 07.34.10.png needs tags \u2014 inspect the attachment, then journal message tag 16380 \"Screenshot 2026-10-06 at 07.34.10.png\" \"<a few words describing what it shows>\"; 1 new message 16380 - answer by opening your turn with [!reply:16380]; message 16380 updated", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "law L1 \u2014 Every subagent dispatch names its model and chooses the least\u2026 \u2014 Use a fast, economical model for mechanical work with a known answer, a capable general model for careful implementation, and the strongest model only when the task turns on difficult judgement. Inheriting the orchestrator's model is not a model choice. If the dispatch API cannot accept a model, that operation is exempt.; law L2 \u2014 Every subagent is bound to a concrete job; never dispatch a generic\u2026 \u2014 Use the most specific available agent type whose declared purpose matches the assignment. On providers without agent types, give the dispatch a concrete task name and bounded prompt. If no suitable specialization exists, keep the work in the main agent instead of manufacturing an unscoped helper.; law L4 \u2014 Follow-up work goes back to the subagent that did the first part\u2026 \u2014 A subagent that drew a design, wrote the code or ran the research keeps what it learned. When the user asks for a change to its work, continue that subagent with a message rather than dispatching a new one that has to rediscover everything; start fresh only when the earlier one is gone or the new work is unrelated.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "command message reply is slower than its budget \u2014 264ms last (51ms of it working, 2ms waiting on locks), against a budget of 50ms. Seen 5 times.", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.", "meta": {"from": "journal"}}
{"content": "1 new message 16385 - answer by opening your turn with [!reply:16385]", "meta": {"from": "journal"}}
{"content": "1 new message 16386 - answer by opening your turn with [!reply:16386]", "meta": {"from": "journal"}}
{"content": "1 new message 16387 - answer by opening your turn with [!reply:16387]", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 92ms last (89ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 90 times.; 1 new comment 2807; report 76 commented", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 72ms last (55ms of it working), against a budget of 50ms. Seen 2573 times.", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "helper 82, Grace Voicewright, reported in message 16389 \u2014 read it, then journal helper finish 82 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.; rule 50 \u2014 Everything the user does is doable in the viewer \u2014 Message 6710 (2026-09-23): the user never uses the CLI, only the UI; everything should be doable from the viewer. The journal commands are for agents; any action meant for the user (making boards, confirming, accepting, hosting, watching an agent) needs its place in the viewer.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.; request GET /api/main/agent is slower than its budget \u2014 171ms last (55ms of it working), against a budget of 50ms. Seen 205 times.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Lay out the parts \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:77. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 77 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:77.", "meta": {"from": "journal"}}
{"content": "report 77 cites nothing it was built on \u2014 you read report:76 just now: journal report link 77 \"<ref>\" for whichever it came from", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 2 of 6 - Write each part \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:77. Write the parts one at a time and in order with journal report section 77 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:77.; fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 3 of 6 - Put it in a collection \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:77. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:77. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:77.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 4 of 6 - Link what it relates to \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:77. Link the rows it answers or was built on, such as the to-dos, plans, documents, reports or messages it is about, with journal report link 77 \"<row>\" for each. Leave out rows it only mentions in passing. Then journal sequence next 10 --about report:77.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 5 of 6 - Offer the next step \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:77. If it asks the user to decide or approve something, give it buttons: journal report update 77 --set buttons='[{\"label\": \"Accept this proposal\", \"say\": \"I accept this proposal\", \"choice\": \"answer\"}, {\"label\": \"Change it first\", \"say\": \"I want changes first\", \"choice\": \"answer\"}]'. A button with say sends those words to you as the user's message; one naming a type, n and action runs that command. Buttons of one decision share a choice, so the others go once one is pressed. Skip this when nothing waits on the user. Then journal sequence next 10 --about report:77.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 6 of 6 - Answer with it \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:77. Say in one or two plain lines what it concludes, then its reference on a line of its own, like doc 41 or report 98, never in backticks. Finish with journal sequence next 10 --about report:77.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2108 stands still while todo 2826 is ready \u2014 if work 2108 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2826. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16404 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2108 stands still while todo 2826 is ready \u2014 if work 2108 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2826. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "The gate for the forced-start and phone fixes, and the judge on them came back\u2026; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; request POST /api/run (work end) is slower than its budget \u2014 109ms last (73ms of it working), against a budget of 50ms. Seen 5 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and fb5bd96b A forced to-do start opens its work, and the\u2026 \u2014 check 27 passed and fb5bd96b A forced to-do start opens its work, and the phone dialog shows its code as it opens is committed; then ran boot guard: installs, serves and launches claude, codex in 12.5s", "meta": {"from": "journal"}}
{"content": "rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16413 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "rule 44 \u2014 Release a new version after every significant change \u2014 The user, message 13219 (2026-10-01): 'Don't forget to release new versions every time you do something significant.' Bump VERSION, add a CHANGELOG entry, push main and the tag. Replaces message 5929's release-on-request.; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "the user put \ud83d\udc4d on comment 2809 - act on it if it asks for something, such as a go-ahead. It needs no reply, and the chat never mentions it", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "your message 16417 names 2874 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16417 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 82ms last (54ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 2659 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 151ms last (51ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 215 times.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.", "meta": {"from": "journal"}}
{"content": "work 2109 in hand \u2014 A helper's plugin events stay out of the main chat \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "message 16424 file Screenshot 2026-10-06 at 07.55.56.png needs tags \u2014 inspect the attachment, then journal message tag 16424 \"Screenshot 2026-10-06 at 07.55.56.png\" \"<a few words describing what it shows>\"; 1 new message 16424 - answer by opening your turn with [!reply:16424]; message 16424 updated", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "your message 16426 names 2874 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16426 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 1072ms last (406ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 2701 times.", "meta": {"from": "journal"}}
{"content": "your message 16435 names 2874 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16435 \"<the text>\"; fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 13 judged files since t\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 13 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2110 stands still while todo 2826 is ready \u2014 if work 2110 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2826. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "work 2104, Redesign the agent inspector, maybe as a large dialog, is still\u2026 \u2014 it was parked because: plan 26 started by the user; profiles phase 3 first. journal work resume 2104 picks it up again.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 5 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 5 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; check 27 passed and a28f6c3c A helper's plugin cards and files stay in the\u2026 \u2014 check 27 passed and a28f6c3c A helper's plugin cards and files stay in the helper's environment: every plugin payload names its environment's queue, and file routes read the environment's own checkout is committed; then ran boot guard: installs, serves and launches claude, codex in 13.8s", "meta": {"from": "journal"}}
{"content": "The gate for to-do 2874 before the viewer build, and the judge on the unit\u2026", "meta": {"from": "journal"}}
{"content": "1 new message 16442 - answer by opening your turn with [!reply:16442]", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Reading them\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "1 new message 16448 - answer by opening your turn with [!reply:16448]", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat; message 16448 file Screenshot 2026-10-06 at 08.03.37.png needs tags \u2014 inspect the attachment, then journal message tag 16448 \"Screenshot 2026-10-06 at 08.03.37.png\" \"<a few words describing what it shows>\"; message 16448 updated", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; hook POST /api/hook/claude is slower than its budget \u2014 99ms last (66ms of it working), against a budget of 50ms. Seen 2730 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 209ms last (91ms of it working), against a budget of 50ms. Seen 5 times.; 1 new message 16450 - answer by opening your turn with [!reply:16450]; report 77 updated", "meta": {"from": "journal"}}
{"content": "helper 81, Massimo Wordwright, reported in message 16451 \u2014 read it, then journal helper finish 81 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 132ms last (52ms of it working), against a budget of 50ms. Seen 223 times.", "meta": {"from": "journal"}}
{"content": "message 16469 file Screenshot 2026-10-06 at 08.09.48.png needs tags \u2014 inspect the attachment, then journal message tag 16469 \"Screenshot 2026-10-06 at 08.09.48.png\" \"<a few words describing what it shows>\"; 1 new message 16469 - answer by opening your turn with [!reply:16469]; message 16469 updated", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 82ms last (55ms of it working), against a budget of 50ms. Seen 91 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 60ms last (56ms of it working), against a budget of 50ms. Seen 5 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 151ms last (57ms of it working), against a budget of 50ms. Seen 7 times.", "meta": {"from": "journal"}}
{"content": "message 16471 file Screenshot 2026-10-06 at 08.09.48.png needs tags \u2014 inspect the attachment, then journal message tag 16471 \"Screenshot 2026-10-06 at 08.09.48.png\" \"<a few words describing what it shows>\"; 1 new message 16471 - answer by opening your turn with [!reply:16471]; message 16471 updated", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "the user changed how you talk to them \u2014 From now on: HOW TO TALK TO THE USER: Talk like a good butler: polite, calm and to the point. Use their name when you answer one of their messages, and now and then otherwise, never in every message; most of your lines simply say what they need to. Keep a dry sense of humor. Now and then, when it fits, tip your hat with a \ud83c\udfa9 reaction when they call you sir, or put a funny reaction on their message; never on every one. Address them as \"Sir Jesse\".", "meta": {"from": "journal"}}
{"content": "waiting: 1 unread worktree 61", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Still waiting\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Lay out the parts \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:78. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 78 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:78.", "meta": {"from": "journal"}}
{"content": "1 new message 16481 - answer by opening your turn with [!reply:16481]", "meta": {"from": "journal"}}
{"content": "message 16481 updated", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 2 of 6 - Write each part \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:78. Write the parts one at a time and in order with journal report section 78 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:78.; sequence 10, Writing a report, step 3 of 6 - Put it in a collection \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:78. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:78. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:78.; sequence 10, Writing a report, step 4 of 6 - Link what it relates to \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:78. Link the rows it answers or was built on, such as the to-dos, plans, documents, reports or messages it is about, with journal report link 78 \"<row>\" for each. Leave out rows it only mentions in passing. Then journal sequence next 10 --about report:78.; sequence 10, Writing a report, step 5 of 6 - Offer the next step \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:78. If it asks the user to decide or approve something, give it buttons: journal report update 78 --set buttons='[{\"label\": \"Accept this proposal\", \"say\": \"I accept this proposal\", \"choice\": \"answer\"}, {\"label\": \"Change it first\", \"say\": \"I want changes first\", \"choice\": \"answer\"}]'. A button with say sends those words to you as the user's message; one naming a type, n and action runs that command. Buttons of one decision share a choice, so the others go once one is pressed. Skip this when nothing waits on the user. Then journal sequence next 10 --about report:78.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 6 of 6 - Answer with it \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:78. Say in one or two plain lines what it concludes, then its reference on a line of its own, like doc 41 or report 98, never in backticks. Finish with journal sequence next 10 --about report:78.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "1 new message 16484 - answer by opening your turn with [!reply:16484]; message 16484 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16487 - answer by opening your turn with [!reply:16487]; message 16487 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16488 - answer by opening your turn with [!reply:16488]; report 78 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16489 - answer by opening your turn with [!reply:16489]", "meta": {"from": "journal"}}
{"content": "sequence 2, Building a plan, step 1 of 4 - Name the goal \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 2 --about plan:27. Settle with the user what is true when the plan is done, and set it as the plan's goal. When it is done: journal sequence next 2 --about plan:27.", "meta": {"from": "journal"}}
{"content": "1 new message 16493 - answer by opening your turn with [!reply:16493]; message 16493 updated", "meta": {"from": "journal"}}
{"content": "1 new comment 2825; plan 26 commented", "meta": {"from": "journal"}}
{"content": "1 new message 16496 - answer by opening your turn with [!reply:16496]", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed check 27 ran out of time: stopped after 600 seconds; journal check set 27 timeout <seconds> gives it longer", "meta": {"from": "journal"}}
{"content": "1 new message 16499 - answer by opening your turn with [!reply:16499]; message 16499 updated", "meta": {"from": "journal"}}
{"content": "helper 82, Grace Voicewright, reported in message 16500 \u2014 read it, then journal helper finish 82 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "1 new message 16501 - answer by opening your turn with [!reply:16501]; message 16501 updated", "meta": {"from": "journal"}}
{"content": "helper 82, Grace Voicewright, reported in message 16506 \u2014 read it, then journal helper finish 82 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Lay out the parts \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:79. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 79 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:79.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 2 of 6 - Write each part \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:79. Write the parts one at a time and in order with journal report section 79 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:79.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 3 of 6 - Put it in a collection \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:79. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:79. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:79.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 5 of 6 - Offer the next step \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:79. If it asks the user to decide or approve something, give it buttons: journal report update 79 --set buttons='[{\"label\": \"Accept this proposal\", \"say\": \"I accept this proposal\", \"choice\": \"answer\"}, {\"label\": \"Change it first\", \"say\": \"I want changes first\", \"choice\": \"answer\"}]'. A button with say sends those words to you as the user's message; one naming a type, n and action runs that command. Buttons of one decision share a choice, so the others go once one is pressed. Skip this when nothing waits on the user. Then journal sequence next 10 --about report:79.", "meta": {"from": "journal"}}
{"content": "answer message 16499 before you write anything \u2014 answer by opening your turn with [!reply:16499]. a reply, a reaction, or journal message processed <n>", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"I've answered your message\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "1 new message 16515 - answer by opening your turn with [!reply:16515]; report 79 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16517 - answer by opening your turn with [!reply:16517]", "meta": {"from": "journal"}}
{"content": "1 new message 16518 - answer by opening your turn with [!reply:16518]", "meta": {"from": "journal"}}
{"content": "the reply tag does this in one step \u2014 [!reply:N] makes the turn itself the reply; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "1 new message 16520 - answer by opening your turn with [!reply:16520]", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed check 27 ran out of time: stopped after 600 seconds; journal check set 27 timeout <seconds> gives it longer", "meta": {"from": "journal"}}
{"content": "1 new message 16527 - answer by opening your turn with [!reply:16527]", "meta": {"from": "journal"}}
{"content": "message 16527 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16529 - answer by opening your turn with [!reply:16529]", "meta": {"from": "journal"}}
{"content": "1 new message 16531 - answer by opening your turn with [!reply:16531]", "meta": {"from": "journal"}}
{"content": "1 new message 16533 - answer by opening your turn with [!reply:16533]", "meta": {"from": "journal"}}
{"content": "1 new message 16536 - answer by opening your turn with [!reply:16536]", "meta": {"from": "journal"}}
{"content": "helper 74, Mies Buildwright, reported in message 16538 \u2014 read it, then journal helper finish 74 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "The gate for the Documents summary fix, queued on the suite lock came back\u2026", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 6f7b585e The Documents list shows summaries in plain words\u2026 \u2014 check 27 passed and 6f7b585e The Documents list shows summaries in plain words: highlighted text flattens chip markup to its labels is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 23.9s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2111 stands still while todo 2909 is ready \u2014 if work 2111 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2909. Stop only when nothing ready is left.; work deferred in words, not parked \u2014 \"once that run finishes\" is the title of a to-do: journal todo create \"<title>\" --brief, then say so", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2113 stands still while todo 2830 is ready \u2014 if work 2113 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2830. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "The suite timing run for to-do 2909 came back - Run the suite with per-test\u2026", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; hook POST /api/hook/claude is slower than its budget \u2014 589ms last (88ms of it working), against a budget of 50ms. Seen 2807 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/codex is slower than its budget \u2014 955ms last (369ms of it working), against a budget of 50ms. Seen 6 times.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 520ms last (56ms of it working, 13ms collecting garbage), against a budget of 50ms. Seen 232 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your message 16557 names 2888, 2875 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16557 \"<the text>\"; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "work deferred in words, not parked \u2014 \"once the gate lands\" is the title of a to-do: journal todo create \"<title>\" --brief, then say so", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "check 27 passed and d6416f40 A fresh record runs each migration once, the\u2026 \u2014 check 27 passed and d6416f40 A fresh record runs each migration once, the viewer is built for the profile, inspector and helper-row fixes, and rule 59 reaches the designer is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 25.6s error: failed to push some refs to 'https://github.com/jessegall/agent-journal.git'", "meta": {"from": "journal"}}
{"content": "work 2111, Viewer text speaks of the agent, never as I, is still parked - can\u2026 \u2014 it was parked because: Orwell Plainword is rewording; it resumes when his report comes. journal work resume 2111 picks it up again.; todo 2835, An agent's inspector loads older chat as you scroll up, is\u2026 \u2014 journal todo start 2835 when it is next; commit d6416f40f closed to-do 2826, to-do 2830, to-do 2831, to-do 2832, to-do\u2026 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; auto mode is on and work 2113 stands still while todo 2777 is ready \u2014 if work 2113 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2777. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "The boot tests after the split came back - Run the boot tests after the split\u2026", "meta": {"from": "journal"}}
{"content": "helper 82, Grace Voicewright, reported in message 16569 \u2014 read it, then journal helper finish 82 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "message 16573 file Screenshot 2026-10-06 at 08.58.57.png needs tags \u2014 inspect the attachment, then journal message tag 16573 \"Screenshot 2026-10-06 at 08.58.57.png\" \"<a few words describing what it shows>\"; 1 new message 16573 - answer by opening your turn with [!reply:16573]; message 16573 updated", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.", "meta": {"from": "journal"}}
{"content": "helper 81, Massimo Wordwright, reported in message 16575 \u2014 read it, then journal helper finish 81 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "helper 81, Massimo Wordwright, reported in message 16576 \u2014 read it, then journal helper finish 81 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2114 stands still while todo 2777 is ready \u2014 if work 2114 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2777. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 the files changed since the last check (`m0065_abandoned_pl\u2026 \u2014 Code Commandments \u2014 the files changed since the last check (`m0065_abandoned_plans_closed.py`, `controller.py`) breaks a rule. Fix it now, at its SOURCE, while the code is still in front of you: \u00b7 \u2022 python-loop-wrapped-in-if at /Users/jessegall/projects/agent-journal/src/migrations/m0065_abandoned_plans_closed.py:14 \u00b7 LOAD the skill `commandments-python-flow` before fixing \u2014 load it even if you believe you already have. \u00b7 Run `commandments info <sin>` if a rule is not one you recognise. This check reads a file at a time, so it is not the whole picture \u2014 `judge` still is.", "meta": {"from": "journal"}}
{"content": "1 new message 16579 - answer by opening your turn with [!reply:16579]", "meta": {"from": "journal"}}
{"content": "sequence 8, Writing an update, step 1 of 3 - See what changed \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 8 --about message:16579. journal report changes lists what happened since the user last opened an update, under need, done, doing, plans, commits and also. Read any row you do not remember before you sum it up. Then journal sequence next 8 --about message:16579.", "meta": {"from": "journal"}}
{"content": "POST /api/hook/claude hit an error \u2014 journal: POST /api/hook/claude hit an error and kept going; the last of it is below and the whole of it is in .journal/runtime/engine.log. Fix it, then say so. TimeoutError: /Users/jessegall/projects/agent-journal/.journal/.migrations.lock", "meta": {"from": "journal"}}
{"content": "gating on check 27 hit an error \u2014 journal: gating on check 27 hit an error and kept going; the last of it is below and the whole of it is in .journal/runtime/engine.log. Fix it, then say so. TimeoutError: /Users/jessegall/projects/agent-journal/.journal/.migrations.lock; the hook hit an error \u2014 journal: the hook hit an error and kept going; the last of it is below and the whole of it is in .journal/runtime/engine.log. Fix it, then say so. the hook got no answer from the server 1 times (codes 000)", "meta": {"from": "journal"}}
{"content": "helper 83, Orwell Plainword, reported in message 16581 \u2014 read it, then journal helper finish 83 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "your wait for The gates for the test split and the abandoned-plan fix is over\u2026 \u2014 say journal work await \"<what you wait for>\" again if you are still only waiting", "meta": {"from": "journal"}}
{"content": "the await tag does this in one step \u2014 [!await] makes the rest of the turn what you wait for; [!await on=(\"<id>\", \"helper:<n>\")] waits on those; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 417ms last (71ms of it working, 22ms collecting garbage), against a budget of 50ms. Seen 233 times.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "command message reply is slower than its budget \u2014 1478ms last (70ms of it working, 1047ms waiting on locks), against a budget of 50ms. Seen 6 times.; request POST /api/run (message reply) is slower than its budget \u2014 1720ms last (105ms of it working, 1078ms waiting on locks), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2114 stands still while todo 2777 is ready \u2014 if work 2114 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2777. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "work 2111, Viewer text speaks of the agent, never as I, is still parked and 1\u2026 \u2014 it was parked because: Orwell Plainword is rewording; it resumes when his report comes. journal work resume 2111 picks it up again.; commit 4a315f8ea closed to-do 2910 and ended work 2114 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "todo 2777 next; Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 4a315f8e An abandoned plan closes, so it is listed under\u2026 \u2014 check 27 passed and 4a315f8e An abandoned plan closes, so it is listed under Closed; the ones already abandoned are closed by a migration is committed; then failed boot guard: installs, serves and launches claude, codex in 13.0s error: RPC failed; HTTP 408 curl 22 The requested URL returned error: 408 send-pack: unexpected disconnect while reading sideband packet fatal: the remote end hung up unexpectedly", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "your message 16590 names 408 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16590 \"<the text>\"; Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "todo 2777 next", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.; hook POST /api/hook/claude is slower than its budget \u2014 67ms last (61ms of it working), against a budget of 50ms. Seen 2902 times.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 170ms last (52ms of it working), against a budget of 50ms. Seen 92 times.; commit babaaac0c closed to-do 2878, to-do 2885, to-do 2908, to-do 2897 and\u2026 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2113 stands still while todo 2894 is ready \u2014 if work 2113 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2894. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "your message 16598 names 194 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16598 \"<the text>\"; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "check 27 passed and babaaac0 The boot tests run side by side, a command takes\u2026 \u2014 check 27 passed and babaaac0 The boot tests run side by side, a command takes the migration lock only when a migration is pending, and the viewer is built for the answer card and the plain wording is committed; then failed boot guard: installs, serves and launches claude, codex in 10.8s error: RPC failed; HTTP 408 curl 22 The requested URL returned error: 408 send-pack: unexpected disconnect while reading sideband packet fatal: the remote end hung up unexpectedly", "meta": {"from": "journal"}}
{"content": "your message 16604 names 109, 194 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16604 \"<the text>\"; The full suite timed on 10 workers came back - Time the full suite on 10\u2026; rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.; rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 111ms last (54ms of it working), against a budget of 50ms. Seen 236 times.", "meta": {"from": "journal"}}
{"content": "rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2113 stands still while todo 2894 is ready \u2014 if work 2113 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2894. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2113 stands still while todo 2894 is ready \u2014 if work 2113 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2894. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; request POST /api/run (work park) is slower than its budget \u2014 226ms last (78ms of it working, 13ms waiting on locks), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 71ms last (64ms of it working), against a budget of 50ms. Seen 2951 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 126ms last (51ms of it working), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your message 16616 names 109 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16616 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "commandments-python-flow, commandments-python-value-objects, journal-messages\u2026 \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed .........................F.............................................. [ 33%] ........................................................................ [ 50%] ........................................................................ [ 67%] ........................................................................ [ 83%] .....................................................................    [100%] =================================== FAILURES =================================== ________ test_a_row_named_by_a_bare_number_is_named_back_with_its_type _________ [gw5] darwin -- Python 3.14.7 /Users/jessegall/projects/agent-journal/.venv/bin/python def test_a_row_named_by_a_bare_number_is_named_back_with_its_type(): from engine import chat record = fresh() report(record, \"working\", \"PreToolUse\") asked, filed = [Messages(record, actor=\"user\").create(f\"hi {i}\") for i in range(2)][-1], Works(record, actor=AGENT).create(\"a job\") chat.send(record, Agents(record, actor=\"system\").by_session(\"claude-1\"), f\"Answered {asked.n}, parked {filed.n}, then work {filed.n}; the suite ({asked.n}) and \\\"finished {filed.n}\\\" pass\") lines = [n for n in nudges(record) if \"without saying what they are\" in n] assert len(lines) == 1 and f\"names {asked.n}, {filed.n} \" in lines[0], \"the bare numbers of real rows are named back, versions and counts are not\" chat.send(record, Agents(record, actor=\"system\").by_session(\"claude-1\"), f\"My reply to {asked.n} went through; parking {filed.n}, 2 revisions left, released 2.84.63\") lines = [n for n in nudges(record) if \"without saying what they are\" in n] assert f\"names {asked.n}, {filed.n} \" in lines[-1], \"any bare reference is named back, whatever word comes before it\" from features.messages.prose import bare assert bare(f\"Two steps:\\n{asked.n}. first\\n{filed.n}) second\") == [], \"the numbers of a numbered list are not row numbers\" assert bare(f\"down from 980 loose files to {asked.n}; it waited {filed.n} before\") == [], \"a small number with no handling verb before it is a count\" assert bare(\"a number under 250 is a count, and so is more than 300\") == [], \"a quantity word before a number makes it a count\" assert formatted(\"a journal question with options; journal question ask\", record, VIEWER) == \"a journal question with options; `journal question ask`\", \"only a real command is code\" shown = formatted(\"run python3 journal.py --root .journal upgrade, or pass --why\", record, VIEWER) assert shown.endswith(\" --root .journal upgrade, or pass `--why`\"), \"a flag of another program and a .journal path stay plain text\" long = \"see src/a.py and docs/b.md --flag \" * 4000 began = time.perf_counter() formatted(long, record, VIEWER) >       assert time.perf_counter() - began < 1.0, \"a long text with many paths and flags formats in linear time\" E       AssertionError: a long text with many paths and flags formats in linear time E       assert (783684.31610925 - 783683.197280416) < 1.0 E        +  where 783684.31610925 = <built-in function perf_counter>() E        +    where <built-in function perf_counter> = time.perf_counter src/features/messages/test.py:191: AssertionError =========================== short test summary info ============================ FAILED src/features/messages/test.py::test_a_row_named_by_a_bare_number_is_named_back_with_its_type 1 failed, 428 passed in 111.91s (0:01:51); check 27 failed - 1 failed, 428 passed in 111.91s (0 -01 -51) \u2014 journal check show 27 says why; fix it, then journal check run 27", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "rule 59 \u2014 Every viewer heading and label says plainly what it is about \u2014 The user, message 16499 (2026-10-06), after 'Where the words count' in the trigger editor: 'the stupid ass titles like where the words count, it doesnt say anything, and im not sure why this keeps happening'. Earlier the same in messages 16380 and 16484 ('Answer with it'). A heading names what the user is choosing or reading in the words a newcomer uses ('Watch for the words in', 'Tell the user in the chat'), never a phrase to decode; a label on a button says what happens when pressed. Test: would someone who never saw the feature know what the heading is about? Applies to designers' prototypes, helpers' builds and shipped sequence and trigger titles alike.; 1 new message 16624 - answer by opening your turn with [!reply:16624]", "meta": {"from": "journal"}}
{"content": "1 new message 16626 - answer by opening your turn with [!reply:16626]", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat; hook POST /api/hook/claude is slower than its budget \u2014 181ms last (106ms of it working, 10ms collecting garbage), against a budget of 50ms. Seen 2989 times.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 22c5dfae0 closed to-do 2777 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 2623ms last (1019ms of it working, 15ms collecting garbage), against a budget of 50ms. Seen 93 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 22c5dfae A reopened row never starts a plan that is not\u2026 \u2014 check 27 passed and 22c5dfae A reopened row never starts a plan that is not running, so a plan waits for the user's go; the formatter's speed test counts its own CPU time is committed; then ran boot guard: installs, serves and launches claude, codex in 13.0s", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "todo 2875 next", "meta": {"from": "journal"}}
{"content": "fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "1 new message 16632 - answer by opening your turn with [!reply:16632]; message 16627 updated", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; message 16633 file Screenshot 2026-10-06 at 09.23.25.png needs tags \u2014 inspect the attachment, then journal message tag 16633 \"Screenshot 2026-10-06 at 09.23.25.png\" \"<a few words describing what it shows>\"; 1 new message 16633 - answer by opening your turn with [!reply:16633]; message 16633 updated", "meta": {"from": "journal"}}
{"content": "request POST /api/run (message reply) is slower than its budget \u2014 140ms last (112ms of it working), against a budget of 50ms. Seen 3 times.", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2117 stands still while todo 2880 is ready \u2014 if work 2117 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2880. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "the user approved plan 27, Faster boot, commands and hooks - start it \u2014 journal plan start 27 makes it active and parks a plan that runs; then work its first phase's rows in order; plan 27 updated", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 337ccae9b closed to-do 2912 \u2014 The rows and the work are done; take the next one.; hook POST /api/hook/claude is slower than its budget \u2014 96ms last (54ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 3007 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 337ccae9 The chat shows a message's buttons without the Go\u2026 \u2014 check 27 passed and 337ccae9 The chat shows a message's buttons without the Go to the choice band, which stays on documents and reports is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 16.0s", "meta": {"from": "journal"}}
{"content": "request POST /api/main/message is slower than its budget \u2014 346ms last (84ms of it working), against a budget of 50ms. Seen 7 times.; 1 new message 16641 - answer by opening your turn with [!reply:16641]", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/codex is slower than its budget \u2014 99ms last (60ms of it working), against a budget of 50ms. Seen 7 times.", "meta": {"from": "journal"}}
{"content": "rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 74ms last (58ms of it working), against a budget of 50ms. Seen 98 times.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2118 stands still while todo 2893 is ready \u2014 if work 2118 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2893. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 7 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 7 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; 1 new message 16652 - answer by opening your turn with [!reply:16652]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 136ms last (92ms of it working, 13ms collecting garbage), against a budget of 50ms. Seen 6 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 103ms last (51ms of it working), against a budget of 50ms. Seen 8 times.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 5b731f9c3 closed to-do 2892, to-do 2913 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 138ms last (80ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 3040 times.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 5b731f9c A request's budget counts the time to its answer\u2026 \u2014 check 27 passed and 5b731f9c A request's budget counts the time to its answer, and the work after the answer is reported beside it; the plan bar says Approve is committed; then ran boot guard: installs, serves and launches claude, codex in 12.7s", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.", "meta": {"from": "journal"}}
{"content": "sequence 10, Writing a report, step 1 of 6 - Add the report\u2019s section headings \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:80. Lead with the answer in the report's brief, then put every part you plan on the report before writing any of them: journal report section 80 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:80.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"nothing waits\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead; sequence 10, Writing a report, step 2 of 6 - Write the report\u2019s sections \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:80. Write the parts one at a time and in order with journal report section 80 \"<part>\" \"<body>\"; the user sees each one appear where you are. Then journal sequence next 10 --about report:80.; sequence 10, Writing a report, step 3 of 6 - Add the document or report to a\u2026 \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:80. If a collection the user keeps fits what you wrote, add it: journal collection add <collection n> report:80. Look with journal collection all first; skip this when none fits, and never make a collection just for it. Then journal sequence next 10 --about report:80.; sequence 10, Writing a report, step 4 of 6 - Link the items behind the\u2026 \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:80. Link the rows it answers or was built on, such as the to-dos, plans, documents, reports or messages it is about, with journal report link 80 \"<row>\" for each. Leave out rows it only mentions in passing. Then journal sequence next 10 --about report:80.; sequence 10, Writing a report, step 5 of 6 - Offer the user a next step \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:80. If it asks the user to decide or approve something, give it buttons: journal report update 80 --set buttons='[{\"label\": \"Accept this proposal\", \"say\": \"I accept this proposal\", \"choice\": \"answer\"}, {\"label\": \"Change it first\", \"say\": \"I want changes first\", \"choice\": \"answer\"}]'. A button with say sends those words to you as the user's message; one naming a type, n and action runs that command. Buttons of one decision share a choice, so the others go once one is pressed. Skip this when nothing waits on the user. Then journal sequence next 10 --about report:80.; sequence 10, Writing a report, step 6 of 6 - Tell the user in the chat \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 10 --about report:80. Say in one or two plain lines what it concludes, then its reference on a line of its own, like doc 41 or report 98, never in backticks. Finish with journal sequence next 10 --about report:80.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "check 27 passed and 0ff2c574 journal speed times the server's start to its\u2026 \u2014 check 27 passed and 0ff2c574 journal speed times the server's start to its first answer and keeps its transcript cache in the scratch copy; built output and worktrees are never judged is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 22.3s", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 0ff2c574c closed to-do 2893 and ended work 2119 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.; rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.; rule 47 \u2014 The journal sets itself up once, when the server starts, never per\u2026 \u2014 Messages 2220 and 2224. Discovering features and their handlers, seating the feature rows and the rename sweep happen once, at server boot, and again only when a feature is switched on or off, a plugin changes or an environment is added: features.load keeps a set-up generation per journal (SEATED) and redoes the work only when that generation moves. A command, a request or a hook uses what is already there; nothing in their path may rediscover handlers or rescan folders. A cost that repeats per call is a bug to fix, not a budget to raise.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 the files changed since the last check (`__init__.py`, `ren\u2026 \u2014 Code Commandments \u2014 the files changed since the last check (`__init__.py`, `renames.py`) breaks a rule. Fix it now, at its SOURCE, while the code is still in front of you: \u00b7 \u2022 python-dict-bag at /Users/jessegall/projects/agent-journal/src/features/renames.py:32 \u00b7 LOAD the skill `commandments-python-value-objects` before fixing \u2014 load it even if you believe you already have. \u00b7 Run `commandments info <sin>` if a rule is not one you recognise. This check reads a file at a time, so it is not the whole picture \u2014 `judge` still is.", "meta": {"from": "journal"}}
{"content": "commandments-python-flow, journal-messages changed since you loaded them \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "your message 16670 names 634 without saying what they are \u2014 put the type before each number, like message 1712 or to-do 644, so the chat links it: journal message edit 16670 \"<the text>\"; the log tag does this in one step \u2014 [!log:N] makes the turn itself the log entry; it runs only when it opens the last text of your turn; fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2120 stands still while todo 2895 is ready \u2014 if work 2120 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2895. Stop only when nothing ready is left.; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "helper 84, Linus Keepwright, reported in message 16675 \u2014 read it, then journal helper finish 84 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit f178e8806 closed to-do 2894, to-do 2895 and ended work 2120 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and f178e880 A process seats feature rows only when the\u2026 \u2014 check 27 passed and f178e880 A process seats feature rows only when the features or environments changed, and old feature names are renamed in one pass over each file is committed; then ran boot guard: installs, serves and launches claude, codex in 10.8s", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 679ms last (411ms of it working, 6ms collecting garbage), against a budget of 50ms. Seen 100 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16679 - answer by opening your turn with [!reply:16679]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 1067ms last (91ms of it working, 3ms collecting garbage), against a budget of 50ms. Seen 7 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (work log) is slower than its budget \u2014 601ms last (268ms of it working, 8ms collecting garbage, then 26ms more after it answered), against a budget of 50ms. Seen 11 times.", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; todo 2898 next; commit 442eace8f closed to-do 2896, to-do 2914 and ended work 2121 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 442eace8 The server answers while it warms, a failed\u2026 \u2014 check 27 passed and 442eace8 The server answers while it warms, a failed warm-up still rolls a broken build back, a restart no longer drops hooks, and the agent makes a collection when related rows sit in none is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 25.3s", "meta": {"from": "journal"}}
{"content": "commandments-python-flow, journal-collections, journal-messages changed since\u2026 \u2014 load one again when you next need it; only the every-start skills are held for", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "phone 12 completed; 1 new phone 13", "meta": {"from": "journal"}}
{"content": "phone 13 completed; 1 new phone 14", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.; phones 14, 15 completed; 2 new phones 15, 16", "meta": {"from": "journal"}}
{"content": "phone 16 completed; 1 new phone 17", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.; rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "1 new message 16697 - answer by opening your turn with [!reply:16697]", "meta": {"from": "journal"}}
{"content": "message 16697 file Screenshot 2026-10-06 at 09.48.05.png needs tags \u2014 inspect the attachment, then journal message tag 16697 \"Screenshot 2026-10-06 at 09.48.05.png\" \"<a few words describing what it shows>\"; message 16697 file Screenshot 2026-10-06 at 09.48.28.png needs tags \u2014 inspect the attachment, then journal message tag 16697 \"Screenshot 2026-10-06 at 09.48.28.png\" \"<a few words describing what it shows>\"; message 16697 updated", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "helper 84, Linus Keepwright, reported in message 16699 \u2014 read it, then journal helper finish 84 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "message 16700 file Screenshot 2026-10-06 at 09.49.40.png needs tags \u2014 inspect the attachment, then journal message tag 16700 \"Screenshot 2026-10-06 at 09.49.40.png\" \"<a few words describing what it shows>\"; 1 new message 16700 - answer by opening your turn with [!reply:16700]; message 16700 updated", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 56 \u2014 Helpers are for work that writes; subagents read, research and design \u2014 The user, message 13464: there must be a clear distinction. A subagent can be dispatched for anything read-only: research, review, design. A helper is for actual work that writes, best in its own worktree when the work is separate. Dieter designing in Claude Design should have been a subagent, not a helper.", "meta": {"from": "journal"}}
{"content": "fact 28 \u2014 The designer agent type exists, so design work goes to a subagent \u2014 Since 2026-10-01 .claude/agents/designer.md (Dieter, Opus, Claude Design tools, read-only on the repository) is an agent type; rule 56 says design is a subagent's job, never a helper's.; rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "1 new message 16703 - answer by opening your turn with [!reply:16703]", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).; law L5 \u2014 Every subagent dispatch names the agent - a human name, a little\u2026 \u2014 A name is how the user and the chat tell subagents apart and how they are messaged later; an id or a task line is not a name. Start the dispatch's description with the name, a colon, then the task, such as \"Dr. Einstein: profile the slow hooks\" or \"Coco Rams: draw the plan card\". A designer can borrow from famous designers, a researcher from famous scientists, mixed up for fun.; rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.; question 202 completed", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.; rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "1 new message 16707 - answer by opening your turn with [!reply:16707]", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 88ms last (65ms of it working), against a budget of 50ms. Seen 103 times.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 2bd94e767 closed to-do 2880 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 2bd94e76 The viewer is built for the update guard - a held\u2026 \u2014 check 27 passed and 2bd94e76 The viewer is built for the update guard: a held update names the files changed by hand and offers Keep my changes or Update anyway is committed; then ran boot guard: installs, serves and launches claude, codex in 12.5s", "meta": {"from": "journal"}}
{"content": "your command ran 30s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2123 stands still while todo 2875 is ready \u2014 if work 2123 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2875. Stop only when nothing ready is left.; Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Reading it\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead; work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "phone 17 completed; 1 new phone 18", "meta": {"from": "journal"}}
{"content": "phones 18, 19 completed; 2 new phones 19, 20", "meta": {"from": "journal"}}
{"content": "phone 20 completed; 1 new phone 21", "meta": {"from": "journal"}}
{"content": "phones 21, 22, 23, 24 completed; 4 new phones 22, 23, 24, 25", "meta": {"from": "journal"}}
{"content": "the phone's address did not answer 3 times, so its tunnel was restarted", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "phones 25, 26, 27, 28, 29, 30, 31 completed; 7 new phones 26, 27, 28, 29, 30, 31, 32", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat; phones 32, 33, 34 completed; 3 new phones 33, 34, 35", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 181ms last (54ms of it working, then 1ms more after it answered), against a budget of 50ms. Seen 10 times.; request POST /api/main/message is slower than its budget \u2014 209ms last (56ms of it working, then 31ms more after it answered), against a budget of 50ms. Seen 8 times.; 1 new message 16718 - answer by opening your turn with [!reply:16718]", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit c3b721143 closed to-do 2899 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and c3b72114 The fold cache keeps each code mark in its own\u2026 \u2014 check 27 passed and c3b72114 The fold cache keeps each code mark in its own folder so tidy prunes old ones whole, and a transcript's turns are written behind the asking thread is committed; then ran boot guard: installs, serves and launches claude, codex in 8.9s", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.; the viewer threw signal timed out \u2014 signal timed out / TimeoutError: signal timed out Seen 1 time.", "meta": {"from": "journal"}}
{"content": "message 16723 file Screenshot 2026-10-06 at 09.57.56.png needs tags \u2014 inspect the attachment, then journal message tag 16723 \"Screenshot 2026-10-06 at 09.57.56.png\" \"<a few words describing what it shows>\"; 1 new message 16723 - answer by opening your turn with [!reply:16723]; message 16723 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16724 - answer by opening your turn with [!reply:16724]", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 259ms last (97ms of it working, 1ms collecting garbage, then 7ms more after it answered), against a budget of 50ms. Seen 8 times.; request GET /api/main/dashboard is slower than its budget \u2014 119ms last (52ms of it working), against a budget of 50ms. Seen 104 times.", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "message 16725 file image.png needs tags \u2014 inspect the attachment, then journal message tag 16725 \"image.png\" \"<a few words describing what it shows>\"; 1 new message 16725 - answer by opening your turn with [!reply:16725]; message 16725 updated", "meta": {"from": "journal"}}
{"content": "1 new message 16728 - answer by opening your turn with [!reply:16728]", "meta": {"from": "journal"}}
{"content": "your command ran 31s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 139ms last (74ms of it working), against a budget of 50ms. Seen 9 times.; fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.; command message reply is slower than its budget \u2014 575ms last (73ms of it working), against a budget of 50ms. Seen 8 times.; request POST /api/run (message reply) is slower than its budget \u2014 577ms last (75ms of it working, 1ms waiting on locks, then 120ms more after it answered), against a budget of 50ms. Seen 4 times.; law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.; 1 new message 16729 - answer by opening your turn with [!reply:16729]", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "1 new message 16732 - answer by opening your turn with [!reply:16732]", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; rule 43 \u2014 A request or hook over its budget is fixed before the next release \u2014 Comment 1151 on this rule. When the faults feature reports a request, a hook or a command slower than its budget, file it as a to-do at once. It does not jump ahead of the work in hand, but no version is published while one is still open: profile it, fix it, and verify the new time before the release goes out. The budget is 50ms, because everything runs locally against files.", "meta": {"from": "journal"}}
{"content": "rule 59 \u2014 Every viewer heading and label says plainly what it is about \u2014 The user, message 16499 (2026-10-06), after 'Where the words count' in the trigger editor: 'the stupid ass titles like where the words count, it doesnt say anything, and im not sure why this keeps happening'. Earlier the same in messages 16380 and 16484 ('Answer with it'). A heading names what the user is choosing or reading in the words a newcomer uses ('Watch for the words in', 'Tell the user in the chat'), never a phrase to decode; a label on a button says what happens when pressed. Test: would someone who never saw the feature know what the heading is about? Applies to designers' prototypes, helpers' builds and shipped sequence and trigger titles alike.", "meta": {"from": "journal"}}
{"content": "1 new message 16735 - answer by opening your turn with [!reply:16735]", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "1 new message 16737 - answer by opening your turn with [!reply:16737]", "meta": {"from": "journal"}}
{"content": "message 16737 updated", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.", "meta": {"from": "journal"}}
{"content": "commit 78ac76afc closed to-do 2900, to-do 2901, to-do 2902 and ended work 2125 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 78ac76af The phone dialog asks for one code per opening\u2026 \u2014 check 27 passed and 78ac76af The phone dialog asks for one code per opening, settings that cannot be turned off sit with their subject, a pack locks one day at a time, the hourly tidy runs once across processes, and a full cache drops its oldest entry is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 22.4s", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "rule 45 \u2014 No prose words as names in code - said, says, heard, spoke, told\u2026 \u2014 Messages 1360 and 1698. The user has said more than once that code must not read like prose: a variable, attribute, property or function is named for what it holds or does (text, command, labels, lines), never with a verb from a story. 'says' on the Design type (1360) and 'said = call.said.lower()' in features/recital.py (1698) are the examples. Rule 27 states the naming rule; this one carries the words, so writing one of them whispers it. Before writing a name, ask whether a reader who has never seen the code would know what it holds.", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "the reply tag does this in one step \u2014 [!reply:N] makes the turn itself the reply; it runs only when it opens the last text of your turn", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 3d3d8b4c2 closed to-do 2918 and ended work 2126 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 3d3d8b4c Each shipped profile meets a meme, a joke or\u2026 \u2014 check 27 passed and 3d3d8b4c Each shipped profile meets a meme, a joke or sharp words in its own voice, then puts the matter right is committed; then ran boot guard: installs, serves and launches claude, codex in 14.7s", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself; work 2127 in hand \u2014 A Code Commandments detector of this project catches\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 2 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 2 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; auto mode is on and work 2127 stands still while todo 2875 is ready \u2014 if work 2127 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2875. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "waiting: 2 unread messages 16693, 16716; 1 unread worktree 63; 1 unread phone 35", "meta": {"from": "journal"}}
{"content": "your chat talked about the journal's workings - \"Still waiting\" \u2014 the user sees replies, reactions, pills and reads themselves; say what the work is instead", "meta": {"from": "journal"}}
{"content": "answer message 16693, message 16716 before you write anything \u2014 answer each by opening a turn with [!reply:<n>]. a reply, a reaction, or journal message processed <n>", "meta": {"from": "journal"}}
{"content": "answer message 16693, message 16716 before you write anything \u2014 answer each by opening a turn with [!reply:<n>]. a reply, a reaction, or journal message processed <n>", "meta": {"from": "journal"}}
{"content": "helper 85, Paula Pagewright, reported in message 16752 \u2014 read it, then journal helper finish 85 once its work is taken or dropped", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.", "meta": {"from": "journal"}}
{"content": "check 27 passed and f962c861 A service that is not needed stops even when it\u2026 \u2014 check 27 passed and f962c861 A service that is not needed stops even when it is already running is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 37.0s", "meta": {"from": "journal"}}
{"content": "The detector's first query over the viewer, and the judge on the services\u2026", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 35s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2127 stands still while todo 2905 is ready \u2014 if work 2127 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2905. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 4 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 4 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "The rule's re-run over the fixed viewer and its skill's publishing came back\u2026", "meta": {"from": "journal"}}
{"content": "rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "phone 35 completed; 1 new phone 36", "meta": {"from": "journal"}}
{"content": "1 new message 16768 - answer by opening your turn with [!reply:16768]", "meta": {"from": "journal"}}
{"content": "your command ran 35s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2127 stands still while todo 2905 is ready \u2014 if work 2127 waits on the user, decide it yourself when you can; otherwise put the question on its row with journal todo ask, end or park the work, and start todo 2905. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 707a82915 closed to-do 2906 and ended work 2127 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 707a8291 A Code Commandments rule of this project catches\u2026 \u2014 check 27 passed and 707a8291 A Code Commandments rule of this project catches viewer text that speaks as the app or uses an internal word, and the four labels it found are plain is committed; then ran boot guard: installs, serves and launches claude, codex in 14.3s; rule 46 \u2014 Only commit and push once the whole journal is proven to boot \u2014 Messages 2178 and 2179. A release that has not been started for real can crash every project that installs it, as 2.78.3 did for Codex and 2.84.0 did to project records. Before every commit and push: the full suite passes, including tests/test_it_boots.py, which installs a packed copy into a fresh project, launches Claude and Codex from it, writes a project record, upgrades again and checks the record survives. When a change touches launching, installing or upgrading, also start a real journal in a scratch project and close it properly afterwards, leaving no process behind.; rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "rule 58 \u2014 The user tests a design's clickable prototype and approves it before\u2026 \u2014 Message 15725 (2026-10-05): 'ask Dieter to create an interactive prototype! I want to test it first and give feedback before giving it my go', and remove any fact or rule that conflicts. Replaces rule 53's 'the designer decides'. The designer still runs one critique round (messages 12800, 13475) and revises before showing the prototype; then the user clicks through it, gives feedback, and only the user's go starts the build.", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 8d0c65fd The wording rule judges the feature labels and\u2026 \u2014 check 27 passed and 8d0c65fd The wording rule judges the feature labels and explanations in Settings too, and the setting about other tools' hooks says it plainly is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 17.1s", "meta": {"from": "journal"}}
{"content": "fact 18 \u2014 cProfile inflates the slow-request profiles about tenfold \u2014 The faults feature writes a profile when a request passes its budget, and the profile is taken with cProfile, which adds per-call overhead. On 2026-09-22 /api/summary profiled at 58ms with 48ms inside Resource.fork's deep copy; with the profiler off the same call ran in 2 to 7ms. Read the profile for where the time goes in relative terms, then time the call with curl before changing anything.; fact 26 \u2014 Claude Code reads agent profiles when a session starts \u2014 Seen 2026-09-26: after the board-filler's profile in .claude/agents gained its steps and the Grep rule, dispatches from the running session still used the old profile (a 3.5-minute first question, shell grep refused); after the session restarted, the same request took 21 seconds with 4 calls. A change to an agent type reaches only sessions started after it is written.", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "1 new message 16778 - answer by opening your turn with [!reply:16778]", "meta": {"from": "journal"}}
{"content": "rule 49 \u2014 A dialog whose content grows keeps one fixed height, and its content\u2026 \u2014 Message 5361, after asking more than once: a dialog that shows output as it arrives (install, update, logs) opens at its final height and never jumps; only its content scrolls.", "meta": {"from": "journal"}}
{"content": "1 new message 16781 - answer by opening your turn with [!reply:16781]", "meta": {"from": "journal"}}
{"content": "law L3 \u2014 Read narrowly - grep for the line, sed a range, head the file; never\u2026 \u2014 Everything a tool returns stays in the context for good and is paid for on every turn after it. Search before you read, read the range you need, and cap output with grep, head or tail. Read a whole file only when you need all of it.", "meta": {"from": "journal"}}
{"content": "rule 38 \u2014 Never change the git branch until the user says so, by name \u2014 The work happens on the branch the user named. That was main until message 5929 and question 80 (2026-09-23), which moved the sins work to the branch sins. Do not create, switch to or merge any other branch unless the user names it in their own words.; rule 57 \u2014 Never merge the overnight refactor into main before its pull request\u2026 \u2014 Messages 15005, 15006, 15109, 15110 (2026-10-04): all refactor work goes on branch overnight-refactor and reaches the user as one pull request, which they read in the morning; nothing of it is merged into main until they say so. Hotfixes the user explicitly asks for go to main at once and are merged into the branch.", "meta": {"from": "journal"}}
{"content": "1 new message 16782 - answer by opening your turn with [!reply:16782]", "meta": {"from": "journal"}}
{"content": "check 27 passed and 52bb5bf6 The viewer fetches the newest messages first and\u2026 \u2014 check 27 passed and 52bb5bf6 The viewer fetches the newest messages first and marks itself ready, then loads the rest behind it is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 45.5s", "meta": {"from": "journal"}}
{"content": "1 new message 16784 - answer by opening your turn with [!reply:16784]", "meta": {"from": "journal"}}
{"content": "your command ran 33s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 34s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 6 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 6 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "1 new message 16790 - answer by opening your turn with [!reply:16790]", "meta": {"from": "journal"}}
{"content": "rule 39 \u2014 Use only registered exclamation response tags \u2014 A tag like [!reply:12] runs a command, and only the tags in the tags.runs setting are registered. An invented tag does nothing and shows as raw text in the chat. Use the registered ones (reply, log, end, todo, fact, rule) and nothing else.", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "fact 24 \u2014 An answer followed by tool calls can be missing from Claude's\u2026 \u2014 Seen 2026-09-24 for messages 9391-9404: text blocks opening with [!reply:n] that were followed by tool calls never appeared in the session's jsonl (only thinking and tool_use rows did), so the journal never saw them and the replies were lost. When a turn goes on after answering, send the answer with journal message reply <n> \"<text>\" instead of the tag.", "meta": {"from": "journal"}}
{"content": "waiting: 1 unread phone 36", "meta": {"from": "journal"}}
{"content": "check 27 passed and fae3ac28 The phone and sharing dropdowns say in plain\u2026 \u2014 check 27 passed and fae3ac28 The phone and sharing dropdowns say in plain words why the tunnel is not connected, with a button to the sharing settings is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 37.0s; work 2113, The test suite runs in seconds again, is still parked and 1 more\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit fae3ac28b closed to-do 2916 and ended work 2129 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "rule 48 \u2014 The viewer is built from its component library, and pages only\u2026 \u2014 Message 4258. Every visual piece the viewer shows more than once, or that a user would recognise as the same kind of thing (a dialog, a side panel or inspector, a dropdown, a list row, a switch, a button), is one component in web/src/kit, extracted aggressively, and every page composes those components instead of building its own copy. Before writing markup or styles in a page, look for the kit component that already does it and extend it with a prop; a second hand-built version is a bug. The side panel that animated in but not out, while a separate skill panel did both, is the example.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "rule 40 \u2014 A feature is named for what it is, never for its machinery \u2014 Messages 599, 600 and 703. A feature is a capability the user would name and would think of switching off. File tracking, a write gate, a phrase bank, a tree diff are services used inside a feature, not features of their own: they live in the feature they serve. Before adding a directory under features/, say what the user would call it; if the answer names a mechanism, it belongs inside something else. Report 16 holds the grouping this implies.", "meta": {"from": "journal"}}
{"content": "check 27 passed and c02a7210 The phone dialog checks the tunnel again while a\u2026 \u2014 check 27 passed and c02a7210 The phone dialog checks the tunnel again while a code waits, so a refused tunnel shows its reason is committed; then ran boot guard: installs, serves and launches claude, codex in 12.8s", "meta": {"from": "journal"}}
{"content": "request GET /api/main/agent is slower than its budget \u2014 96ms last (51ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 19 times.", "meta": {"from": "journal"}}
{"content": "sequence 27, Checking the instruction files, step 1 of 3 - Read the\u2026 \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 27. Read AGENTS.md and CLAUDE.md at the project root and the journal's block at the head of each, narrowly: grep for the headings, then sed the sections you need. Note every place where two of them tell an agent opposite things, with the file and line on both sides. Then journal sequence next 27 --about <ref>.; sequence 27, Checking the instruction files, is still at step 1 of 3 - carry\u2026 \u2014 finishing it comes before anything else; do the step now, Read the instruction files: Read AGENTS.md and CLAUDE.md at the project root and the journal's block at the head of each, narrowly: grep for the headings, then sed the sections you need. Note every place where two of them tell an agent opposite things, with the file and line on both sides. Then journal sequence next 27 --about <ref>.; message 16527 file Screenshot 2026-10-06 at 08.34.57.png needs tags \u2014 inspect the attachment, then journal message tag 16527 \"Screenshot 2026-10-06 at 08.34.57.png\" \"<a few words describing what it shows>\"", "meta": {"from": "journal"}}
{"content": "fact 23 \u2014 Every upgrade brings system sequences and their triggers in line\u2026 \u2014 install.py runs ship_sequences after the migrations on each upgrade, so features/sequences/shipped.py is the whole source: change its wording and the next upgrade updates every journal, no migration needed. Shipped rows carry system=True and are read-only for everyone but SYSTEM (controllers/base.py _shipped).", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 604ms last (52ms of it working), against a budget of 50ms. Seen 107 times.", "meta": {"from": "journal"}}
{"content": "sequence 27, Checking the instruction files, step 2 of 3 - Report any\u2026 \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 27. With nothing found, say so in one plain line and move on. Otherwise journal report create \"Contradictions in the instruction files\" --brief \"<each one: both sides with file and line, which should win and why>\". Then journal sequence next 27 --about <ref>.; fact 30 \u2014 The tunler server refuses TLS for any subdomain without a tunnel \u2014 Seen 2026-10-04 in the server's docker logs (ssh root@tunler.jessegall.nl, container tunler): 'TLS handshake error ... host \"journal-probe.tunler.jessegall.nl\" not allowed'. A made-up subdomain never answers even when the server is healthy; probe https://tunler.jessegall.nl/ for the server itself. Root SSH to the server works.", "meta": {"from": "journal"}}
{"content": "sequence 27, Checking the instruction files, step 3 of 3 - Suggest a fix for\u2026 \u2014 Finishing this sequence comes before anything else you do; everything else waits until it is finished or abandoned. Take it up first with journal sequence follow 27. File each fix as a suggestion whose brief holds the exact change, as a diff: journal suggestion suggest \"<the change>\" --brief \"<why, and the diff>\". Never edit the files yourself; the user accepts a suggestion first, and the journal's block is only ever written by the journal. When an accepted fix comes back as a to-do, apply it only where the lines still read as the diff shows; if they changed since, read the files again and propose the fix anew. Finish with journal sequence next 27 --about <ref>.", "meta": {"from": "journal"}}
{"content": "1 new message 16801 - answer by opening your turn with [!reply:16801]", "meta": {"from": "journal"}}
{"content": "fact 9 \u2014 Every public method on a controller becomes a journal command \u2014 The CLI is generated from the controllers: each public method of Controller, or of a typed controller, turns into journal <noun> <method>. A helper added to the base class therefore becomes a command on every type \u2014 which is how journal <type> handled and journal <type> refuse came to exist, from the CRUD funnel and the refusal funnel. An internal helper on a controller is named with a leading underscore, as _shaped and _status already are, or it ships as a command nobody meant.", "meta": {"from": "journal"}}
{"content": "rule 42 \u2014 Every user-facing text passes the formatters before it leaves the\u2026 \u2014 Not only a brief. A title, an abstract, an outcome and every section body are read by a person, so each goes through the same formatters on its way to the viewer \u2014 chat turns, activity items, to-do rows, inspector pages, docs alike. One field formatted out of five is not a rule, it is an accident, and it is how a raw tag ended up in the activity list after the tags feature had been stripping them for weeks. When a new field carries words a person reads, it joins the list in the same place.; rule 35 \u2014 Write clean code - one funnel per kind of operation, never the same\u2026 \u2014 Every kind of operation has one funnel: one method that creates, one that saves, one that refuses, one that formats. A second method that does the same thing under another name splits the behaviour, and the two drift apart. Before writing a method, search for the one that already does it and extend that. scripts/checks/funnels.py finds bodies written twice.; rule 54 \u2014 Settings and feature switches are read at boot and on change, never\u2026 \u2014 The user, message 13349: the application boots, determines every feature and setting once, and re-evaluates only when something changes, such as a setting or a plugin. Never lazy-load settings.", "meta": {"from": "journal"}}
{"content": "the todo tag does this in one step \u2014 [!todo=\"the title\"] files it with the turn as its brief; it runs only when it opens the last text of your turn; fact 13 \u2014 This live session runs the installed copy in .journal/journal.pyz \u2014 The running journal (server, hooks, CLI) runs from .journal/journal.pyz with its viewer and skills in .journal/src, never from the repo. A change in the repo reaches it only through python3 src/journal.py --root .journal upgrade, which packs the zip again. A commit alone changes nothing that is running.; rule 27 \u2014 Name a declaration with the word a reader already knows \u2014 An attribute, a variable or a field gets the ordinary programming word for what it holds, not an evocative one. was, heard and alone were poetry; aliases, notify_actions and urgent_actions are what they are. The test: could a reader who has never seen this codebase guess what it holds from the name alone? Prose belongs in the help text and the abstract, where it is read as prose. This does not license abbreviations \u2014 a plain word in full, not a short one.", "meta": {"from": "journal"}}
{"content": "chat etiquette - a line from the journal is an instruction, not a message\u2026 \u2014 a turn that only handles a journal line needs no words: act on it, or say once in the chat what you wait on, then carry on; what the user needs to know still goes to the chat", "meta": {"from": "journal"}}
{"content": "rule 37 \u2014 Close every to-do explicitly with todo done or a Journal commit\u2026 \u2014 Ending work does not close its row. A to-do is closed by journal todo done <n> --how, or by a commit whose message carries Journal: todos done <n> at column 0, several numbers separated by commas. A row left open after its work landed misleads the next session and auto mode.", "meta": {"from": "journal"}}
{"content": "rule 52 \u2014 A chat mark for something the user did sits on the user's side \u2014 Message 10960 (2026-09-25): marks for the user's own actions, such as answering a question, are right-aligned like the user's messages. A mark is put there by giving its card side=user.", "meta": {"from": "journal"}}
{"content": "your last message ran its paragraphs together \u2014 a blank line between parts is what makes a message readable: one thought to a paragraph", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 3 judged files since th\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 3 judged files since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.", "meta": {"from": "journal"}}
{"content": "rule 36 \u2014 Clean, DRY, idiomatic before it is committed, never after it is\u2026 \u2014 The user should never be the one who finds duplication, dead code, a clumsy name or a pattern the codebase does not use. Read the diff before every commit as a reviewer would, and fix what is not clean then, not in a follow-up after a complaint.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 21a7ac45 The tunler server address is asked for once - the\u2026 \u2014 check 27 passed and 21a7ac45 The tunler server address is asked for once: the Connect form keeps it, and the settings tab is named Phone and share links is committed; then ran boot guard: installs, serves and launches claude, codex in 13.2s", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked and 1 more\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 21a7ac456 closed to-do 2917 and ended work 2130 \u2014 The rows and the work are done; take the next one.; request GET /api/main/agent is slower than its budget \u2014 208ms last (58ms of it working, 6ms collecting garbage, then 2ms more after it answered), against a budget of 50ms. Seen 24 times.; fact 25 \u2014 A designer's install packs the whole tree, half-done server edits\u2026 \u2014 2026-09-25: Eames and Saul run python3 src/journal.py --root .journal upgrade after their viewer builds; it packs every file in src, so a server handler I was halfway through writing went live and raised on every PostToolUse hook. While designers work in parallel, keep server edits whole between tool calls (write and test in the scratchpad first), and reinstall after reverting anything.; fact 27 \u2014 Running src/journal.py against the live .journal root starts a\u2026 \u2014 Seen 2026-10-01: with VERSION bumped in the tree, src/journal.py --root .journal saw the live server as another build and started a new one on a new port (8431, 8432), and the user's tabs kept losing connection. Run source builds against a throwaway or demo root (~/projects/demo-crumb/.journal), never the live one, until the release is installed.", "meta": {"from": "journal"}}
{"content": "your command ran 32s in the foreground and was moved to the background \u2014 carry on with other work; you are told when it ends. Start a command you expect to take long in the background yourself", "meta": {"from": "journal"}}
{"content": "the await tag on did not run \u2014 ! a plan is active: open work for a row of its current phase with journal todo start <n>, or --force \"<why>\" - add what is missing to the tag itself", "meta": {"from": "journal"}}
{"content": "Code Commandments \u2014 before you wrap up \u2014 you've changed 1 judged file since the\u2026 \u2014 Code Commandments \u2014 before you wrap up: you've changed 1 judged file since the last commit. Consider running `commandments judge --changes` to confirm they conform, and fix any sin at its SOURCE (don't launder a finding with a default/cast/null-check). This is a one-time nudge for this batch \u2014 if you've already judged, or these changes aren't worth a scan, just say so and carry on.; todo 2875 next", "meta": {"from": "journal"}}
{"content": "the await tag on did not run \u2014 ! a plan is active: open work for a row of its current phase with journal todo start <n>, or --force \"<why>\" - add what is missing to the tag itself", "meta": {"from": "journal"}}
{"content": "1 new message 16806 - answer by opening your turn with [!reply:16806]", "meta": {"from": "journal"}}
{"content": "rule 55 \u2014 Always dispatch Codex helpers on gpt-6-sol \u2014 The user's word, message 13431: switch the codex agents to GPT-6-Sol and make it their default. ~/.codex/config.toml names it as the default model too.; rule 41 \u2014 Keep moving, run the whole suite before every commit, never wait \u2014 The full suite runs in about seven seconds: .venv/bin/python -m pytest -q --timeout=300 -n auto. Run it before every commit instead of picking tests by name. Group rows that sit in the same code into one sitting: write them all, test once, commit once. And never wait, not for a subagent, a build, or an answer you can carry on without. Dispatch it and keep working. If you truly are waiting on something, say so in the work log.", "meta": {"from": "journal"}}
{"content": "1 new message 16808 - answer by opening your turn with [!reply:16808]", "meta": {"from": "journal"}}
{"content": "fact 20 \u2014 A slim supervisor holds the agent and a worker reloads on every build \u2014 Since 2.118.0 (2026-09-23). src/supervisor.py is standard library only and never reloads: journal claude hands its process over to it (os.execv), and it owns the pty and the agent process, relays the terminal, writes the printed and screen captures, listens on the typist socket, restarts the agent in the same session from a relaunch command written to its runtime folder while a restart is pending, and stops it with escalation while draining the pty (an agent cannot finish exiting on macOS while its output is unread). It starts the worker (src/worker.py, which runs runner/worker.py; engine/worker.py stays as an alias for supervisors started before 2.201) and starts it again whenever it exits: RELOAD on a new build, RELAUNCH to restart the agent, STOP to end, HEAL or a quick crash to roll back a build through journal heal. The worker holds everything else: seating the session, the start-up confirm typed through the typist, services, viewer, update check, check-in, and the one-time relaunch of sessions launched before agents/terminal.py LAUNCH. agents/terminal.py holds only journal-side helpers. The server (serve.py) still runs the engines and re-execs itself on a .py change. When the agent exits, the supervisor runs journal ended, which puts set-aside hooks back and stops the server when no session is left.", "meta": {"from": "journal"}}
{"content": "message 16810 file Screenshot 2026-10-06 at 10.45.35.png needs tags \u2014 inspect the attachment, then journal message tag 16810 \"Screenshot 2026-10-06 at 10.45.35.png\" \"<a few words describing what it shows>\"; 1 new message 16810 - answer by opening your turn with [!reply:16810]; message 16810 updated", "meta": {"from": "journal"}}
{"content": "work 2113, The test suite runs in seconds again, is still parked - can you\u2026 \u2014 it was parked because: Plan 27 started; the suite is 109 s at load 30 with every core, and what is left is the machine's load, measured again in plan 27's baseline. journal work resume 2113 picks it up again.; commit 8065fab4c closed to-do 2921 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 8065fab4 A check tells the agent every 15 minutes about an\u2026 \u2014 check 27 passed and 8065fab4 A check tells the agent every 15 minutes about an open GitHub issue still waiting for an answer is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 20.4s", "meta": {"from": "journal"}}
{"content": "rule 51 \u2014 Every finished feature is committed, pushed and released with a new\u2026 \u2014 Message 9207 (2026-09-24): when a new feature is ready, commit, push and publish a new tag. This is the user's standing word for releasing, so rule 44's only-when-the-user-says is met by it for finished features; fixes in between wait for the next feature or a patch the user asks for.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 109ms last (73ms of it working), against a budget of 50ms. Seen 10 times.; hook POST /api/hook/claude is slower than its budget \u2014 114ms last (74ms of it working, 1ms waiting on locks, then 166ms more after it answered), against a budget of 50ms. Seen 3048 times.", "meta": {"from": "journal"}}
{"content": "request POST /api/run (share tunnel) is slower than its budget \u2014 507ms last (374ms of it working, 3ms collecting garbage, 2ms waiting on locks, then 191ms more after it answered), against a budget of 50ms. Seen 1 time.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 173ms last (92ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 10 times.", "meta": {"from": "journal"}}
{"content": "journal: the engine hit an error and kept going; the last of it is below and the whole of it is in .journal/runtime/engine.log. Fix it, then say so. resources.base.Refused: session 'c35e7375-7ce3-4bc3-b1ae-7cdf73bd1488' belongs to environment 'codex-hotfix'", "meta": {"from": "journal"}}
