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 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 `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 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` `deploy/packaging/` holds the Linux packaging: `packager.toml`
(cargo-packager: `.deb`, AppImage, the pacman tarball with its PKGBUILD), (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 `.github/workflows/release.yml` builds them on a version tag. The macOS
bundle is `packager-macos.toml`, `Info.plist` and `build-macos.sh` (an 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). 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) ## 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 codes), backup: { last_at, through_seq, stale }, prompts: [{ name, args
}], needs_attention: { items: [{ id, severity, kind, title, tool?, args? }], needs_attention: { items: [{ id, severity, kind, title, tool?, args?
}], counts: { kind: n } }, truncated?, handbook_sections, }], 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 prompt, args?, why, required }], isolation }`. The connect text names it
as the first call. `book.url` is the book's path on this server 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 (`/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 (below); its next step comes first, before anything else, at every
start until it is done: `books.handbook {section: "format-upgrade"}`, start until it is done: `books.handbook {section: "format-upgrade"}`,
"Tell the human first: …". "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 `isolation` is `same_user (advisory)` locally (D-32): the books are
hidden from agents in the owner's own account, not locked against them. 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 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. 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 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 the program inside a signed `.app` breaks its signature (it will: step 5
must decide, probably by updating a whole `.app`); the translocated-app must decide, probably by updating a whole `.app`). **Translocation
check (`translocated_app_refuses_to_connect`) is not built. (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) ## 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 `update --check` says "already downloaded". A local release is named by
`<root>/updates/feed` (a `file://` address, then the key file). Without a `<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 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 log why.
viewer's notice, which need an admin-socket intent and a place in the
viewer (see the unit's As-built block). 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 ## 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 (`failed_update_rolls_back`), `install_kind` (the marker test), and
`parse_version`'s rejections. `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) ## P5: the agent surface (2026-10-29)
Every tool result carries the envelope (`next_steps`, `guards`, `warnings`, Every tool result carries the envelope (`next_steps`, `guards`, `warnings`,
+2 -2
View File
@@ -1645,7 +1645,7 @@ Each unit follows the 8.0.4 template. "A-n" is an owner answer numbered in 8.3.1
#### P11a: packaging and updates (D-19, D-22, D-24, D-25, D-26) #### P11a: packaging and updates (D-19, D-22, D-24, D-25, D-26)
1. **Status:** in progress: step 0 landed 2026-10-29 (`5e220cca`) as far as the macOS store and the portable CI job; its keyring stores are removed by U0e (D-63), and the Windows half of step 0 moved to P11c (D-62). Steps 1 to 7 open, macOS and Linux first. P11 was split 2026-10-26 (D-54): P11a is installers and updates; P11b the agent dock and books homes. 1. **Status:** in progress: steps 0-4 and the macOS half of step 6 landed 2026-10-29 (the AppImage restart, the daily staged update, the unsigned `.dmg` job); the in-app accept, the viewer's update notice and "Check for updates", and the translocation refusal landed 2026-11-05. Step 5 (signing) is deferred by D-64; left: the release workflow's first run (the owner's click), the Windows installer (P11c's), the trials on a Mac and a Windows machine. P11 was split 2026-10-26 (D-54): P11a is installers and updates; P11b the agent dock and books homes.
2. **Depends on:** none in code. From the owner before the signing steps: the Apple Developer ID certificate and notarization credentials as CI secrets; the Windows signing identity (Azure Artifact Signing if eligible, else an OV certificate); GitHub Releases enabled on the public mirror; the release hardware token; clean VMs or runners for the install tests. Q-4 (keep the two-key release manifest of D-25, or minisign alone) before step 5. 2. **Depends on:** none in code. From the owner before the signing steps: the Apple Developer ID certificate and notarization credentials as CI secrets; the Windows signing identity (Azure Artifact Signing if eligible, else an OV certificate); GitHub Releases enabled on the public mirror; the release hardware token; clean VMs or runners for the install tests. Q-4 (keep the two-key release manifest of D-25, or minisign alone) before step 5.
3. **Feature and schema:** none. 3. **Feature and schema:** none.
4. **Build:** 4. **Build:**
@@ -1660,7 +1660,7 @@ Each unit follows the 8.0.4 template. "A-n" is an owner answer numbered in 8.3.1
5. **Files:** owns `crates/ledger-host/src/update.rs`, `deploy/packaging/*`, `.github/workflows/release.yml`, `docs/packaging.md`; touches `commands.rs`, `crates/ledger-gui/src/owner.rs`, `mise.toml`. 5. **Files:** owns `crates/ledger-host/src/update.rs`, `deploy/packaging/*`, `.github/workflows/release.yml`, `docs/packaging.md`; touches `commands.rs`, `crates/ledger-gui/src/owner.rs`, `mise.toml`.
6. **Tests and proofs:** named above. 6. **Tests and proofs:** named above.
7. **Done when:** (1) on a clean Linux VM the AppImage installs, opens, onboards and posts an entry with no terminal and no admin password, and updates itself from a test feed; (2) on Arch, Debian and Fedora the packages install and upgrade with the head unchanged; (3) an update leaves every book's head and goldens unchanged and a bridge session carries on across it; (4) a backup made on one system restores on another; (5) macOS and Windows: the same as (1), once the signing prerequisites exist (record which are done in the As-built block); (6) `just check` green. 7. **Done when:** (1) on a clean Linux VM the AppImage installs, opens, onboards and posts an entry with no terminal and no admin password, and updates itself from a test feed; (2) on Arch, Debian and Fedora the packages install and upgrade with the head unchanged; (3) an update leaves every book's head and goldens unchanged and a bridge session carries on across it; (4) a backup made on one system restores on another; (5) macOS and Windows: the same as (1), once the signing prerequisites exist (record which are done in the As-built block); (6) `just check` green.
8. **As built:** (steps 0 to 3 as far as stated, 2026-10-29; steps 4 to 7 are later waves.) **Step 0, partly.** Built: the macOS login Keychain store (`apple-native-keyring-store` 1.0.2, feature `keychain`) and the Windows Credential Manager store (`windows-native-keyring-store` 1.1.0) behind `tokens.rs::Keeper`, on the same `Items` trait and the same laws as the Secret Service (`os` modules per OS; shared `refused`, `make_in`, `read_item`, `remove_item`); `NOT_YET` now only for other OSes; the ignored `os_keyring_keeps_and_forgets` compiles on all three; the `portable` job in `.github/workflows/check.yml` (`macos-latest` gating, `windows-latest` allowed to fail) builds `ledger-cli --no-default-features`, `cargo build --workspace --all-targets`, `cargo test --workspace` and the ignored keyring test. Checked here: Linux only (fmt, clippy, the tokens tests); no macOS/Windows target is installed, so both stores and the job are proven only by the first CI run. **Not built, and not a `cfg` gap: the Windows host.** Windows data root from the known-folder API with an owner-only DACL, named pipes with a SID check, `flock` and the Unix-only `host`, `start`, `bridge`, `opener` and `local`/`admin` sockets are a port of several crates (inventory in `docs/packaging.md`); macOS needs the CI run to show which Linux-only items (`seccomp` in `refuse.rs`, `renameat2`, `close_range`) need fences. So the step 0 Done-when ("setup ... gui work on each OS") is open for both. **Step 1 (the trial): not run in that wave**: it needs clean macOS, Windows and Linux machines; `mise.toml` now pins `cargo-packager` 0.11.8, `cargo-packager-updater` 0.2.3, `cargo-generate-rpm` 0.21.1 and `docs/packaging.md` holds the empty table and what is known. **Steps 1 (Linux half), 2 and 3 (2026-10-29, branch `P11a`).** *Pins:* the EM removed step 0's early pins; `mise.toml` now pins `cargo-packager` 0.11.8 and `cargo-generate-rpm` 0.21.1, which `just package-linux` calls; **`cargo-packager-updater` is a library, not a tool, so it is not pinned** (the plan's three-tool list was wrong on that); `update.rs` verifies with `minisign-verify` 0.3 (the crate the updater uses) and the tests sign with the `minisign` 0.10 dev-dependency. *Step 1:* `docs/packaging.md` has the Linux rows filled from real builds (47 MB .deb, 36 MB .rpm, 44 MB AppImage, the pacman tarball and PKGBUILD) and eight findings; the `.dmg` and NSIS rows stay "(trial)" (need a Mac and Windows; Windows is P11c). The findings that shaped the build: a binary linked on this machine (glibc 2.44) does not start on Debian 13, so packages are built on `ubuntu-24.04` in CI and in `rust:1.99-trixie` locally (`just release-trixie`); the books refuse to run under Docker's default seccomp filter (tests use `seccomp=unconfined`); an AppImage's launcher appends arguments to the desktop file's `Exec`, so the desktop files say plain `wanderbooks` and a bare launch with no terminal is the viewer (`main.rs::is_bare_launch`); the AppImage needs a square icon (**a placeholder PNG is committed; the owner should replace it**). *Step 2:* `crates/ledger-host/src/update.rs` (`update`, `rollback`) and the two CLI modes: a manifest (version; per platform `url`, `sha256`, `signature` = base64 of the minisign `.sig`, the shape cargo-packager signs in), accepted only if higher, sha256 equal AND signature valid against `WANDERBOOKS_RELEASE_PUBKEY` (compiled in; **no build carries one until step 5**, so today a build updates only from a `file://` feed with `--trusted-key`); stage under `<root>/updates/<version>/`, keep the running version under `updates/previous/` with the journal format it knew, swap by renaming a complete copy made beside the target, `--help` health check with automatic put-back; `rollback` refuses when a book declares a format or feature the kept version lacks; a package marks itself with `share/wanderbooks/install-method` and then both refuse. The restart is the host's existing one (nothing new in `host.rs`). Tests: `crates/ledger-cli/tests/update.rs` (`update_is_atomic`, `failed_update_rolls_back`, `unsigned_update_refused`, `bridge_survives_update`, `update_waits_for_a_running_call`, `package_managed_install_refuses_update`, `rollback_refuses_after_a_book_consented_to_a_newer_format`, `an_update_replaces_the_program_keeps_the_old_one_and_rollback_brings_it_back`) all pass (8 in 12 s); five unit tests in `update.rs`. *Step 3:* `deploy/packaging/` (`packager.toml`, the rpm table in `crates/ledger-cli/Cargo.toml`, `aur/PKGBUILD`, `build-linux.sh`, `repo-indexes.sh`, `local-release.sh`, `test-linux-packages.sh`), the `just` recipes `package-linux`, `local-release`, `release-trixie`, `test-linux-packages`, and `.github/workflows/release.yml` (Linux packages on a `v*` tag, attached to the release, apt and dnf indexes pushed to `gh-pages`; untested, GitHub only). `linux_packages_install_and_upgrade` is `test-linux-packages.sh`: install 0.1.0 from an apt, a dnf repository and `makepkg` over the AUR PKGBUILLine truncated 8. **As built:** (steps 0 to 3 as far as stated, 2026-10-29; steps 4 to 7 are later waves.) **Step 0, partly.** Built: the macOS login Keychain store (`apple-native-keyring-store` 1.0.2, feature `keychain`) and the Windows Credential Manager store (`windows-native-keyring-store` 1.1.0) behind `tokens.rs::Keeper`, on the same `Items` trait and the same laws as the Secret Service (`os` modules per OS; shared `refused`, `make_in`, `read_item`, `remove_item`); `NOT_YET` now only for other OSes; the ignored `os_keyring_keeps_and_forgets` compiles on all three; the `portable` job in `.github/workflows/check.yml` (`macos-latest` gating, `windows-latest` allowed to fail) builds `ledger-cli --no-default-features`, `cargo build --workspace --all-targets`, `cargo test --workspace` and the ignored keyring test. Checked here: Linux only (fmt, clippy, the tokens tests); no macOS/Windows target is installed, so both stores and the job are proven only by the first CI run. **Not built, and not a `cfg` gap: the Windows host.** Windows data root from the known-folder API with an owner-only DACL, named pipes with a SID check, `flock` and the Unix-only `host`, `start`, `bridge`, `opener` and `local`/`admin` sockets are a port of several crates (inventory in `docs/packaging.md`); macOS needs the CI run to show which Linux-only items (`seccomp` in `refuse.rs`, `renameat2`, `close_range`) need fences. So the step 0 Done-when ("setup ... gui work on each OS") is open for both. **Step 1 (the trial): not run in that wave**: it needs clean macOS, Windows and Linux machines; `mise.toml` now pins `cargo-packager` 0.11.8, `cargo-packager-updater` 0.2.3, `cargo-generate-rpm` 0.21.1 and `docs/packaging.md` holds the empty table and what is known. **Steps 1 (Linux half), 2 and 3 (2026-10-29, branch `P11a`).** *Pins:* the EM removed step 0's early pins; `mise.toml` now pins `cargo-packager` 0.11.8 and `cargo-generate-rpm` 0.21.1, which `just package-linux` calls; **`cargo-packager-updater` is a library, not a tool, so it is not pinned** (the plan's three-tool list was wrong on that); `update.rs` verifies with `minisign-verify` 0.3 (the crate the updater uses) and the tests sign with the `minisign` 0.10 dev-dependency. *Step 1:* `docs/packaging.md` has the Linux rows filled from real builds (47 MB .deb, 36 MB .rpm, 44 MB AppImage, the pacman tarball and PKGBUILD) and eight findings; the `.dmg` and NSIS rows stay "(trial)" (need a Mac and Windows; Windows is P11c). The findings that shaped the build: a binary linked on this machine (glibc 2.44) does not start on Debian 13, so packages are built on `ubuntu-24.04` in CI and in `rust:1.99-trixie` locally (`just release-trixie`); the books refuse to run under Docker's default seccomp filter (tests use `seccomp=unconfined`); an AppImage's launcher appends arguments to the desktop file's `Exec`, so the desktop files say plain `wanderbooks` and a bare launch with no terminal is the viewer (`main.rs::is_bare_launch`); the AppImage needs a square icon (**a placeholder PNG is committed; the owner should replace it**). *Step 2:* `crates/ledger-host/src/update.rs` (`update`, `rollback`) and the two CLI modes: a manifest (version; per platform `url`, `sha256`, `signature` = base64 of the minisign `.sig`, the shape cargo-packager signs in), accepted only if higher, sha256 equal AND signature valid against `WANDERBOOKS_RELEASE_PUBKEY` (compiled in; **no build carries one until step 5**, so today a build updates only from a `file://` feed with `--trusted-key`); stage under `<root>/updates/<version>/`, keep the running version under `updates/previous/` with the journal format it knew, swap by renaming a complete copy made beside the target, `--help` health check with automatic put-back; `rollback` refuses when a book declares a format or feature the kept version lacks; a package marks itself with `share/wanderbooks/install-method` and then both refuse. The restart is the host's existing one (nothing new in `host.rs`). Tests: `crates/ledger-cli/tests/update.rs` (`update_is_atomic`, `failed_update_rolls_back`, `unsigned_update_refused`, `bridge_survives_update`, `update_waits_for_a_running_call`, `package_managed_install_refuses_update`, `rollback_refuses_after_a_book_consented_to_a_newer_format`, `an_update_replaces_the_program_keeps_the_old_one_and_rollback_brings_it_back`) all pass (8 in 12 s); five unit tests in `update.rs`. *Step 3:* `deploy/packaging/` (`packager.toml`, the rpm table in `crates/ledger-cli/Cargo.toml`, `aur/PKGBUILD`, `build-linux.sh`, `repo-indexes.sh`, `local-release.sh`, `test-linux-packages.sh`), the `just` recipes `package-linux`, `local-release`, `release-trixie`, `test-linux-packages`, and `.github/workflows/release.yml` (Linux packages on a `v*` tag, attached to the release, apt and dnf indexes pushed to `gh-pages`; untested, GitHub only). `linux_packages_install_and_upgrade` is `test-linux-packages.sh`: install 0.1.0 from an apt, a dnf repository and `makepkg` over the AUR PKGBUILLine truncated
#### P11b: the agent dock and books homes (D-27) #### P11b: the agent dock and books homes (D-27)