Map A17 onto Outline's actual primitives, and record the MCP decision #23

Merged
dongar merged 1 commits from a17-outline-mapping into main 2026-09-08 19:38:30 +00:00
Collaborator

Follow-on to #22 (which merged while I was writing this). Doc-only.

The mapping, from what you observed rather than what I assumed. Collections carry per-user permissions in three tiers — visible-to, read-write, manager — and permissions are collection-scoped, not per-document. So: one collection per writing principal, with read membership granted to everyone else.

I also tightened what that actually guarantees, because I had been sloppy: it enforces that no other agent can write a given agent's world model, which is the collision that would otherwise be silent and unattributable. It does not enforce operator-versus-agent exclusion, since you hold manager on your own wiki and can always grant yourself write. That half is procedural — the break-glass routine stops the agents first. Two different strengths, and the spec now says so instead of implying one covers both.

Per-user grants rather than groups. Outline groups appear not to be federated with LLDAP, so using them would put membership in a second place nothing syncs — cutting against A8's premise that each agent is an LLDAP principal. Three principals make the manual mapping trivial.

The requirement that would silently void everything else: since Outline API keys are per-user (thank you for confirming), each agent's token must belong to that agent's own Outline user. A token minted from an admin account evaluates every permission above against the wrong identity while appearing to work perfectly — the same shape as the Gitea surprise, where the design was right and the identity carrying it wasn't.

One requirement got weaker, not stronger. I previously wanted conditional writes to protect your manual edits. Because break-glass stops the agents, concurrent writes cannot occur, so compare-and-swap is unnecessary; all that is needed is the ability to observe that a document changed since the agent last read it, feeding A3's divergence check. A revision id or even updatedAt satisfies that, so the unverified API question no longer blocks anything.

Outline's MCP server: considered, deferred

Recorded as a rejected-for-now alternative so it does not get silently re-litigated. It exposes search/read/create/edit over remote Streamable HTTP with a workspace-level toggle.

Not adopted for the retrieval path, because A17's enforcement depends on this library sitting between the model and the store — staleness marking, snapshotting into the canonical log, binding procedures to action classes, and gating a failed confirm-first read all happen to a retrieval before the model sees it. MCP's purpose is the opposite, and a procedure the model may decline to fetch does not constrain it (A12).

It stays a reasonable candidate for the discretionary search tool later, and unlike the vendor agent SDKs A13 rejects, MCP is a neutral standard rather than lock-in. Two things to verify before adopting: that it honours the key owner's collection permissions rather than mere workspace membership, and that the workspace-level toggle does not force broader exposure than intended.

🤖 Generated with Claude Code

Follow-on to #22 (which merged while I was writing this). Doc-only. **The mapping, from what you observed rather than what I assumed.** Collections carry per-user permissions in three tiers — visible-to, read-write, manager — and permissions are collection-scoped, not per-document. So: **one collection per writing principal**, with read membership granted to everyone else. I also tightened what that actually guarantees, because I had been sloppy: it enforces that **no other agent** can write a given agent's world model, which is the collision that would otherwise be silent and unattributable. It does *not* enforce operator-versus-agent exclusion, since you hold manager on your own wiki and can always grant yourself write. That half is procedural — the break-glass routine stops the agents first. Two different strengths, and the spec now says so instead of implying one covers both. **Per-user grants rather than groups.** Outline groups appear not to be federated with LLDAP, so using them would put membership in a second place nothing syncs — cutting against A8's premise that each agent is an LLDAP principal. Three principals make the manual mapping trivial. **The requirement that would silently void everything else:** since Outline API keys are per-user (thank you for confirming), each agent's token must belong to that agent's *own* Outline user. A token minted from an admin account evaluates every permission above against the wrong identity while appearing to work perfectly — the same shape as the Gitea surprise, where the design was right and the identity carrying it wasn't. **One requirement got weaker, not stronger.** I previously wanted conditional writes to protect your manual edits. Because break-glass stops the agents, concurrent writes cannot occur, so compare-and-swap is unnecessary; all that is needed is the ability to *observe* that a document changed since the agent last read it, feeding A3's divergence check. A revision id or even `updatedAt` satisfies that, so the unverified API question no longer blocks anything. ### Outline's MCP server: considered, deferred Recorded as a rejected-for-now alternative so it does not get silently re-litigated. It exposes search/read/create/edit over remote Streamable HTTP with a workspace-level toggle. Not adopted for the retrieval path, because A17's enforcement depends on this library sitting *between* the model and the store — staleness marking, snapshotting into the canonical log, binding procedures to action classes, and gating a failed confirm-first read all happen to a retrieval before the model sees it. MCP's purpose is the opposite, and a procedure the model may decline to fetch does not constrain it (A12). It stays a reasonable candidate for the *discretionary* search tool later, and unlike the vendor agent SDKs A13 rejects, MCP is a neutral standard rather than lock-in. Two things to verify before adopting: that it honours the key owner's **collection** permissions rather than mere workspace membership, and that the workspace-level toggle does not force broader exposure than intended. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dev-agent added 1 commit 2026-09-08 19:37:13 +00:00
Map A17 onto Outline's actual primitives, and record the MCP decision
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 58s
2352a84004
Observed rather than assumed, after checking the running instance:
collections carry per-user permissions in three tiers and permissions
are collection-scoped, not per-document. So one collection per writing
principal, with read membership for everyone else.

- States precisely what that enforces: no other agent can write a
  given agent's world model. It does not enforce operator-versus-agent
  exclusion, since the operator holds manager on their own wiki; that
  half is procedural, via the break-glass routine that stops the
  agents first. Two different strengths, so the spec no longer implies
  one guarantee covers both
- Per-user grants, not groups: Outline groups are not federated with
  LLDAP, so using them would put membership somewhere nothing syncs.
  Three principals make the manual mapping trivial
- Outline API keys are per-user, so each agent's token must belong to
  that agent's own Outline user. A token minted from an admin account
  voids every boundary above while appearing to work — the same shape
  as the Gitea permission surprise
- Collections identified by ID from config, documents by ID recorded
  in the state store, which also makes writes idempotent across a
  retry
- The conditional-write requirement softens to a revision *read*:
  break-glass stops the agents, so compare-and-swap is unnecessary and
  only divergence detection is needed for A3

Also records Outline's MCP server as considered and deferred. A17's
enforcement depends on this library sitting between the model and the
store; MCP's purpose is direct model access, and a procedure the model
may decline to fetch does not constrain it. Still a candidate for the
discretionary search tool, with two things to verify first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dongar approved these changes 2026-09-08 19:38:26 +00:00
dongar merged commit dd22eda528 into main 2026-09-08 19:38:30 +00:00
dongar deleted branch a17-outline-mapping 2026-09-08 19:38:30 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dev/nisse#23