Decisions: memory, MCP responses and tool surface (2026-09-17)

Versions

#319 • Updated Sep 17, 2026

Decisions taken on 2026-09-17, in the order they were made. Each links to the concept page or task that carries the detail. Status as of the end of that day.

Web app

  • Tab switches keep the tab bar still. The Moderation tab is reset only when the project changes, not during a switch between tabs of one project. Done.
  • The memories tab is headed "Memories", without the project name, description or visibility badge, like the tasks tab. Done.
  • A min-height on the content area was rejected: the flex layout already keeps the footer at the bottom. The remaining blank moment when switching to tasks or docs is a known limit; caching the project for an immediate heading or a delayed fade-in are the options if it matters.

Memory structure and hygiene (#290, #291)

  • Title, summary and detail get distinct roles: title up to 80 characters for people, summary up to 300 as a terse gist for recall, detail for everything else (#293).
  • Weight critical, normal, low: critical memories are always in recall and never in a bag, low ones only in search (#294).
  • Bags: exactly one level of submemories. A bag is derived from having children, never a flag. Recall shows a bag with the titles of its children, because they stay current when the bag's summary does not (#295, #296).
  • Recall mode per project: flat below a threshold of top-level memories, grouped above it, overridable in settings (#296).
  • A deterministic hygiene score from structure and timestamps, without an LLM, always with findings that name the items. A child changed after its bag's summary lowers the score (#292).
  • Growth is slowed at write time, not only cleaned up afterwards: enforced limits, similarity hints from memory_store, aging of session summaries (#297), and cleanup guidance in the system guide (#298).

Commits

  • Commit links only on tasks (#299): several commits per task, full SHA over MCP, the repository recorded as it was when linked, no free-text description, no new tool.
  • Pages join the relation graph (#300), so memories and pages reach commits through their task.
  • Memories and pages record the source state they were checked against per version (verifiedAgainst, #301 under #216), not a commit list.
  • Automatic linking from commit messages is not pursued now.

Recall (#215, done)

  • Every section states how many entries it shows of how many and where the rest are read (task_list, item_search).
  • No fixed caps of twelve tasks or pages: sections take a share of maxChars, so a larger budget shows more.
  • Recall is a bounded summary; task_list is the complete task list.

Bounded MCP responses (#302)

  • Lists carry no details (#303, done): identify, sort and filter; item_get returns the rest.
  • One paging contract for every list: limit, cursor, total, hasMore, nextCursor (#304).
  • Large items are read in parts with offset and take in characters, cut at line breaks, a server-reported nextOffset, an outline of headings, reads pinned to a version, and a guard against writing back a partially read page (#305).
  • Responses are trimmed (#306, done): null fields left out; the relaxed JSON encoder instead of six-character escapes; task entries without timestamps or assignee reference; age only for changes within seven days (today, 3d) or 7d+ for work still in progress; item_get on a memory returns its current revision only.
  • Context is measured in tokens, not characters. Short field names (cSC) and compact dates (2609170103) were rejected: they save few tokens and invite misreading. A columnar format was deferred, since paging bounds list size anyway.

MCP tool surface v2 (#309, #310)

  • Eight tools replace 27: recall, project_get, project_store, item_get, item_list, item_store, item_action, item_delete.
  • Read, write and irreversible stay separate tools, because clients grant permissions per tool.
  • Per-kind data travels as a JSON object (fields, filter) validated by the server; unknown fields are refused.
  • One patch rule: omitted keeps, null clears.
  • One version per item replaces the mix of version and baseRevisionId. Every item already counts its changes.
  • No revisions. They existed because a proposal used to be an unpublished revision next to the published one; proposals are a separate system now. Items carry their current content, and one version history for every kind appends the full state of each content version with author and time. It serves reading earlier states, comparing proposals with their baseVersion, and deleting a single version for privacy. Tasks move from their field-change log to full states as well.
  • Moves are not version-checked and do not increase the version: whoever moves an item wants it at the new place, so the last move wins. Existence of the target, cycles and permissions are still checked when the move runs, and a text change is never refused because of a concurrent move.
  • Visibility of a memory is private or shared. Private is visible to the author only; shared to everyone with access to the project, directly or through its organization, and to everyone when the project is public. No public per memory: hiding inside a public project is only obscurity. shared rather than public, which would contradict the project's own badge in a private project, and rather than project, which is no counterpart to private.
  • Organization memories are dropped. An organization is an access group for projects; organization-wide guidance stays in its agent instructions. Personal memories across projects stay.
  • Blocking a task is an action with a required reason (block, unblock).
  • Forcing a proposal: a setting on the agent key in the web app and propose: true per call.
  • Batches are one JSON object for every kind: up to 100 items and 1 MB, mixed kinds, local keys so items can be each other's parents, atomic and replayable, one proposal for the whole batch when any item needs review. Needed for moving memories between agents and for imports from task boards and wikis.
  • Dropped parameters: includeUpstream (upstream knowledge is always included), relation depth, slugs for addressing items when writing.
  • project_get replaces the assignee list and also reports the caller's permissions, so an agent knows before writing whether it publishes or proposes.
  • Privacy deletion is decided by a person in the web app. Agents can still request a deletion, which locks the content at once.
  • No aliases for old tool names; the switch happens in one step (#317).
  • Measure first: the eight schemas are prototyped and measured before anything is built (#311).
  • project_update keeps omitted values already now, instead of replacing all three and silently removing the repository URL. Done.

Review of the plan, later the same day

  • v2 changes the tool surface only. The two data model decisions above run after the switch as their own tasks: #320 drops the revisions, #321 changes visibility. Until then v2 maps version onto the published revision internally and accepts today's visibility values. Reason: both are migrations with their own risk, and neither is needed for the tool contract.
  • The kinds are not fixed. Commits and further kinds will follow; per-kind tools cost three to four tools each, v2 one description line. This, not only today's 19,000 characters, is why the consolidation is worth a rewrite.
  • Most clients load the whole list, so list size is the primary cost. Claude Code loads tools on demand by keyword; per-tool size and findable names are recorded by #311 for it, but do not decide.
  • Gate in #311: the list lands well below 19,000 characters and a real session on the stubs needs no more retries than today once the error messages name the allowed fields. Nothing else gates.
  • Memory creates still run on draft revisions (DraftRevisionId in proposals); #320 moves them to proposals before removing the revision tables.

Not yet deployed

Everything marked done after the deployment of the tab fix (commits from #215 onwards) is committed but not deployed.

Connection interrupted or session ended. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.