{"content": "helper 85, Paula Pagewright, reported in message 16819 \u2014 read it, then journal helper finish 85 once its work is taken or dropped; work 2128 in hand \u2014 The chat's first load stops waiting on one call after\u2026 \u2014 if this is not what you are doing, end it or park it and start the work you are in; 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.; 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.; todo 2923 next; check 27 passed and ed0f2e9f Enter always sends a message typed into Codex, as\u2026 \u2014 check 27 passed and ed0f2e9f Enter always sends a message typed into Codex, as in 2.249.7 on main is committed; then ran boot guard: installs, serves and launches claude, codex in 11.7s; command environment stop is slower than its budget \u2014 65ms last (51ms of it working, 1ms collecting garbage), against a budget of 50ms. Seen 1 time.; 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.; 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.; request GET /api/main/agent is slower than its budget \u2014 407ms last (68ms of it working, 2ms collecting garbage, then 5ms more after it answered), against a budget of 50ms. Seen 26 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.; 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.; request GET /api/main/dashboard is slower than its budget \u2014 2132ms last (1028ms of it working, 17ms collecting garbage), against a budget of 50ms. Seen 108 times.; request POST /api/main/message is slower than its budget \u2014 172ms last (53ms of it working, then 32ms more after it answered), against a budget of 50ms. Seen 9 times.; message 16815 file Screenshot 2026-10-06 at 10.53.03.png needs tags \u2014 inspect the attachment, then journal message tag 16815 \"Screenshot 2026-10-06 at 10.53.03.png\" \"<a few words describing what it shows>\"; 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.; 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 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.; 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 GET /api/main/dashboard is slower than its budget \u2014 719ms last (472ms of it working, 7ms collecting garbage, then 1ms more after it answered), against a budget of 50ms. Seen 110 times.; request GET /api/main/agent is slower than its budget \u2014 170ms last (53ms of it working, 7ms collecting garbage), against a budget of 50ms. Seen 29 times.; 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.; request POST /api/main/message is slower than its budget \u2014 379ms last (69ms of it working, then 32ms more after it answered), against a budget of 50ms. Seen 10 times.; 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.; 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.; 6 new messages 16814, 16815, 16817, 16818, 16825, 16828 - answer each by opening a turn with [!reply:<n>]; message 16815 updated", "meta": {"from": "journal"}}
{"content": "message 16815 file Screenshot 2026-10-06 at 10.53.03.png needs tags \u2014 inspect the attachment, then journal message tag 16815 \"Screenshot 2026-10-06 at 10.53.03.png\" \"<a few words describing what it shows>\"", "meta": {"from": "journal"}}
{"content": "todo 2923 next", "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).; request GET /api/main/agent is slower than its budget \u2014 187ms last (78ms of it working, 1ms collecting garbage, then 1ms more after it answered), against a budget of 50ms. Seen 30 times.; 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 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.; 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 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": "1 new message 16836 - answer by opening your turn with [!reply:16836]", "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 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.; Is rule 60 a ruling for the whole project? \u2014 rule 60, \"A hotfix is done by a dispatched agent in a worktree of main\", 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 60 --how \"<why>\" and file it here as a fact or a reminder instead.", "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/dashboard is slower than its budget \u2014 328ms last (228ms of it working, 8ms collecting garbage), against a budget of 50ms. Seen 112 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.; 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 60 \u2014 A hotfix is done by a dispatched agent in a worktree of main \u2014 Message 16836 (2026-10-06): the orchestrator cut a worktree under .claude/worktrees for a Codex hotfix, its session moved to a new environment and the user's messages stopped reaching it. The user: when working on a branch and a hotfix comes in, create a worktree of main and dispatch an agent to do that work. The orchestrator stays on its branch and in its environment, and never cds into another checkout.; 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.; 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 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 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 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": "request GET /api/main/helper is slower than its budget \u2014 138ms last (58ms of it working), against a budget of 50ms. Seen 13 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 142ms last (76ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 12 times.", "meta": {"from": "journal"}}
{"content": "message 16839 file Screenshot 2026-10-06 at 11.17.10.png needs tags \u2014 inspect the attachment, then journal message tag 16839 \"Screenshot 2026-10-06 at 11.17.10.png\" \"<a few words describing what it shows>\"; 1 new message 16839 - answer by opening your turn with [!reply:16839]; message 16839 updated", "meta": {"from": "journal"}}
{"content": "helper 85, Paula Pagewright, reported in message 16841 \u2014 read it, then journal helper finish 85 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": "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": "command message reply is slower than its budget \u2014 141ms last (54ms of it working), against a budget of 50ms. Seen 9 times.; request POST /api/run (message reply) is slower than its budget \u2014 143ms last (55ms of it working, then 15ms more after it answered), against a budget of 50ms. Seen 5 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16844 - answer by opening your turn with [!reply:16844]", "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 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 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": "check 27 passed and 789fc180 The trigger editor's scope field is called\u2026 \u2014 check 27 passed and 789fc180 The trigger editor's scope field is called Trigger when, and each option finishes that sentence is committed; then ran boot guard: installs, serves and launches claude, codex in 13.6s", "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 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/dashboard is slower than its budget \u2014 537ms last (59ms of it working, then 7ms more after it answered), against a budget of 50ms. Seen 115 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": "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": "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 16853 - answer by opening your turn with [!reply:16853]", "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.; 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 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 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 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 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 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": "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 4dcfc017a closed to-do 2930, to-do 2931 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 4dcfc017 An agent never loses its own environment - a\u2026 \u2014 check 27 passed and 4dcfc017 An agent never loses its own environment: a claim or switch carries every session of its process, and a running session moves to a worktree's environment only when it starts there is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 63.0s", "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": "waiting: 1 unread worktree 64", "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 a86f2079 journal sequence next names the step that\u2026 \u2014 check 27 passed and a86f2079 journal sequence next names the step that follows, never an included sequence's raw marker is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 64.3s", "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.", "meta": {"from": "journal"}}
{"content": "your message 16857 names 123 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 16857 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "the boot guard rerun, to tell machine load from a slowdown in the last commit\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": "auto mode is on and work 2132 stands still while todo 2923 is ready \u2014 if work 2132 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 2923. Stop only when nothing ready is left.", "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.", "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": "you ran the same check 3 times in a row - grep -n \"is not online\" -r src\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 23694ms last (15049ms of it working, 8ms collecting garbage, then 238ms more after it answered), against a budget of 50ms. Seen 3049 times.; hook POST /api/hook/codex is slower than its budget \u2014 854ms last (98ms of it working, then 5063ms more after it answered), against a budget of 50ms. Seen 14 times.", "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 session tests for the explained offline refusal came back - Test the\u2026", "meta": {"from": "journal"}}
{"content": "helper 85, Paula Pagewright, reported in message 16859 \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 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.", "meta": {"from": "journal"}}
{"content": "commit 6fa0ee4cc closed to-do 2924 and ended work 2133 \u2014 The rows and the work are done; take the next one.", "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": "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": "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; check 27 passed and 6fa0ee4c A session that is not online says why - taken by\u2026 \u2014 check 27 passed and 6fa0ee4c A session that is not online says why: taken by another session, no terminal reporting it, or how long since its terminal checked in is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 36.1s; request GET /api/main/dashboard is slower than its budget \u2014 4838ms last (139ms of it working, then 73ms more after it answered), against a budget of 50ms. Seen 116 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.", "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": "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 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 2 times (codes down); phone 36 completed; 1 new phone 37", "meta": {"from": "journal"}}
{"content": "the viewer build with the Documents page came back - Check migration\u2026; phone 37 completed; 1 new phone 38", "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 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 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 16869 - answer by opening your turn with [!reply:16869]", "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": "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 794967e49 closed to-do 2891, to-do 2889 and ended work 2134 \u2014 The rows and the work are done; take the next one.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/helper is slower than its budget \u2014 983ms last (95ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 14 times.; request GET /api/manifest is slower than its budget \u2014 1544ms last (111ms of it working, 12ms collecting garbage, then 39ms more after it answered), against a budget of 50ms. Seen 13 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 794967e4 The Documents library and preview, built into the\u2026 \u2014 check 27 passed and 794967e4 The Documents library and preview, built into the viewer is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 20.7s", "meta": {"from": "journal"}}
{"content": "1 new message 16873 - answer by opening your turn with [!reply:16873]", "meta": {"from": "journal"}}
{"content": "1 new message 16874 - answer by opening your turn with [!reply:16874]", "meta": {"from": "journal"}}
{"content": "helper 87, Ogilvy Plainword, reported in message 16876 \u2014 read it, then journal helper finish 87 once its work is taken or dropped; 1 new message 16875 - answer by opening your turn with [!reply:16875]", "meta": {"from": "journal"}}
{"content": "2 new messages 16877, 16878 - answer each by opening a turn with [!reply:<n>]", "meta": {"from": "journal"}}
{"content": "1 new message 16880 - answer by opening your turn with [!reply:16880]", "meta": {"from": "journal"}}
{"content": "1 new message 16881 - answer by opening your turn with [!reply:16881]; message 16881 updated", "meta": {"from": "journal"}}
{"content": "message 16881 file image.png needs tags \u2014 inspect the attachment, then journal message tag 16881 \"image.png\" \"<a few words describing what it shows>\"", "meta": {"from": "journal"}}
{"content": "1 new message 16882 - answer by opening your turn with [!reply:16882]", "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": "request GET /api/main/agent is slower than its budget \u2014 418ms last (77ms of it working, 5ms collecting garbage), against a budget of 50ms. Seen 41 times.", "meta": {"from": "journal"}}
{"content": "answer message 16878, message 16880, message 16881 before you write anything \u2014 answer each by opening a turn with [!reply:<n>]. a reply, a reaction, or journal message processed <n>; 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": "answer message 16874 before you write anything \u2014 answer by opening your turn with [!reply:16874]. a reply, a reaction, or journal message processed <n>", "meta": {"from": "journal"}}
{"content": "1 new message 16886 - answer by opening your turn with [!reply:16886]", "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.", "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 16889 - answer by opening your turn with [!reply:16889]", "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": "check 27 passed and 48793ed5 The plain-text rule flags only long phrases that\u2026 \u2014 check 27 passed and 48793ed5 The plain-text rule flags only long phrases that end in a preposition, and the links section keeps the user's Links to and Linked by is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 55.4s; the record copy for profiling the dashboard request came back - Copy the\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.; 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 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 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 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 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.", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2137 stands still while todo 2927 is ready \u2014 if work 2137 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 2927. 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 bare-number test came back - Teach the check seconds, gigabytes, HTTP\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": "helper 86, Hedy Patchmarr, reported in message 16896 \u2014 read it, then journal helper finish 86 once its work is taken or dropped", "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 31 \u2014 The overnight branch may be merged once every to-do is done and all\u2026 \u2014 Message 16886 (2026-10-06): the user said to keep working the to-do list on overnight-refactor; when every to-do item is finished, the whole suite passes and no part is left untested, the agent may merge it into main. This is the user's go for rule 57's 'before its pull request is approved'. Until all three hold, nothing of the branch goes to main except the hotfixes the user asks for.; 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 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": "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 ae6530a22 closed to-do 2911 and ended work 2137 \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 408ms last (55ms of it working, then 8ms more after it answered), against a budget of 50ms. Seen 71 times.; request GET /api/main/dashboard is slower than its budget \u2014 459ms last (53ms of it working), against a budget of 50ms. Seen 121 times.", "meta": {"from": "journal"}}
{"content": "1 new message 16899 - answer by opening your turn with [!reply:16899]", "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": "check 27 passed and ae6530a2 A measurement with its unit, an HTTP code or a\u2026 \u2014 check 27 passed and ae6530a2 A measurement with its unit, an HTTP code or a load figure in the chat is no longer taken for a row number is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 23.7s", "meta": {"from": "journal"}}
{"content": "1 new message 16903 - answer by opening your turn with [!reply:16903]", "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 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 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 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": "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": "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 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 60 \u2014 A hotfix is done by a dispatched agent in a worktree of main \u2014 Message 16836 (2026-10-06): the orchestrator cut a worktree under .claude/worktrees for a Codex hotfix, its session moved to a new environment and the user's messages stopped reaching it. The user: when working on a branch and a hotfix comes in, create a worktree of main and dispatch an agent to do that work. The orchestrator stays on its branch and in its environment, and never cds into another checkout.", "meta": {"from": "journal"}}
{"content": "1 new message 16907 - answer by opening your turn with [!reply:16907]", "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": "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 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": "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.; 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 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.; commit 3a431d34e closed to-do 2925 and ended work 2138 \u2014 The rows and the work are done; take the next one.; request GET /api/main/agent is slower than its budget \u2014 377ms last (59ms of it working, 2ms collecting garbage, then 21ms more after it answered), against a budget of 50ms. Seen 77 times.; 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.; 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": "command message reply is slower than its budget \u2014 807ms last (96ms of it working, 56ms waiting on locks), against a budget of 50ms. Seen 11 times.", "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.; check 27 passed and 3a431d34 A relaunched agent's old terminal record is\u2026 \u2014 check 27 passed and 3a431d34 A relaunched agent's old terminal record is retired instead of taking the new process, so it never holds an environment as a second live terminal is committed; then failed boot guard: slower than 15s boot guard: installs, serves and launches claude, codex in 22.4s", "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:81. 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 81 \"<part>\" \"Being written.\" for each, in order: the evidence, what was already sound, what remains uncertain. Then journal sequence next 10 --about report:81.", "meta": {"from": "journal"}}
{"content": "request GET /api/main/dashboard is slower than its budget \u2014 1093ms last (50ms of it working, 2ms collecting garbage), against a budget of 50ms. Seen 122 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 GET /api/main/helper is slower than its budget \u2014 1369ms last (61ms of it working, then 7ms more after it answered), against a budget of 50ms. Seen 15 times.", "meta": {"from": "journal"}}
{"content": "request GET /api/manifest is slower than its budget \u2014 907ms last (107ms of it working, 12ms collecting garbage, then 15ms more after it answered), against a budget of 50ms. Seen 14 times.", "meta": {"from": "journal"}}
{"content": "hook POST /api/hook/claude is slower than its budget \u2014 764ms last (61ms of it working, 4ms collecting garbage, then 4151ms more after it answered), against a budget of 50ms. Seen 3055 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": "waiting: 3 unread worktrees 66, 67, 68", "meta": {"from": "journal"}}
{"content": "auto mode is on and work 2139 stands still while todo 2903 is ready \u2014 if work 2139 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 2903. Stop only when nothing ready is left.", "meta": {"from": "journal"}}
{"content": "the three coverage helpers being dispatched came back - Dispatch three helpers\u2026", "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 three coverage helpers being dispatched came back - Dispatch three helpers\u2026", "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.", "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 36s 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 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": "request GET /api/main/agent is slower than its budget \u2014 7092ms last (77ms of it working, 2ms collecting garbage, then 55ms more after it answered), against a budget of 50ms. Seen 120 times.; request GET /api/main/agent is slower than its budget \u2014 7798ms last (77ms of it working, then 391ms more after it answered), against a budget of 50ms. Seen 120 times.", "meta": {"from": "journal"}}
{"content": "check 27 passed and 190e7e6c Packing old rows leaves out a row whose file is\u2026 \u2014 check 27 passed and 190e7e6c Packing old rows leaves out a row whose file is already gone instead of failing on it is committed; then failed Traceback (most recent call last): File \"/Users/jessegall/projects/agent-journal/scripts/boot_guard.py\", line 122, in <module> took = guard() File \"/Users/jessegall/projects/agent-journal/scripts/boot_guard.py\", line 102, in guard installed = subprocess.run([sys.executable, str(HERE / \"install.py\"), \"upgrade\", str(place / PROJECT)], env=env, capture_output=True, text=True, timeout=WAIT) File \"/opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py\", line 557, in run stdout, stderr = process.communicate(input, timeout=timeout) ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^ File \"/opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py\", line 1221, in communicate stdout, stderr = self._communicate(input, endtime, timeout) ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ File \"/opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py\", line 2154, in _communicate self._check_timeout(endtime, orig_timeout, stdout, stderr) ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"/opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py\", line 1268, in _check_timeout raise TimeoutExpired( ...<2 lines>... stderr=b''.join(stderr_seq) if stderr_seq else None) subprocess.TimeoutExpired: Command '['/opt/homebrew/opt/python@3.14/bin/python3.14', '/Users/jessegall/projects/agent-journal/src/install.py', 'upgrade', '/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/guard-z6a5gy46/Project builds']' timed out after 45.0 seconds", "meta": {"from": "journal"}}
{"content": "you ran the same check 3 times in a row - cd\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.", "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 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": "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": "waiting: 3 unread worktrees 66, 67, 68", "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 \"an uploaded image asks the agent for tags\" >       tagged = subprocess.run( [sys.executable, str(HERE / \"journal.py\"), \"--root\", str(record.root), \"--env\", record.env, \"message\", \"tag\", str(message.n), image.name, \"deployment graph with three regions\"], capture_output=True, text=True, timeout=20, ) src/features/attachment_descriptions/test.py:76: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /opt/homebrew/Cellar/python@3.14/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/subprocess.py:557: in run stdout, stderr = process.communicate(input, timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /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: -9 args: ['/Users/jessegall/projects/agent-journal/.venv...> endtime = 794776.883176625, orig_timeout = 20 stdout_seq = [b'---\\n{\\n  \"n\": 1,\\n  \"title\": \"look at this\",\\n  \"abstract\": \"\",\\n  \"refs\": [],\\n  \"seen\": [\\n    \"user\",\\n    \"age...ted\": 1791282215.887145,\\n  \"deleted\": 0.0,\\n  \"completed\": 0.0,\\n  \"outcome\": \"\",\\n  \"type\": \"message\"\\n}\\n---\\n\\n\\n'] stderr_seq = [], 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', '/var/folders/jw/5_sm4hg92x70kql0hrg4w54c0000gn/T/agent-journal-7eb9648a9d92404783b8a0d00456f593/gw2/tmpipciqtti/.journal', '--env', 't', 'message', 'tag', '1', 'dashboard.png', 'deployment graph with three regions']' timed out after 20 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 src/features/attachment_descriptions/test.py::test_an_uploaded_image_is_nudged_for_tags_and_the_cli_tag_command_files_and_finds_them 1 failed, 436 passed in 392.79s (0:06:32)", "meta": {"from": "journal"}}
{"content": "you stopped 5 minutes ago with work 2141, Drop the 29 migrations that only\u2026 \u2014 carry on with it now. If it waits on something outside your hands, say journal work await \"<what you wait for>\"; if it waits on the user, put the question on its row with journal todo ask and take the next ready row; if something else goes first, journal work park 2141 \"<why>\".; 1 new message 16921 - answer by opening your turn with [!reply:16921]", "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": "1 new message 16922 - answer by opening your turn with [!reply:16922]", "meta": {"from": "journal"}}
{"content": "1 new message 16923 - answer by opening your turn with [!reply:16923]", "meta": {"from": "journal"}}
{"content": "answer message 16922, message 16923 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": "check 27 failed, nothing was committed \u2014 check 27 failed, nothing was committed bringing up nodes... bringing up nodes... ........................................................................ [ 16%] ........................................................................ [ 32%] ........................................................................ [ 49%] ...................................................... check 27 ran out of time: stopped after 600 seconds; journal check set 27 timeout <seconds> gives it longer", "meta": {"from": "journal"}}
{"content": "your message 16925 names 600 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 16925 \"<the text>\"", "meta": {"from": "journal"}}
{"content": "helper 86, Hedy Patchmarr, reported in message 16926 \u2014 read it, then journal helper finish 86 once its work is taken or dropped", "meta": {"from": "journal"}}
