Two Readers, One Final
Every final answer a Coop worker writes has two readers, and they want opposite things. Dad reads the chat: in the Desktop app, in a terminal, on his phone. He wants a few lines in his own language that say what happened, where it landed, and whether he has to do anything. The engine reads the very same text and wants a record it can commit: which binding, which outcome, what evidence. One string, two audiences, and for a while the engine won and Dad lost.
It started with the engine being too strict. Until 2026-09-30, report_text demanded that the whole final be JSON, or one fenced block of it. A worker that wrote one sentence to Dad before the JSON, or a harness that appended its own tail after it (Codex Desktop's memory citations), lost the report without a word. Commit a17c3a3 made the parser tolerant: scan from every { with json.JSONDecoder.raw_decode, and the first object carrying a coop_reports list is the report, wherever it sits. That was right. It also meant workers could paste their JSON anywhere, and they did. Dad's chats ended in braces and quoted keys.
A Construct Markdown Defines and Never Draws
The fix came from a corner of Markdown most people never look at: the link reference definition. You have seen the reference style of links, [docs][1] in the text and [1]: https://example.com at the bottom. That bottom line defines a label. The CommonMark spec says plainly that it does not correspond to a structural element of the document, and its own example shows a document holding nothing but a definition rendering with no visible content at all.
So a definition is a line that a renderer parses and then draws as nothing. Commit ba0fb04 (todo #360, 2026-10-09) built the ending on it. A worker's final now has two parts: two to four plain lines in Korean for Dad (반말, addressed to 아빠), saying what was done, where it landed and what he must do; then a blank line; then the report on one line in this shape:
[coop-report]: <{"coop_reports":[{"binding_id":…,"outcome":…,"body":…,"for_dad":…,"evidence":{}}]}>
The pointy brackets turn the entire JSON into the definition's link destination. The commit records that the Claude Code terminal, react-markdown and pulldown-cmark all draw the line as nothing. Dad sees his Korean lines. The engine still finds the JSON, because its scanner never cared about Markdown in the first place.
The blank line is not decoration. A link reference definition cannot interrupt a paragraph. Put the definition directly under the Korean lines and CommonMark reads it as one more line of that paragraph, drawn in full.
One Angle Bracket Breaks It Open
A destination in pointy brackets may contain no line ending and no unescaped < or >. That rule bit within a day. One worker's report body carried a <repo> placeholder, the most ordinary thing an engineer types. The line stopped being a definition, every renderer drew it as text, and Dad got the whole JSON with its for_dad key showing and no answer meant for him. In the same stretch, workers answering a reply review or a follow-up several turns after their first work envelope had lost the rule entirely and pasted fenced JSON blocks, or opened with an English paragraph.
Commit d2bea99 (todo #375, 2026-10-10) answered both. final_form became the one ending text, and every envelope kind carries it: work, reply review and follow-up alike, because a rule stated once in the first envelope is a rule the worker no longer has three turns later. It now says the JSON must contain no < or > anywhere, body and evidence included, and tells the worker what to write instead: to for an arrow, {repo} for a placeholder, at least for a comparison. Acknowledgement tokens moved onto hidden lines of their own, [coop-read]: <COOP_READ id> and [coop-reply]: <COOP_REPLY id>, so the visible final is only the Korean lines.
The Engine's Model of Hidden
The engine keeps its own description of a hidden line, REPORT_LINE. The first version, from ba0fb04, accepted any characters between the brackets. That made it a liar in exactly the case that mattered: it would call a line hidden that every renderer was drawing. d2bea99 narrowed the destination to <[^<>\n]*>, CommonMark's own destination grammar minus backslash escapes, which the ending never needs. Even the leading {0,3} copies the spec's allowance of up to three spaces of indentation.
The commit says the pattern now matches a definition exactly as CommonMark reads one. That holds for the line and not for its surroundings, and the difference matters. The regex runs with re.M and judges one line at a time, so it cannot see what sits above it. A perfect definition line written directly under the Korean lines is drawn as part of that paragraph, and the same line inside a code fence is drawn as code. REPORT_LINE matches both. That is why the blank line is a written instruction in final_form rather than something the engine checks, and why the pattern's only job is cutting lines out of what Dad reads, a job where matching a line the renderer drew does no harm.
Notice the separation of jobs. Finding the report and deciding what Dad reads are two different functions. report_spans finds every JSON object with a coop_reports list, hidden or not, so a broken line still commits its report. dad_text decides what is shown when the engine relays a final: the reports' for_dad lines win, cut at FOR_DAD_LIMIT, 1,200 characters; without them it cuts the report spans, the REPORT_LINE lines and any empty code fences out of the prose. Every notification line passes through it, so whatever a worker pasted, report JSON never reaches Dad through Coop's own relay. The body stays the English record.
A Closed Set of Fields
One more guard sits on the report itself. REPORT_FIELDS is exactly binding_id, outcome, body, evidence and for_dad. A report carrying any other key is refused as malformed instead of being accepted with the extra quietly dropped. A misspelled evidnce that vanished without a trace would be worse than a rejection, because the worker would believe its evidence was on the record. The refusal happens in the adapter, before the engine is asked, and the third lesson of this track shows what that placement costs.