ADR-264: The creating writers create their record new in the workspace
- Status
-
Accepted (amended; current contract below)
- Date
-
2026-11-05
- Amends
-
ADR-260 and ADR-262 (their consequence that the creating writers refuse a draft workspace after the card), ADR-146, ADR-180 and ADR-197 (their live-workspace restriction)
- Amended
-
2026-11-05 by ADR-266 (move, copy, delete and translate act in the workspace); 2026-11-08 by ADR-274 (the two replacing writers replace in the workspace, and
attach_asks the inline owners in the workspace)file_ to_ record - Authors
-
Netresearch DTT GmbH
Note
Current contract (reviewed 2026-11-07): Structural writers now support the recorded draft workspace within the explicit refusal limits; see Tools, languages and workspaces.
The decision text below records its original context; the linked successor explains the changed contract.
Context
ADR-260 and ADR-262 let the writers that
change fields of an existing record store a workspace version in the draft
workspace a run was started in. The writers that CREATE a record still
refused a draft workspace, after the approval card:
create_ (tt_), create_
(pages), create_ (any table without a writer of its own),
attach_ and attach_ (a
sys_ and the parent's field). set_ and
replace_ create a reference too, and delete the one it
replaces.
Measured on 2026-11-05 against TYPO3 14.3.10 with a backend user in a draft workspace, the DataHandler started with that user:
- a new record is one row new in the workspace:
t3ver_the workspace,wsid t3ver_1,state t3ver_0, the pid of the page or of the record it is placed after (oid Data, theHandler:: process_ datamap () $createbranch;New Version resolveandSorting And Pid For New Record () getplace it after a live element or one new in the workspace, and after a moved element where the workspace shows it);Sort Number () - the permission to create on a page is asked of the STORED row the pid
names,
Backend— the live row of a page with a version, the row itself of a page new in the workspace — not of the overlay (13.4.37 and 14.3.10);Utility:: get Record ('pages', $pid) - a
deleteof a row new in the workspace discards it: the row is removed (Data→Handler:: delete Action () discard); a() deleteof a live reference stores a delete placeholder (t3ver_2);state - a new file reference of a parent with a version, or new in the workspace, is related to the parent's LIVE uid, and the parent's version counts it;
- versioning a parent versions its inline children, and
RelationwritesHandler:: write Foreign Field () tstampandsorting_1..n onto the LIVE child rows by uid (14.3.10 and 13.4.37, the update at the end of the method). The live order stays, and an editor's own save in the workspace does the same;foreign - a datamap that names the parent's existing references by their live uids writes the new order onto those live rows and leaves the versions' order: a new reference then does not come last in the workspace. Named by the versions' uids, it comes last and live keeps its rows;
- discarding a parent's version discards its children's versions and the references new under it;
- where the sort order of a page has no room for the new record — it goes
first and the first record sorts below 1, or the record after its anchor
sorts at most one above it, rows new in any workspace counted —
DatarenumbersHandler:: increase Sorting Of Following Records () sortingof every row on the page witht3ver_0, live rows and rows new in any workspace alike (14.3.10 and 13.4.37, called fromoid get). An editor's own create in the workspace does the same. Versions are left where they were, and the workspace overlay takes the version'sSort Number () sorting: measured on 14.3.10 with the anchor at 256, the next element at 257 and that element's version at 257, the new element got 512, the live next element 769 — and in the workspace the new element came after the next element instead of after its anchor.
Decision
The five creating writers create in the run's workspace. Like the field
writers they refuse a draft workspace only where the run did not record it,
name the workspace on the card and end their answer with "Stored as a draft
in the workspace …". Everything around the write looks at the records as
the workspace shows them, through
Creates:
- the page, the parent page, the anchor element or page, the content element or record a file is attached to: the live row overlaid with its version, or a row new in that workspace; never another workspace's row, and not one deleted in the workspace;
- the page permission is asked of the stored row, as core asks it; for a record a file is attached to that the workspace moved to another page, of both pages — core asks the update of the record on the page its stored row is on, and the new reference's insert on the page it moved to;
- the read-back reads the row new in the workspace;
- the duplicate warnings of the two draft tools look at live rows and the
rows of that workspace, each as the workspace shows it: deleted, moved
away or renamed there is no copy. The connected-translation check of
create_counts live rows and the rows of that workspace — not another workspace's drafts;content_ element_ draft - a rollback deletes the new row, which core discards.
A file reference goes onto the parent's version, made first. The attach
writers make the parent's version with its own version command before the
datamap, then name the field with the uids of the references' rows in the
workspace. A write that fails — refused, not read back, or thrown — discards
the version it made, and with it the reference; where the parent already had
a version, or is new in the workspace, the reference is deleted and the
field list put back. The workspace is as it was, and where that does not
happen the answer says so. Whether the version is this call's is read from
the DataHandler's own copy map: a version somebody else made in the meantime
is never discarded.
The attach writers ask what core asks before it updates the parent.
Making a version asks only the table grant and "show page"
(versionize); the update of the parent then asks
check of its stored row — language, auth values
such as the content type, edit lock — and the discard of a version asks the
same. A user who lacks one of them would get a card, a reference row and a
version nobody can take back, and the refusal only after the approval. So the
plan asks Acts's may of the
stored row before the card, in live as in a draft workspace.
A create that finds no room in the sort order is refused, and nothing
moves. The creating writers run their datamap through
Tool with keep, which
overrides increase: in a draft workspace the
run leaves the sort order alone and records that it found no room. The
record it inserted is deleted again — a row new in a workspace is removed
outright — and the call is refused with the reason. Shifting this
workspace's versions as well would need a write outside the DataHandler,
and the live rows and the other workspaces' drafts would still move. Live
runs renumber as before. Moves reach the same renumbering and are not part
of this record. Unlike the other refusals of a draft workspace, this one
comes after the approval card: whether there is room is known only inside
the DataHandler run, from rows of every workspace, which the card does not
read.
A table TYPO3 does not version is refused before the card. The five
writers implement Refuses with
refuse (ADR-262):
create_ and attach_ for the table the call
names, the attach writers for sys_ as well, the two draft
tools for tt_ and pages — which a stock TYPO3 versions; the
refusal matters where an installation takes that off, or sets
versioning, which 13.4 honours for a versioned
table too. A name the TCA does not declare is left to the plan, which
refuses it in its own words. The card shows the refusal instead of the
change.
The two replacing writers refuse a draft workspace before the card
(lifted by ADR-274).
set_ and replace_ delete a reference
and handle the copies of its translations; in a workspace each delete has to
name the right one of the live uid, the version's uid and the uid of a row
new there, or it discards a draft instead of deleting a record. That is not
built here. They implement Refuses with the
live-workspace refusal they gave after the card.
Consequences
✓ An editor in a draft workspace can let the chat draft a page with content, a record and an image on them; all of it waits in the Workspaces module, and live stays as it was.
✓ In live too, an attach the user's record-level rights do not allow is refused before the card instead of after the approval.
✓ The model can build on its own drafts in that workspace: a page it created there takes an element, an element it created there takes an image and anchors the next element.
✗ Live is not untouched to the byte. Versioning a parent's references writes
tstamp and renumbers sorting_ on the live reference rows
(core, see above). Which references, which files, their content and their
order stay as they were; nothing of another workspace is read or shown.
✗ Where a page's sort order has no room at the place asked for — the record goes first and the first record sorts below 1, or the record after the anchor sorts at most one above it — a create in a draft workspace is refused after the approval, where an editor's own create in the workspace succeeds and puts the drafts out of order. The assistant can reach this by itself: each create halves the gap of 256, so the tenth create in a row at the top of a page, or the ninth after the same element, is refused. The rows counted are those of the whole page, across columns and languages, and include other workspaces' new rows, so an editor may see room on the page and still get the refusal. Another place on the page, or room made in live, gets past it.
✗ set_ and replace_ still write in live
only; in a draft workspace the editor has to switch to live for them (lifted by
ADR-274).
✗ attach_ asks the records that hold the record as an
inline child (ADR-230) live: where such an owner exists in
the workspace only, the call is refused as not found (lifted by
ADR-274: the owners are read in the workspace).
✗ sys_ is not marked as written by an AI
(Mark marks pages and tt_), in a
workspace as in live.
✗ The connected-translation check of create_ counts
the rows stored in live and in the workspace without the overlay: a
translation deleted there, or a version of one, still counts. It can refuse
an element the page would take; it cannot let through one it would not.
✗ The move, copy, delete and translation writers still refuse a draft workspace after the card (lifted by ADR-266).