docs: record the in-app accept, the update notice and translocation (P11a)
check / check (push) Waiting to run
portable-macos / portable (macos-latest) (push) Waiting to run
portable-windows / portable (windows-latest) (push) Waiting to run
two-user / two-user (push) Waiting to run

The interface spec gains `books.start`'s allocated `update_waiting` field
and says only the owner can accept it. The packaging and architecture
notes now describe the lease intent, the viewer's notice and "Check for
updates", and the translocated-app refusal instead of listing them as not
built. The testing record names each new test and the mutation targets it
gives P12. The plan's P11a status and As-built block record what landed
and what still waits: the Mac trial, the Windows installer (P11c's) and
signing (D-64).
This commit is contained in:
swaits committed 2026-11-05 08:29:20 -07:00
1 parent e2754898d8
commit 7b1774512b
5 files changed
+98 -9

No files matched your search

+15 -1
View File
@@ -417,7 +417,19 @@ downgrading is theirs). Anything else (an AppImage, whose file is named by
call is cut off), after a second check against the key, or by
`wanderbooks update`, the owner's say-so, which reuses the staged copy. A
local release is named in `<root>/updates/feed` (a `file://` address, then
the key file), the same rule as `--trusted-key`.
the key file), the same rule as `--trusted-key`;
- **the owner accepts one in the app**: the viewer reads the staged
`meta.json` (`Owner::update_waiting`) and shows a notice with "Update
now", and Books has "Check for updates" (`Owner::check_for_updates`) and
"Update to X"; accepting is the host's lease intent `apply_update`, which
installs the staged copy as `update` does and lets the host's own
replaced-program watch restart onto it once no call runs. `books.start`
carries the same notice to an agent as `update_waiting`, with a next step
to tell the human; no tool accepts it;
- **a translocated macOS app refuses to connect agents**
(`start::translocation_refusal`): a quarantined download runs from a
read-only copy under `AppTranslocation` that vanishes with the app, so
the person moves it out of Downloads first.
`deploy/packaging/` holds the Linux packaging: `packager.toml`
(cargo-packager: `.deb`, AppImage, the pacman tarball with its PKGBUILD),
@@ -428,6 +440,8 @@ throwaway-keyed feed on this machine) and `test-linux-packages.sh`.
`.github/workflows/release.yml` builds them on a version tag. The macOS
bundle is `packager-macos.toml`, `Info.plist` and `build-macos.sh` (an
unsigned `.dmg` from CI's `macos` job; signing and notarization are step 5).
A translocated app (Gatekeeper's copy of a quarantined download) may run
but refuses to connect an agent, saying how to move it out of Downloads.
## The result envelope and the guards (`ledger-mcp`, P5)
+8 -1
View File
@@ -124,7 +124,8 @@ settings-and-conventions section says how to use them.
codes), backup: { last_at, through_seq, stale }, prompts: [{ name, args
}], needs_attention: { items: [{ id, severity, kind, title, tool?, args?
}], counts: { kind: n } }, truncated?, handbook_sections,
format_upgrade?: { guard, format, adds, how }, next_steps: [{ tool |
format_upgrade?: { guard, format, adds, how }, update_waiting?: { version,
staged_at, how }, next_steps: [{ tool |
prompt, args?, why, required }], isolation }`. The connect text names it
as the first call. `book.url` is the book's path on this server
(`/b/ID/mcp`), never a path on a disk. `you` is the token's own facts
@@ -145,6 +146,12 @@ settings-and-conventions section says how to use them.
(below); its next step comes first, before anything else, at every
start until it is done: `books.handbook {section: "format-upgrade"}`,
"Tell the human first: …".
`update_waiting` is there only while the host has downloaded and checked
a newer version of wanderbooks (`<root>/updates/staged/`): its version,
when it was staged (UTC, RFC 3339) and how it may be accepted. Only the
owner can accept it, in the app (Books, then Update) or with
`wanderbooks update` at a terminal; no tool can. Its next step asks the
agent to tell the human, and nothing changes until they accept.
`isolation` is `same_user (advisory)` locally (D-32): the books are
hidden from agents in the owner's own account, not locked against them.
On the owner's own machine, where the server runs as a separate account
+28 -5
View File
@@ -135,8 +135,17 @@ subset. **Not checked: that the `.dmg` builds, mounts or runs**: that needs
the CI run (Actions, release, Run workflow, this branch) and then a Mac.
Open items for the trial on a Mac: whether `wanderbooks update` replacing
the program inside a signed `.app` breaks its signature (it will: step 5
must decide, probably by updating a whole `.app`); the translocated-app
check (`translocated_app_refuses_to_connect`) is not built.
must decide, probably by updating a whole `.app`). **Translocation
(P11a step 6):** a `.dmg` downloaded from the internet keeps its
quarantine bit, so Gatekeeper may run the app from a read-only copy under
`/private/var/folders/…/AppTranslocation/…` that goes away when it
closes. `wanderbooks connect` (and the viewer's Connect, which is the same
function) refuses there, before a token is made or an agent's list is
touched, and says to move the app out of Downloads and open it again:
`ledger_host::start::translocation_refusal` reads the path the program
would give the agent. Checked on Linux by standing in for that path
(`translocated_app_refuses_to_connect`, `only_a_translocated_path_refuses_an_agent`);
the real Gatekeeper copy still needs the trial on a Mac.
## Updates staged by the host (step 4)
@@ -148,9 +157,23 @@ first; `wanderbooks update` applies it too and reuses the staged copy;
`update --check` says "already downloaded". A local release is named by
`<root>/updates/feed` (a `file://` address, then the key file). Without a
compiled-in release key and without that file, the look does nothing but
log why. Not built: the in-app "restart now to update" choice and the
viewer's notice, which need an admin-socket intent and a place in the
viewer (see the unit's As-built block).
log why.
The owner accepts a staged update **in the app** (P11a step 4). The
viewer reads `<root>/updates/staged/meta.json` every few seconds
(`Owner::update_waiting`) and shows a standing notice above the page,
"Version X of wanderbooks is ready", with "Update now"; Books also has
"Check for updates" (`Owner::check_for_updates`, which looks at the feed
at once whatever the daily look did) and "Update to X" while one waits.
Accepting sends the host's lease intent `apply_update`; the host installs
the staged copy exactly as `wanderbooks update` does and its existing
watch for a replaced program (host.rs INVARIANT 7) restarts onto it once
no call is running, the books staying open and every holder's session
carrying on. With no host running there is no call to cut off, so the
viewer applies it itself. `books.start` tells an agent the same thing
(`update_waiting`) and adds a next step to tell the human; no tool can
accept it. The accepted update replaces the program file, not a whole
`.app`, which step 5 must revisit for a signed bundle.
## What `update` and `rollback` need from a release
+45
View File
@@ -3823,6 +3823,51 @@ add), `swap_in`'s temporary-then-rename (`update_is_atomic`), `healthy`
(`failed_update_rolls_back`), `install_kind` (the marker test), and
`parse_version`'s rejections.
## P11a steps 4 and 6: the in-app accept, the viewer's notice, translocation (2026-11-05)
The accept is the host's lease intent `apply_update` (the name
`plan/served.md` 8.0.3 allocates); the viewer's names are
`Owner::update_waiting`, `Owner::check_for_updates` and `Owner::apply_update`,
and the agent's is `books.start`'s `update_waiting`.
- `crates/ledger-cli/tests/update.rs` (Linux):
`books_start_says_an_update_waits` — a quiet `books.start` has no
`update_waiting`; after the host stages a signed 9.0.0, `books.start`
names its version and staged time and says only the owner accepts it,
its next step says to tell the human, and no path appears in the answer;
`the_owner_accepts_a_staged_update_in_the_app` — accepting with nothing
staged is refused and changes nothing; once 9.0.0 is staged, the same
intent (`opener::ask_once_within` with `Intent::ApplyUpdate`, what the
viewer's `apply_update` sends) installs it, the program becomes the
release's bytes, the old version is kept under `updates/previous/`, and
an agent's bridge writes all through the host's restart onto it: every
account is in the chart exactly once.
- `crates/ledger-gui/tests/update_notice.rs`:
`the_menu_checks_for_updates_and_a_waiting_update_shows_a_notice_that_accepts`
(Books, Check for updates asks the owner and its words show; the standing
notice names the version; Update now asks the owner to apply it, and the
notice goes once it is installed) and
`books_outside_a_data_root_have_no_update_notice`.
- `crates/ledger-cli/tests/translocation.rs`:
`translocated_app_refuses_to_connect` — with the program's path standing
in for Gatekeeper's copy (`APPIMAGE` names a file under
`AppTranslocation`), `connect claude` is refused before a token is made
or a home is made, and the stand-in agent never runs; the same program at
an ordinary path connects. `crates/ledger-host/src/start.rs`:
`only_a_translocated_path_refuses_an_agent` (the marker is a path part,
not letters in a name).
Not checked here: the real Gatekeeper copy and the `.dmg` (needs a Mac,
the owner's trial); the Windows installer job is P11c's, not on this branch.
**For P12 (mutation targets, none run, D-33):** `update_waiting`'s
"newer than this program" comparison (a mutant that offers the running
version must die on `books_start_says_an_update_waits`'s quiet first
call), `staged_update_in`'s plausibility check (a staged version with a
path separator must not become a notice), and `translocation_refusal`'s
component comparison (a part that merely contains the word must not
refuse).
## P5: the agent surface (2026-10-29)
Every tool result carries the envelope (`next_steps`, `guards`, `warnings`,