# Under the hood

> One polymorphic anchor, one store, one API and one component for every kind of object, with access reusing the platform's own checks, and two realtime mechanisms because discussing something and editing it together are different problems

Source: https://aipril.dev/docs/collab/under-the-hood

## One anchor, every kind of object

A thread does not belong to a dataset feature or a task feature. It is attached through a
**polymorphic anchor** - an entity type and an entity id - which means there is one store, one REST
surface, one composable and one component behind every comment on the platform.

That is the decision that makes the rest cheap. Adding threads to a new kind of object is wiring an
anchor, not building a comment system for it, so comments turn up in places most products would not
have bothered with - a range of cells, a checklist item, a block on a canvas.

And it is why they behave identically everywhere. Nobody re-implements mentions, reactions, resolve,
edit history or attachments per surface, so none of them is subtly different where you happen to be.

## An agent's comment is an ordinary comment

[AI employees](https://aipril.dev/docs/ai-organisation.md) write into threads through the same path as people: the same
anchor, the same store, the same access checks, the same event stream. An agent's finding on a
record is a comment on that record, with an author, in the sequence.

That is what keeps it useful instead of noisy. You can quote it, reply to it, react to it and resolve
it; it appears in the thread the humans are already reading, not in a parallel feed of machine
output; and the same visibility rules apply, so an agent cannot surface something into a thread that
its readers may not see.

## Access is the platform's, not the thread's

Comment access reuses the platform's existing entity-access checks instead of carrying a permission
model of its own. If you may not see the record, you may not see the conversation about it, and that
follows from one rule instead of two that have to be kept in agreement.

Threads are scoped to the organisation the same way everything else is.

## Two realtime mechanisms, deliberately

Collaboration covers two things that look alike from the outside and are not alike underneath:
**discussing something**, and **editing something together**. The platform carries a mechanism for
each, and the split is the point.

**Editing together is a convergence problem.** Several people change the same document at the same
moment and every copy has to end up identical without a lock and without a server arbitrating each
keystroke. That is what conflict-free replicated data types are for, and it is how the
[spreadsheet](https://aipril.dev/docs/spreadsheets.md) works - concurrent edits merge deterministically, so two people
typing in neighbouring cells never produce a "your version / their version" dialog.

**A thread is an event problem.** A comment is posted, edited, reacted to, quoted, resolved -
discrete events with an author and an order. Nobody co-edits somebody else's comment character by
character. So threads are delivered as an event stream over server-sent events: simpler, cheap to
hold open on every screen that has a thread on it, and shaped like what is happening.

Running one mechanism for both would have made one of them worse - a document that has to reconcile
merge state to show a reaction, or a thread paying for machinery it never uses. Both are
collaboration; neither is the other's special case.

## Delivery, and not delivering twice

A mention leaves the platform through the recipient's own channels - Slack, Telegram or email - over
the same connectors that carry process notifications, so there is one delivery path, not a second
one built for comments.

Two properties keep that from becoming noise: **delivery is logged for de-duplication**, so a
mention does not arrive twice because two things fired; and **read state is per mention**, so an
inbox empties as people deal with what is in it.

## Attachments

Files on a thread go into the platform's own file storage, with a size cap and a restricted set of
accepted types, instead of being embedded in the comment. An attachment is a stored object with the
ordinary properties, not a blob inside a text record.

## Activity is a separate, append-only stream

The record of what changed is not the same thing as the record of what was said, and it is stored
separately: an append-only activity stream, insert-only by grant, read through an endpoint that
applies the same entity-access gate and pages with a cursor.

So a busy object's history stays readable, and nothing in the activity path can rewrite what was
recorded.

## Where it shows up

Because the anchor is generic, collaboration appears wherever an object is worth discussing -
[records and tasks](https://aipril.dev/docs/processes/forms.md), [datasets](https://aipril.dev/docs/engine.md),
[spreadsheet ranges](https://aipril.dev/docs/spreadsheets.md), projects, and blocks on a page under design. The surfaces
differ; the mechanism does not.
