ADR-180: The sixth writer creates a page, and the one-record rule holds
- Status
-
Accepted (its rejected generic-writer alternative is re-evaluated — see ADR-197; its one-record rule counts what core's copy brings along — see ADR-199)
- Date
-
2026-08-20
- Amends
-
ADR-146 (its "revisit at the sixth writer" trigger)
- Amended
-
2026-09-21 by ADR-197 (the rejected generic-writer alternative, re-evaluated as a builtin); 2026-09-23 by ADR-199 (a copy is one named record plus what core copies with it)
- Authors
-
Netresearch DTT GmbH
Context
Five writing tools exist (ADR-135, ADR-146). Two of them bring a record into being — a hidden content element and a hidden translation — and none of them creates a page. A backend assistant asked to "add a subpage with an introductory text" can therefore draft the text but has nowhere to put it, and answers that it cannot, which is correct and useless in equal measure.
ADR-146 named two triggers for coming back to the writers: a sixth writer, and any writer that needs to write more than one record per call. Both were on the table at once, because the obvious shape of this tool is "page and first element in one call" — one approval instead of two. This record is the review that trigger asked for.
The alternative that was evaluated and rejected is wrapping a third-party MCP
server's generic Write tool into the registry. It is one tool over the
whole TCA — the shape ADR-135 refuses — it writes into a
workspace of its own choosing with no way to publish from here, and as a remote
tool it is admin-only by this extension's own rule. The page writer is a
purpose-built action on the terms every other writer already meets.
ADR-197 re-evaluated the generic shape as a builtin rather
than a remote tool and accepted it for CREATE only, under conditions that
record states; the three objections above are what those conditions answer.
Decision
create_ is the sixth writing tool, on exactly the terms of the
previous five: disabled by default, in the editing group, an explicit
Tool (NON_ — two calls leave two pages), a
human approval before every call (ADR-134), a preview at
suspend (ADR-136), a write through the Data under
the acting user's permissions, a read-after-write verification, and a refusal
vocabulary that never confirms a uid exists. It uses the ADR-146 plan
shape, as that record asked a sixth writer to.
What it creates is as small as a page can be:
- Always hidden. There is no argument to switch it off. Core's own default
for a new page happens to be hidden too, but the tool does not rely on that —
an installation may set
TCAdefaults., and the read-back catches the page that came out visible and deletes it again.pages. hidden = 0 - Always a standard page.
doktypeis fixed. A shortcut, a mount point, a link or a folder carries configuration (a target, a mount, a URL) rather than content, and a model that needs one needs a different conversation with an editor first. - Always the default language. A page in another language is a translation
of an existing page and has a tool of its own,
create_.translation_ draft - The field set is fixed: title, navigation title, position (
parentand an optionalafter_that must be a subpage of the same parent).page_ uid slugis left to the DataHandler's generator;hidden,doktype,fe_,group perms_*,is_,siteroot TSconfig,backend_and every other page field are refused by name.layout*
Authorisation is Permission:: on the parent, checked by the
tool and then again by the DataHandler, which for a new page additionally
enforces tables_, the page-type grant (pagetypes_) and the
field-level grants. The DataHandler's own permission check on a new page reads
the language field from the incoming record, so the tool states
sys_ explicitly — a non-admin is refused when it is missing.
The editor-action declaration (ADR-152) names pages with
parent as the required argument that carries the uid: the record an editor
selects is the page the new one goes under.
What the fourth plan() writer showed had become common
ADR-146 asked, at the next writer, whether anything the shared trait had
deliberately left per tool had become common. Two things had, identically in
all four plan writers, and a fourth copy of each was what the
duplication gate refused: the guard execute opens with (an acting user,
a backend environment, the live workspace — answered as the user to write as,
or the refusal) and the creation of one record through the DataHandler with
the reading back of its uid. Both moved into Plans
as writable and create, and the three ADR-146
writers use them in the same change — a private copy per file would have
relocated the duplication, not removed it. What stays per tool is plan
and everything that reads its shape: the record to write, the read-back and
its fields, the success line. The two pre-ADR-146 writers are, as before, not
retrofitted.
The one-record rule holds
The tool creates the page and nothing on it. "Page plus first element" was the tempting shape, and it is refused for the reason ADR-146 gave in advance: the one-record rule is what makes every refusal whole and every preview readable. An approver reading a card that says "a page and an element" is judging two records with two permission sets in one click, and the refusal vocabulary — "the whole call is refused rather than partially applied" — would need a second meaning for the case where the page may be created and the element may not.
A model that wants both calls create_ and then
create_ on the uid it was given; the page tool's
success message hands over that uid and names the next tool. Two approvals is
the cost, and it is the right cost: each card shows one thing, and the person
approving the element can see the page it goes on.
The mechanism that does span records already exists and is not this: ADR-162's batch planner runs one editor action over several records as N ordinary runs — N approvals, N audits — and is the answer to "ten pages under this parent", not a bigger single tool.
Consequences
✓ Six editorial writes are available where five were; a backend assistant can now build a subpage and its first text, each step approved and each step visible to the approver as one record.
✓ The write fence, the approval pause, the preview and the fail-closed audit apply without the tool arranging any of it (ADR-141).
✓ The read-back's safety net is exercised by a test that sets the installation-level default to visible, so the net is known to hold on the installation where it matters rather than only on stock core.
✕ Two approvals for "page with text". A person who wants one click per page will ask for a combined tool; the answer is this record, not a new one.
✕ The page arrives without content and without properties beyond its title, which is less than the backend's new-page wizard offers. That is the point, not a gap: the wizard is for editors, who may set everything; the tool is for a model, which may set what it can be held to.
Revisit when
A writer is proposed that genuinely needs more than one record per call and cannot be expressed as two tools in sequence or as a batch over one action. The question then is what the approver agreed to, and it has no answer in this record.