Files
pad/plugin
xarmian 4d6ebd426c fix(server): attribution comes from the request, never the body (BUG-3372) (#1754)
* fix(server): attribution comes from the request, never the body (BUG-3372)

A body could set who wrote a comment, an item, a link or a version, and
from which client: author (free text, rendered as the comment's author),
created_by, source, last_modified_by and version_source all yielded to a
body value, so any editor could post as another member, or file a write
as an agent's or a human's at will.

- Comments and replies: author is the signed-in account's name (empty,
  labelled by kind, with no account); created_by and source are the
  request's.
- Item create: created_by and source are the request's. Item update:
  last_modified_by and the version row's source are the request's, and a
  body source no longer moves items.source (an update never should).
- Links (blocks / parent / ...): created_by is the request's.
- The canonical account columns are filled on every request write path:
  items.created_by_user_id and last_modified_by_user_id (create, update,
  artifact import, cross-workspace copy), item_links.user_id (link
  create, parent set on create and update). Account deletion already
  nulls all three.
- X-Pad-Agent stays the agent signal (self-declared; it only labels the
  caller's own writes) but is trimmed, stripped of control characters
  and capped at 64 runes; a header that cleans to nothing is not an
  agent.

No client sends any of these fields except the web's comment create and
the MCP comment door, which send the values the server derives anyway.

Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC

* fix(server): legacy document doors and bulk edits stamp attribution from the request (BUG-3372)

Codex review round 1:
- POST/PATCH /documents still took created_by, source and
  last_modified_by from the body; they are now the request's.
- Bulk field and tag edits stamped the writer's kind but not the
  account; they now fill last_modified_by_user_id like a single update.

Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC

* fix(server): the document autosave hint reads the body's source before attribution is stamped (BUG-3372)

The legacy document PATCH skips its activity row for a content-only web
autosave, keyed on the body's source. Stamping source from the request
made every cookie request 'web', so every content-only update lost its
activity row (TestActivityEndpoints). The body's value is kept as that
hint only: it decides whether an activity row is written, never what any
row says.

Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC

* fix(server): the document autosave skip also requires a web request (BUG-3372)

Lead's review: the autosave hint is the body's, so any caller could skip
the activity row; a PAT or agent sending "source":"web" on a
content-only PATCH wrote no audit record. The skip now also requires the
request's own source to be web. Red-first test with the cookie control.

Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC
2026-10-03 03:51:24 -04:00
..