From 7ce5e90ca2de2e110910468f85aed07be4edd6e9 Mon Sep 17 00:00:00 2001 From: August Cayzer Date: Sun, 25 Oct 2026 15:03:33 +0000 Subject: [PATCH] docs(swift-sdk): say a replaced refused copy keeps its file budget --- content/docs/(overview)/devices.mdx | 2 +- content/docs/(reference)/swift-sdk.mdx | 3 ++- content/docs/run/troubleshooting.mdx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/content/docs/(overview)/devices.mdx b/content/docs/(overview)/devices.mdx index 66b0e473..30feecbb 100644 --- a/content/docs/(overview)/devices.mdx +++ b/content/docs/(overview)/devices.mdx @@ -37,7 +37,7 @@ The hydration **empties the copy first** if the instance's event log now ends be **For any other reason, the copy refuses reads and writes until a hydration, which empties it first.** Examples are another instance answering at the same address, or a credential whose reach changed. -A copy made by an earlier build of the core can have a different shape. The device **refuses to open it** with `wrong_schema`, deletes nothing, and says how many writes the file holds that the server hasn't taken, because only the build that made it can send them. If some are of a kind the current build no longer sends, it names the kind. [Troubleshooting](/run/troubleshooting#staying-in-step) says what to do. An app built on the core's Node or Swift binding can replace a refused copy that holds nothing unsent with an empty one; the files the copy downloaded stay. A [folder](/run/sync-a-folder#move-to-a-newer-cli) can replace its own copy itself. +A copy made by an earlier build of the core can have a different shape. The device **refuses to open it** with `wrong_schema`, deletes nothing, and says how many writes the file holds that the server hasn't taken, because only the build that made it can send them. If some are of a kind the current build no longer sends, it names the kind. [Troubleshooting](/run/troubleshooting#staying-in-step) says what to do. An app built on the core's Node or Swift binding can replace a refused copy that holds nothing unsent with an empty one; the files the copy downloaded and the file budget's setting stay. A [folder](/run/sync-a-folder#move-to-a-newer-cli) can replace its own copy itself. **An app can remove its working copy, with the unsent writes the person agreed to lose.** An app passes the number of writes the server hasn't taken that it showed the person, through the core's Node binding (`MarfaCore.removeStore`). The device removes the copy only when the copy holds exactly that number, whatever build made it: the file, its logs, the files it downloaded and its lock, and nothing else beside them. A symbolic link at one of those names is removed, never what it leads to. If the copy holds another number, or one the device can't read, the device refuses with `unsent_differs`, says the copy's own number, or that it can't read one, and leaves the copy, its logs, its downloaded files and its lock as they were. It refuses with `reading_handle` while anything else writes the copy, the app's own open copy included, so the app closes its copy first. A helper process that only reads the copy doesn't stop the removal, and keeps showing the old copy until it reopens. A folder's copy goes only with the folder, so the device refuses it with `invalid`. A path with no copy holds no unsent write, so removing it again finishes a removal that stopped part way. diff --git a/content/docs/(reference)/swift-sdk.mdx b/content/docs/(reference)/swift-sdk.mdx index bbe6e174..444b85af 100644 --- a/content/docs/(reference)/swift-sdk.mdx +++ b/content/docs/(reference)/swift-sdk.mdx @@ -63,7 +63,8 @@ let copy = try await WorkingCopy.open(store: storeURL, server: Server(url: serve A store that another build of the core made throws `wrongSchema` from `open`, and nothing is deleted. Its `unsent` count is the number of writes the instance has not taken. **When it is `0`**, `WorkingCopy.replaceRefused(store:server:fileBudget:)` replaces the file with an empty one and opens it. Hydrate the new copy as you would a new one. - **The files the copy downloaded stay.** The core keeps them by content, beside the store, not in it. -- **What only the old store held is gone:** pins, the position in the instance's events, declared types and the file budget's setting. Pass `fileBudget` to set it again; otherwise it is the default. +- **The file budget's setting stays.** The new store has the setting the old one recorded. If the old store records none, or none the core can read, the budget is the default and the replacement still succeeds. Pass `fileBudget` to choose another; if it can't be set once the store is replaced, the call throws `store` and says so, and you can set it with `setBudget(_:)` after you open the copy again. +- **What only the old store held is gone:** pins, the position in the instance's events and declared types. - **A store that holds an unsent write is left as it is**, and the call throws `wrongSchema` with the count. A store that opens as it is, or a path with no store, throws `invalid`. [Troubleshooting](/run/troubleshooting#staying-in-step) covers what to do with unsent writes. ## Reach a plain http server diff --git a/content/docs/run/troubleshooting.mdx b/content/docs/run/troubleshooting.mdx index 6ed5fa1d..60f74cf9 100644 --- a/content/docs/run/troubleshooting.mdx +++ b/content/docs/run/troubleshooting.mdx @@ -278,7 +278,7 @@ A working copy is a file that one build of the core made, and a build with a dif The message says how many writes the file holds that the server has not taken: waiting, blocked, refused or dead. Only the build that made the file can send or show them, so what you do depends on that count: -- **None.** Every write the file holds reached the server. Run `marfa device hydrate` to build a new working copy. An app built on the core's Node binding can call `replaceRefused` instead, and one built on the [Swift SDK](/swift-sdk#replace-a-refused-copy) can call `WorkingCopy.replaceRefused(store:server:fileBudget:)`: it replaces the old file with an empty copy, so what only the old file held, such as pins, is lost. The files the copy downloaded stay. +- **None.** Every write the file holds reached the server. Run `marfa device hydrate` to build a new working copy. An app built on the core's Node binding can call `replaceRefused` instead, and one built on the [Swift SDK](/swift-sdk#replace-a-refused-copy) can call `WorkingCopy.replaceRefused(store:server:fileBudget:)`: it replaces the old file with an empty copy, so what only the old file held, such as pins, is lost. The files the copy downloaded and the file budget's setting stay. - **Some.** Open the file with the build that made it, run `marfa device drain` to send the writes, and use `marfa device discard` on any it refuses that you don't want. Then hydrate a new copy with the newer build. Keep the old file until you have done this, because deleting it loses those writes. An app that lets a person discard the copy instead can remove it through the core's Node binding (`MarfaCore.removeStore`), passing the count the person agreed to lose. The core removes the file, its logs, its downloaded files and its lock only if the file still holds exactly that count, and otherwise refuses with `unsent_differs` and the file's own count. [Devices](/devices#the-working-copy) explains the removal.