Entity versioning & history
Every meaningful edit to an initiative, plan, skill, or set of agent instructions is captured as a full snapshot with its source. Restore is non-destructive.
An AI authoring tool without a real undo button is not a serious product. The moment the agent rewrites an initiative or changes a plan in the middle of a Flow, you should be able to see what it did, who set it in motion, and put it back the way it was. Version history is the part of Disco Parrot that makes that promise real. Every meaningful save is captured as a full snapshot of the record. Each snapshot remembers who did the editing, when, and whether it was a human or an agent. Restoring an older version writes forward, not backward; the history grows by one. Nothing is overwritten and nothing is lost.
What gets versioned
Four kinds of record are versioned today, with full read-and-restore surfaces. The list is deliberate.
| Record | Why it is versioned |
|---|---|
| Initiative | The unit of intent. The body, the open questions, the dependencies, and the schedule fields all move over time, often by AI in chat or by a Flow. |
| Plan | A single body of work, often authored by AI through a Flow. The steps and acceptance criteria are exactly the parts a reviewer needs to look back at. |
| Skill | A prompt is a working artifact that the team tunes, sometimes with AI assistance. A version history lets you tune without losing the prior wording. |
| Agent Instructions | The steering you give the agent at the workspace level or at the level of a sandbox profile. A change here ripples across runs, so the history is what you reach for if a tuned instruction stops working. Agents can edit instructions during a run, so versions are marked with the source for the same reason any other AI edit is. |
Three more records (sandbox profiles, MCP server configurations, and Flows) write version rows into the same store whenever they are saved, but the list and restore surfaces are not yet exposed for them. The history exists; the UI does not.
Bugs, sprints, goals, test cases, projects, and documents are not versioned in this system today. Their changes are still captured in the audit log and, where the change came from an agent, in the Sessions transcript. The version-history surface itself is reserved for the four records above. Documents do carry a per-file version history of their own (each upload writes a new version row); this is a different shape from the entity-versioning system here, and lives on the Documents page.
What a version actually is
A version is a full snapshot, not a diff. The whole record body lands in a versions store the moment the save happens: title, body, structured fields, relationships, all of it. Reading the record at any point in its history is a single lookup, not a replay of changes against a base.
Alongside the body, the version row remembers who did it and what they did. A version number that grows over time. The user under whose session the save happened (the agent runs inside someone's session, so AI edits still carry a real user id). The display name at the time of the save, frozen on the row so a name change later does not rewrite history. The timestamp. The source mark of user, ai, or system. A short change note: a person's own message when there is one, an auto-generated summary of which fields changed when the agent did the editing, or "Restored from v3" when a restore brought an older body back. A hash of the canonical body, which is what tells the platform whether the next save is actually different from the last one.
Bodies of any size land in the same place. Records over about 58 KB of text overflow to blob storage and get referenced from the version row, which is the storage layer's polite way of dealing with a one-megabyte spec body without losing it. From a reader's point of view, the version detail page renders the body the same way whether it came back inline or from blob; the overflow is something you do not have to think about.
A version saved by an agent and a version saved by a human read the same way in the UI, with one difference: the source is marked ai. That mark drives the audit trail filter and lets the version history page tell the two apart at a glance.
A version row only appears when something changed
- version number increments
- source mark, editor, time set
- change note recorded
- existing version number returned
- save marked as a no-op
- history stays clean
A version history full of duplicates is not really a version history. Fifty rows where ten are real and forty are echoes of unrelated saves is just noise; you can't find what changed because the changes are buried in things that did not. So a save in Disco Parrot only writes a version row when the body is actually different.
The check is the hash. The platform canonicalizes the record body about to be saved, hashes it, compares it to the latest row's hash. Different bodies write a new version. Identical bodies return the existing version number and skip the write. The result is that "v8 is different from v7" is always a true statement; the diff is always real; the list is always worth reading.
Plans skip the pull-request fields
There is one deliberate exclusion. A plan version does not include the pull-request fields (prNumber, prUrl, prStatus, prBranch, prOpenedAt, prMergedAt).
The reason is the lifecycle. Those fields are driven from GitHub through webhooks and the create-pr step, not from the editorial process the rest of the plan body goes through. Including them in the snapshot would make versions of a plan look like they were changing just because a PR moved from open to merged, and that is not an editorial change anyone made. Restoring a past version of a plan also preserves the current PR fields on the record rather than reverting them, so you cannot accidentally "undo" a PR by restoring an older plan body.
This carve-out is unique to plans. Initiatives, skills, and agent instructions snapshot every field.
Restore is non-destructive
Restoring an older version writes a new version that takes the older one's body and re-saves it as the current record. If you are at version 8 and you restore version 3, the new state is version 9 (carrying version 3's body) and versions 4 through 7 stay in the history exactly where they were. The change note on version 9 reads "Restored from v3".
That choice matters. A destructive restore (delete the rows after version 3 and stamp version 3 as the current state) loses the work that happened between version 3 and version 8, and there is no way to look at it again. A non-destructive restore keeps every step visible. If the restore turns out to have been the wrong move, you can restore a more recent version and the history shows what happened: version 3 was restored as version 9, then version 7 was restored as version 10.
The restore writes through the same edit path as a manual save. The audit log gets a row (typed as an update with restoredFrom set in the payload and the change-note prefixed with "Restored from v"), the version history gets a new entry, and the SSE stream tells every connected client to refresh. A restored plan does not lose its current PR linkage, and a restored skill stays valid against its bindings. If you try to restore a version of a record that has been deleted, the endpoint returns a not-found rather than silently un-deleting; the right path is to undelete the record first, then restore.
Reading the history
The version-history surface is a list, and it answers questions the way every other list in Disco Parrot does: filter, sort, search. The planner-facing operational guide at Reading version history walks through the diff view, the restore flow, and a weekly playbook for reviewing agent edits. The most useful filter is the source mark. Showing only the AI-authored versions of an initiative tells you exactly what the agent did across a stretch of work; showing only the human-authored ones surfaces every editorial change a person layered on top. You can also filter by who edited, by a range of version numbers, and search the change notes for keywords.
The list itself is intentionally lean. Each row shows the version number, the editor, the time, the source mark, and the change note: enough to scan, not enough to slow the page down. Tap into a row and the detail page loads the full body and compares it to the current state, side by side in a git-style diff for the structured view, paragraph by paragraph in the edits-only view. The diff is computed in the browser from the two bodies the page already has, so it is fast and the network does not move large bodies around just to render the colored lines.
How the agent's edits get marked
The AI source mark is not a label slapped on after the fact. It is written at the moment of the save, by the part of the platform that did the writing.
When you continue an initiative in chat and the agent rewrites the body, the sweep that reads the agent's edits back from the sandbox stamps each save as AI. When a Flow step authors a plan, the create or update call inside the step does the same. When a chat conversation invokes the dedicated tool to refine an agent instruction at the profile level, that write lands as AI too. Every path that ends in a write-by-agent flows through the same field. A filter that asks "show me only the agent's edits" answers against something real, not against a guess.
That consistency is what makes the audit trail honest. The version history shows the body the agent saved. The audit log records that the edit happened. The Sessions transcript shows the tool calls and file edits that produced it. Each one carries the same mark; you can move between them without losing the thread.
Two people saving at the same time
The interesting edge case in a workspace with active humans and active agents is two saves landing on the same record at almost the same moment. The platform takes a conservative line on it.
The canonical record updates last-write-wins; whichever save reaches the store second is the body of the record afterward. The version-history rows for both saves are attempted in parallel, and if the second one collides with the first at the version-row level, the platform retries the version write a few times to land it cleanly. Most of the time, both rows land and the history reads truthfully. In the rare case where every retry collides, the canonical record is still saved and the missing version row is logged as a failure rather than silently dropped. The default is that the record stays correct first, and the history stays correct as soon as the retry can manage.
Where versions are kept and for how long
There is no TTL on the version store today. Every version of every record stays in the history for as long as the record does. The reason is the audit story: a version history that quietly evicts old snapshots is a version history you cannot trust when you actually need it. We pay the storage cost so the answer to "what did this initiative look like at the start of the quarter" is always available.
Soft-deleted records keep their history. Undeleting the record brings the history back with it.
Permissions
Each entity splits read from manage. Reading the version list and opening a past version goes with the entity's read scope (initiatives.read, plans.read, commands.read for skills, agent-instructions.read). Restoring goes with the entity's manage scope (initiatives.manage, and so on). The diff is computed in the browser, so it inherits the read scope automatically; a user who can read can compare any two versions of a record. A user who can read but not manage can audit without being able to change anything. A user who can manage can do both.
How the three audit records fit together
Version history is one of three records that, together, answer the question "what happened on this record." The audit log records that a save happened: when, by whom, against what record, with the source mark. The version history holds the actual body that was saved at that moment. The Sessions transcript shows the full execution behind an agent's save, including the tool calls and the file edits that led to it.
The three are linked by the source mark and the record id, not by hard pointers between them. That is enough. When a restore happens, the audit row carries restoredFrom in its payload so the choice is recorded; the version row is the result of the restore. Together, the three records tell the story from "an edit was attempted" to "this is the body that landed" to "this is what the agent did to get there."
The bigger picture
An AI that can write into your real work is only safe if you can read what it wrote, compare it to what was there before, and put it back the way it was. Disco Parrot gives you the version history for that, the audit log next to it, and the Sessions transcript when you need to see the full run. The agent rewriting your initiative is not a black box. Every save is captured, every change is honest, and nothing you wanted is ever lost behind something it did.
The planner-facing operational guide: opening the tab, reading the diff, restoring an older body.
The discrete-event view of what changed, when, and by whom. Pairs with the body snapshots here.
The per-execution transcript of an agent run, including the file edits and tool calls behind an AI version.
Versions plus audit plus checkpoints is what makes agent authoring safe to ship on real work.