K Kuickr
Field notes

Preventing document overwrites when two writers share one page

An agent publishes over MCP; a person pushes from a repo. Stopping them destroying each other was the easy half — the write you have to worry about is the one that never happened.

The setup

Two writers, one page

An agent publishes over MCP, straight to the server. A person edits files in a git repo and pushes them with the CLI. Both are first-class. Neither knows about the other.

That is the setup for the oldest bug in shared documents: the last writer wins, and the other one's work is gone. So we built guards. They work. Then we lost a week of research anyway — to a failure mode the guards were never aimed at.

Signature

Where the two paths meet

Toggle the loop and watch what survives.

MCPAgent publishes a page to the server
RepoYou edit files and push with the CLI
ResultBoth pages live, both in git
Pull first and the repo is a complete picture of what exists.
What already works

Four guards against destruction

  • Changed files onlyPush hashes every local file against a manifest of what the last sync actually sent, and moves only what differs. A push that re-uploads everything is a push that overwrites everything.
  • Prune is offDelete a file locally and the server page stays. Moved a folder, switched branch, cloned fresh? Under an aggressive prune each of those reads as "the user deleted forty pages."
  • The three-way guardThe manifest records the common ancestor. If both sides moved on from it, that is a real conflict — and Kuickr stops rather than picking a winner. Pull carries the identical guard the other way.
  • Drift is printedPush names pages that exist on the server but not in your directory, instead of silently ignoring them.
  • Repo-managed pages die at their sourceAsk the server to delete a page that came from a repository and it refuses: delete this Page at its repository source. The repo is not merely the conventional home for that content, it is the only place the deletion can start. One authority for existence, not two.
The part that failed

Protection is not retention

A folder had sixteen pages on the server and eleven in the repo. The five that existed only on the server were not scratch — they were among the most-wanted documents in the project.

Nothing had been lost. The guards had done their job through several pushes. Those pages had simply never been pulled, so they were invisible to the repo, to grep, and to every agent reading the project to understand it. Live, correct, and unfindable by the people most likely to need them.

The drift warning had been printing the whole time. A line in push output that nobody acts on is not a mechanism. Preventing destruction is automatic; keeping two authoring paths converged is not.

The third failure

A rename is not a rename until the other side is told

Then we ran the loop we had just written down, and it handed back seventeen files we thought were gone.

Every one was a local move. Files renamed to a new convention. Old drafts filed into an archive folder. Perfectly good housekeeping — in git. The server had never been told, so it still served all of them at their old paths, and the pull dutifully restored what it saw. Both trees had been carrying the same documents under two names for weeks. That is not a sync bug. That is what a move looks like when only one side hears about it.

The obvious response is a bulk delete of anything that looks like an old name. We checked first. Three of the seventeen had no new name to be a duplicate of. They were documents that had only ever existed on the server, and a tidy-up would have destroyed all three while looking like cleaning.

Which is the same lesson a third time. Deletion should follow evidence, not resemblance — whether the thing doing the deleting is a prune flag, a force push, or a person who is confident they recognise a pattern.

The fix

It is git's loop, because it is git's problem

kuickr pull      # bring down anything authored elsewhere
git commit       # the repo now matches reality
kuickr push      # send your changes

Pull first. Always. The push guard protects you from destroying someone's work. Only pulling keeps your copy honest about what exists.

We found the fifth guard by tripping it. Cleaning up one of those stale duplicates, we asked the server to delete it and were told no — delete this Page at its repository source. Exactly right. The page had come from a repository, so the repository is where its life ends. Anything else would mean two places could decide whether a document exists, which is the same class of problem as two places deciding what it says.

Both commands take --force, and it does what it says: overrides the three-way guard and takes your side. It is the one flag that can destroy another writer's page. A conflict is information — it means you are about to find out what someone else wrote. Pull, look, merge, push. "Force it to clear the error" is how you turn a guard that worked into an outage.

The shape of it

Losing a document is losing the reason

Every one of these choices trades a little convenience for the property that no write silently destroys another. Push is narrower than you would expect. Delete does not propagate. Conflicts stop the world instead of resolving themselves.

But the incident taught the sharper lesson: a guard that only prevents damage still lets a document become invisible. Documents are not rows in a table. They are the record of what a team decided and why.

Keep every page, from every writer

kuickr pull · kuickr push · one loop, both directions
More from PaveLabs
K Kuickr you're hereA home for every page
N NodepadOperator runtime & notes B BuiltOnRailsA community of Rails builders shipping in public I InventListA live feed of ships and stories from indie makers S ShorttagsEvery side project's metrics on one dashboard s starrrsSave, organise and discuss links on boards C ConductorShip & orchestrate agents