Skip to content

An installed app sees its own package read-only and its own folder as HOME, and nothing else of /apps or /home - #807

Merged
Japabu merged 5 commits into
mainfrom
wt/toyos-appgrants
Oct 9, 2026
Merged

Japabu merged 5 commits into
mainfrom
wt/toyos-appgrants

Conversation

@Japabu

@Japabu Japabu commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Stage I1: stage 2 of the layout track, plus the /apps slice of stage 2 of the isolation track. An installed app now sees its own package read-only and its own folder as HOME. Of what the file servers serve, it sees nothing else of /apps or /home, and nothing of /config, /state, /log or /boot. It still reaches /tmp and /system, which the kernel serves to every program.

Before this change, every /apps launch was minted every directory of every role read-write, with HOME=/home/toy. That meant an installed app could write all of /apps, /home, /config, /state and /log.

This lands a regression for gbae's ROM browsing. Under this view gbae, started from the desktop, can browse to no ROM. A ROM given on its command line loads only from its own folder. This was measured, not guessed: see gbae below. It is recorded as issues/an-installed-gbae-browses-to-no-rom.md, and its exit is the file picker of the package track's stage 6 (issues/a-package-is-a-directory-under-apps-and-the-installer-is-a-program.md). The grant was not widened for it.

Head: 0ad87a593, on d6298c83e (main with #800, #802 and #803 merged in).

What changed, per decision

  • A grant carries its access (toyos/src/fs.rs).
    • Grant gains Access::{ReadOnly, ReadWrite}.
    • The badge format goes to GRANT_VERSION = 3, with one access byte between the share and the root, so MAX_GRANT_ROOT drops from 55 to 54.
    • An access byte other than 0 or 1 decodes to no grant. It is never read as either access.
  • fileserver enforces the access on the server side (userland/fileserver).
    • At accept, a connection's writes is the grant's access ANDed with the volume's own writability. Both come from the kernel-stamped badge and the server's own volume, never from the client.
    • fileserver::rights::changes classifies every request on the wire: an open by its flags, ten requests that only read, and eight that change (WRITE, TRUNCATE, MKDIR, RMDIR, UNLINK, RENAME, SYMLINK, STREAM).
    • A request that changes is refused PermissionDenied on a connection that may not write, before the volume sees it.
    • A request number the wire does not have is None. Its client is let go on every connection, read-only or not, with the same words as before (NO_SUCH_OPERATION).
    • HELLO's RIGHT_WRITE answers the access and the volume together.
    • The wire has no request that changes metadata, so there is nothing more to refuse.
  • A row's view is declared in one place: toyos_manifest::Program::view.
    • A package row gets exactly two DATA grants: apps/<name> read-only, and home/toy/Apps/<name> read-write.
    • Every other row gets whole_tree(), every directory of every role read-write, as before.
    • The supervisor mints per View. A compile-time assertion holds the longest package folder (MAX_PROGRAM_NAME = 32, under home/toy/Apps/) within MAX_GRANT_ROOT, so mint and Grants::view cannot fail and expect that bound.
    • Storage rows still get no grants, and the supervisor's own namespace is still the whole tree.
  • A package named after a row the image declares is refused (Manifest::app_row, row_named).
    • A package's folder is /home/toy/Apps/<name>. The shell keeps its history in Apps/shell.
    • The supervisor says the refusal as the launch's reason: supervisor: launcher: the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder.
  • An app's HOME is its folder, and a launch without it is refused.
    • Program::home answers /home/toy/Apps/<name> for a package row.
    • Before a package's launch, the supervisor's file worker makes that folder and Config Data Cache State (toyos_manifest::APP_FOLDERS), and checks each is a directory (make_app_home).
    • If any one is not a directory, the launch is refused (MSG_REFUSED), and the supervisor names why. That includes the case where the file worker is still busy with an earlier call.
    • A service's own home keeps its old, logged path (make_home, unchanged from main).
    • The shell's row is not a package, so its HOME is still /home/toy.
  • [apps] keeps no DATA-wide grant at all. It is connectors only.
  • Write access to /apps is an open owner question. No package can write /apps, its own directory included. pkg, the shell, and every other declared row keep the whole tree read-write. Two of the owner's rulings pull against each other here, and they are filed as issues/whether-pkg-alone-writes-apps.md (kind: question), not decided.
  • A running swap across this change.
    • A fileserver swapped onto a supervisor of the other version reads every grant as no grant, and lets each connection go by name: its badge is no grant.
    • There is no compatibility layer, so this change lands by image.
    • The brief said to bump the version and to say what a swap does. Both are done.

gbae

gbae bfe8dabf8 (gh api repos/Japabu/gbae/tarball/HEAD), started with no ROM, browses std::env::current_dir() (src/main.rs:471) with src/menu.rs's list_directory. Its config is $HOME/.config/gbae/config.

The measurement was a guest run, under QEMU on tests/proctreecase, at 1c8490e4e. A temporary patch, posted, made the app_view package run gbae's list_directory verbatim from its cwd, its ROM load, and its config save. EXIT=0. The GBAE lines:

  • From cwd /, the menu lists /'s nine mount points. /system lists bin/ and etc/. Every other one lists only ../, including /home and /home/toy. Browsable ROMs: [].
    • The desktop launches with the compositor's cwd. I read that this is / in the code (a service gets the supervisor's cwd); I did not measure it.
  • From cwd /home/toy: the same, [].
  • load_rom(/home/toy/Downloads/measure.gba): NotFound. load_rom(/home/toy/Apps/appview/Data/measure.gba): Ok(3).
  • Config $HOME/.config/gbae/config: saved and read back in its own folder.

I did not run the release binary itself. gbae v0.2.0's ToyOS build was linked on 2026-09-04 against an ABI that has moved since. Its menu is drawn in a window, which no harness here reads.

Checks: this is a security boundary

Negative control. The whole production change (toyos/src/fs.rs, userland/fileserver, userland/supervisor, toyos-manifest, system.toml) was reverted onto d6298c83e, the base the green arm G0 was measured on. The test, tests/proctreecase and the QEMU harness were kept.

  • app_view is red, EXIT=1, and names each hole:
    • home_dir() is /home/toy;
    • the app holds fs:/apps, fs:/home, fs:/config, fs:/state, fs:/log and fs:/boot;
    • it reads /apps/other/kept and /home/toy/Apps/other/Data/kept;
    • std's write into its package is answered Ok;
    • /apps, /home, /home/toy, /home/toy/Apps, /state and /log list their contents;
    • the package named shell ran.
  • The first run of the control panicked while setting up, before it named anything: the package named shell ran and left the app's folder behind, so no file could be planted there. The job now names that as red (0ad87a593), and the control was run again.

Mutations. Each was applied as a checked patch, run, and restored in the same script, leaving the tree clean. All of them, and the control, were run at 0ad87a593. Patches, script and logs are posted as comments.

mutation where it should show result
H1: RENAME counted as a read fileserver::rights host test red, EXIT=101 (request 15 writes)
H2: any access byte decodes as read-write toyos::fs host test red, EXIT=101 (access 2)
H3: a package's own directory minted writable toyos-manifest host test red, EXIT=101
H4: a package named after a row allowed toyos-manifest host test red, EXIT=101
H5: an unknown request counted as a write fileserver::rights host test red, EXIT=101 (request 0)
G0: harness alone, no mutation app_view under QEMU green, EXIT=0, every arm held
G1: fileserver ignores the grant's access app_view under QEMU red, EXIT=1; every write op named, plus fs:/apps/appview says it is writable
G2: supervisor mints every directory read-write app_view under QEMU red, EXIT=1; the same names
G3: a launch goes ahead without its folder app_view under QEMU red, EXIT=1; a package whose folder is a file ran
G4: a package named after a row allowed app_view under QEMU red, EXIT=1; a package named after the shell's row ran, and it held /home/toy/Apps/shell as HOME
NC: the whole change reverted app_view under QEMU red, EXIT=1; the names listed above

Independent oracle. None exists for this boundary: no external specification, differential implementation or third-party checker. The guest test spells every expected path from the owner's layout ruling (issues/where-everything-lives.md). It does not ask toyos-manifest, so it does not read back the code the supervisor reads. The metal judge reads row_named for the supervisor's refusal line, as launch_authority's judge reads launch::refused.

Tests

  • Host tests:
    • toyos::fs: a grant round-trips with both accesses; versions 2 and 4 are refused; access bytes 2, 0x80 and 0xff are refused.
    • fileserver::rights: every request number from 1 to 19 is classified; an open is classified by its flags; an unknown number is None.
    • toyos-manifest: a package's view, spelled out in full and at the longest package name; a package named after any declared row is refused by row_named, shell included; every declared row gets the whole tree; a package's HOME; the shell's HOME is unchanged.
  • app_view is a metal row on tests/proctreecase, as round 1 ruled: test_rs_app_view is a PROCTREECASE job.
    • The judge requires: the job passed; every arm held; the job's closing line; and the supervisor's two refusals, by name.
    • The job installs its own binary as the package appview, beside another package and another app's folder.
    • First, two launches are refused: the same binary installed as shell, and appview while a file stands where its folder goes.
    • Then it launches appview through the launcher. As the app, it checks:
      • home_dir() is its folder, with all four sub-folders, and a write there lands in /home;
      • its package reads, and every writing request on it is refused by the server, through toyos::fs::Dir past std, as is std's own write;
      • it holds none of the ten other fs: names, reads neither of the other files, and no kernel mount point lists anything.
    • The job then checks that the package is byte-for-byte what it installed.
    • No QEMU test registers it. It runs under QEMU only through the posted temporary harness, for the mutations and the control.

Gates (head 0ad87a593)

gate command exit
host cargo run -- --ci host 0 (78 steps, all green)
image cargo run -- --build-only 0
whole guest suite cargo test --test toyos-build 0 (41/41); uptime load averages 66.42 / 72.94 / 67.15 before, 37.04 / 63.91 / 65.66 after
T14 staging cargo test --test toyos-build -- --metal --metal-readback <dir> boot:testcases boot:proctreecase 2: three images staged (proctreecase 68e6a454…e5f450bb, testcases 3efa3bad…2629e4d4, testcases-watchdog 784e6e5c…274e44dd), with their sha256 in request.txt. Staging touched no machine; the orchestrator's reading of these images is below.

T14 at 0ad87a593, run by the orchestrator (comment 6084824923): all three images' hashes matched request.txt, each toyos-metal --fat32-check exit 0; judge EXIT=0, [metal] 255 passed, 0 failed, 3 boot(s); process_tree, launch_toctou, launch_authority, port_badge, fs_share, app_view PASS, with app_view's refusals and every arm held in its log; 137 C cases ccheck: 0; its badge is no grant 0 times. The earlier reading at bb091478f is superseded.

Net lines: git diff --shortstat origin/main...HEAD is 16 files, +753 −94.

  • Production: +247 −71.
  • Tests: +413 −19.
  • Issue files: +93 −4.

What I am unsure of

  • gbae's cwd from the desktop is read from the code, not measured. Its menu reaches no ROM from either cwd measured.
  • Desktop apps in the image (editor, paint, files, doom, …) are rows, not packages. They keep the session's HOME and the whole tree until isolation stage 2 gives every row a view.
  • A package's folder replaced by a symlink. The supervisor now refuses the launch: symlink_metadata says it is not a directory. No test plants a symlink; the planted file covers the same branch.
  • What made the app's folder in the control. On the reverted base, /home/toy/Apps/appview existed after the package named shell ran, and that run's write of Apps/appview/Data/kept was not refused. I did not trace which program made the folder. Nothing in this change rests on it.

🤖 Generated with Claude Code

https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C

… HOME, and nothing else of /apps or /home

A file grant carries its access: `toyos::fs::Grant` gains `Access`
(read-only or read-write), and the badge format goes to version 3, with
the access byte between the share and the root. fileserver refuses
`PermissionDenied`, before the volume sees it, every request that would
change what a read-only connection holds: an open to write, append,
truncate, create or create anew, WRITE, TRUNCATE, MKDIR, RMDIR, UNLINK,
RENAME, SYMLINK and STREAM. The list is `fileserver::rights::changes`,
closed by default, so a request the wire gains is refused on a read-only
connection until it is named as a read. HELLO's RIGHT_WRITE answers the
grant's access and the volume's together.

A row's view is `toyos_manifest::Program::view`. A package launched from
/apps is minted two grants on DATA: `apps/<name>` read-only, and
`home/toy/Apps/<name>` read-write; nothing of /config, /state, /log or
/boot, no other package and no other part of the home. Its HOME is that
folder (`Program::home`), which the supervisor makes with Config, Data,
Cache and State before every launch. `[apps]` keeps no directory of its
own: it is connectors only. Every row the image declares, pkg and the
shell among them, keeps the whole tree read-write, as the package track
rules the installer holds nothing a shell does not.

A grant the package's name puts past the badge is a refused launch, never
a supervisor panic.

A running swap of fileserver across this change is refused in effect: a
server of one side reads the other's grants as no grant and lets every
connection go by name. The change lands by image.

The `app_view` machine test boots tests/proctreecase, whose test-runner
row now lists /apps, installs itself as a package and launches it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Mutation and negative-control patches, each applied with git apply --check then git apply on head bb091478f, run, and restored with git checkout -- . in the same script (paths sanitized).

H1-rename-reads

diff --git a/userland/fileserver/src/rights.rs b/userland/fileserver/src/rights.rs
index 036a43ac8..468d0ca55 100644
--- a/userland/fileserver/src/rights.rs
+++ b/userland/fileserver/src/rights.rs
@@ -13,7 +13,7 @@ use toyos::fs::*;
 pub fn changes(op: u32, flags: u64) -> bool {
     match op {
         OPEN => flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0,
-        HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => false,
+        RENAME | HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => false,
         _ => true,
     }
 }

H2-any-access-byte-reads-write

diff --git a/toyos/src/fs.rs b/toyos/src/fs.rs
index 303fc2e93..3eccf459f 100644
--- a/toyos/src/fs.rs
+++ b/toyos/src/fs.rs
@@ -206,7 +206,7 @@ impl<'a> Grant<'a> {
         let access = match rest[8] {
             0 => Access::ReadOnly,
             1 => Access::ReadWrite,
-            _ => return None,
+            _ => Access::ReadWrite,
         };
         let root = core::str::from_utf8(&rest[9..]).ok()?;
         canonical(root).then_some(Self { share, access, root })

H3-package-dir-writable

diff --git a/toyos-manifest/src/lib.rs b/toyos-manifest/src/lib.rs
index 136603f47..1b3146277 100644
--- a/toyos-manifest/src/lib.rs
+++ b/toyos-manifest/src/lib.rs
@@ -291,7 +291,7 @@ impl Program {
     /// (`issues/every-program-sees-only-the-files-it-was-given.md`, stage 2).
     pub fn view(&self) -> Vec<View> {
         match self.package() {
-            Some(name) => vec![data_dir(package::Package::dir(name), false), data_dir(app_home(name), true)],
+            Some(name) => vec![data_dir(package::Package::dir(name), true), data_dir(app_home(name), true)],
             None => whole_tree(),
         }
     }

G1-fileserver-ignores-access

diff --git a/userland/fileserver/src/main.rs b/userland/fileserver/src/main.rs
index 528372ac7..2a61d9a8b 100644
--- a/userland/fileserver/src/main.rs
+++ b/userland/fileserver/src/main.rs
@@ -459,7 +459,7 @@ impl Server {
             rx: ipc::FrameRx::new(),
             root: grant.root.to_string(),
             share: grant.share,
-            writes: grant.access == Access::ReadWrite && self.volume.writable(),
+            writes: self.volume.writable(),
             window: None,
             fids: BTreeMap::new(),
             next_fid: 1,

G2-supervisor-mints-every-dir-writable

diff --git a/userland/supervisor/src/main.rs b/userland/supervisor/src/main.rs
index c5e50e840..2b7af7744 100644
--- a/userland/supervisor/src/main.rs
+++ b/userland/supervisor/src/main.rs
@@ -2298,7 +2298,7 @@ impl Grants<'_> {
 /// namespace name a program opens it under. A package's name is part of its
 /// directories', so one no grant carries is refused, by name.
 fn mint(acceptor: &Acceptor, share: u64, dir: &View) -> Result<(String, Connector), String> {
-    let access = if dir.write { Access::ReadWrite } else { Access::ReadOnly };
+    let access = Access::ReadWrite;
     let mut badge = [0u8; MAX_BADGE];
     let badge = Grant { share, access, root: &dir.root }
         .encode(&mut badge)

NC-whole-change-reverted

diff --git b/system.toml a/system.toml
index fdc2ef3c6..b5430d2c9 100644
--- b/system.toml
+++ a/system.toml
@@ -26,8 +26,7 @@ start = ["logkeeper", "diskserver", "fileserver", "compositor", "soundserver", "
 # a row read out of one would be a directory deciding what the machine hands
 # out; this is the image's answer, one for all of them. Connectors only —
 # `devices` and `syscap` have no spelling here, so nothing installed claims
-# hardware or enters the RT band — and no directory: a package sees its own
-# `/apps/<name>` read-only and its own `/home/toy/Apps/<name>`, its `HOME`.
+# hardware or enters the RT band.
 [apps]
 receives = ["compositor", "soundserver", "filepicker"]
 
diff --git b/toyos-manifest/src/lib.rs a/toyos-manifest/src/lib.rs
index 136603f47..00ef53351 100644
--- b/toyos-manifest/src/lib.rs
+++ a/toyos-manifest/src/lib.rs
@@ -64,16 +64,6 @@ pub fn session_home() -> String {
     format!("/home/{USER}")
 }
 
-/// An installed package's own folder in the session user's home: its `HOME`,
-/// and the one directory of the home its view holds.
-pub fn app_home(name: &str) -> String {
-    format!("{}/Apps/{name}", session_home())
-}
-
-/// What an app's own folder holds, made with it: where it keeps its config,
-/// data, cache and state. English on disk, as every home folder is.
-pub const APP_FOLDERS: [&str; 4] = ["Config", "Data", "Cache", "State"];
-
 /// Where each system service keeps its own persistent data, one directory per
 /// program key.
 pub const STATE: &str = "/state";
@@ -106,42 +96,6 @@ pub fn role_dirs(role: &str) -> Option<&'static [RoleDir]> {
     ROLES.iter().find(|(name, _)| *name == role).map(|(_, dirs)| *dirs)
 }
 
-/// One directory capability in a program's view: the grant the supervisor
-/// mints on `role`'s port (`toyos::fs::Grant`).
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct View {
-    pub role: &'static str,
-    /// What the program's namespace calls it, after `fs:`.
-    pub dir: String,
-    /// Where it is on the role's volume.
-    pub root: String,
-    /// Whether a request that changes what it holds is served.
-    pub write: bool,
-}
-
-/// Every directory every role serves, each read-write: the view of a program
-/// no narrower one is declared for.
-pub fn whole_tree() -> Vec<View> {
-    ROLES
-        .iter()
-        .flat_map(|(role, dirs)| {
-            dirs.iter().map(|d| View { role, dir: d.dir.to_string(), root: d.root.to_string(), write: true })
-        })
-        .collect()
-}
-
-/// The DATA directory `dir`, beneath one DATA serves.
-fn data_dir(dir: String, write: bool) -> View {
-    let role = "data";
-    let parent = role_dirs(role)
-        .expect("DATA is a role")
-        .iter()
-        .find(|d| dir.strip_prefix(d.dir).is_some_and(|rest| rest.starts_with('/')))
-        .unwrap_or_else(|| panic!("manifest: {dir} is beneath no directory DATA serves"));
-    let root = format!("{}{}", parent.root, &dir[parent.dir.len()..]);
-    View { role, dir, root, write }
-}
-
 /// How often a `restart` row is started again before the supervisor gives up on it: at
 /// most this many ends inside [`RESTART_WINDOW_SECS`]. Past it the row's ports
 /// close, and a client's next connection is answered `Gone`.
@@ -267,32 +221,12 @@ impl Program {
         self.slots || self.receives.iter().any(|r| r == SWAP_PORT)
     }
 
-    /// The installed package this row launches: a row [`Manifest::app_row`]
-    /// made.
-    pub fn package(&self) -> Option<&str> {
-        package::package_of(&self.path)
-    }
-
     /// The `HOME` the supervisor starts this row with. A location grants nothing: what
     /// the program can reach is its view's business, never this string's.
     pub fn home(&self) -> String {
-        match (self.service, self.package()) {
-            (true, _) => format!("{STATE}/{}", self.name),
-            (false, Some(name)) => app_home(name),
-            (false, None) => session_home(),
-        }
-    }
-
-    /// The directories this row's program is endowed. **An installed package
-    /// sees its own directory read-only and its own folder of the home
-    /// read-write, and nothing else any role serves**: no other package, no
-    /// other part of the home, no `/config`, `/state`, `/log` or `/boot`.
-    /// Every other row sees the whole tree, until each declares its own
-    /// (`issues/every-program-sees-only-the-files-it-was-given.md`, stage 2).
-    pub fn view(&self) -> Vec<View> {
-        match self.package() {
-            Some(name) => vec![data_dir(package::Package::dir(name), false), data_dir(app_home(name), true)],
-            None => whole_tree(),
+        match self.service {
+            true => format!("{STATE}/{}", self.name),
+            false => session_home(),
         }
     }
 }
@@ -305,11 +239,10 @@ pub struct Manifest {
     /// Names the supervisor serves itself. The supervisor is in every image and is no `[programs]`
     /// key, so these have no declaration to come from.
     pub supervisor_serves: Vec<String>,
-    /// The connectors every program launched from `/apps` is given beside its
-    /// view ([`Program::view`]), and nothing else. `/apps` is writable to
-    /// the installer, so this row is the image's rather than the package's —
-    /// which is why a device class and a `syscap` right have no spelling on
-    /// the package side at all.
+    /// The namespace every program launched from `/apps` is given: connectors,
+    /// and nothing else. A package directory is writable, so this row is the
+    /// image's rather than the package's — which is why a device class and a
+    /// `syscap` right have no spelling on the package side at all.
     pub apps: Vec<String>,
     /// Program names, in the order `[boot] start` gave them — which orders
     /// nothing, because every port exists before any server runs.
@@ -617,67 +550,10 @@ mod tests {
         let m = sample();
         assert_eq!(m.program("soundserver").unwrap().home(), "/state/soundserver");
         assert_eq!(m.program("terminal").unwrap().home(), "/home/toy");
+        assert_eq!(m.app_row("gbae", "/apps/gbae/gbae").home(), "/home/toy");
         let m = parse("program sshserver /system/bin/sshserver\nservice\nprogram shell /system/bin/shell\n");
         assert!(m.program("sshserver").unwrap().service);
         assert!(!m.program("shell").unwrap().service);
-        // The shell keeps its history under the session's home, in its own
-        // `Apps/shell` (`OWN_FOLDER` in `userland/shell`).
-        assert_eq!(m.program("shell").unwrap().home(), "/home/toy");
-    }
-
-    /// **An installed package's `HOME` is its own folder** of the session
-    /// user's home, the layout's `/home/<user>/Apps/<name>`.
-    #[test]
-    fn a_package_s_home_is_its_own_folder() {
-        let row = sample().app_row("gbae", "/apps/gbae/gbae");
-        assert_eq!(row.package(), Some("gbae"));
-        assert_eq!(row.home(), "/home/toy/Apps/gbae");
-        assert_eq!(APP_FOLDERS, ["Config", "Data", "Cache", "State"]);
-        assert_eq!(sample().program("compositor").unwrap().package(), None);
-    }
-
-    fn views(row: &Program) -> Vec<(&'static str, String, String, bool)> {
-        row.view().into_iter().map(|v| (v.role, v.dir, v.root, v.write)).collect()
-    }
-
-    /// **An installed package sees its own directory read-only and its own
-    /// folder read-write, and nothing else any role serves.** Spelled out
-    /// whole, so a third directory, a wider root or a writable package is red.
-    #[test]
-    fn a_package_s_view_is_its_own_directory_read_only_and_its_own_folder() {
-        let row = sample().app_row("gbae", "/apps/gbae/gbae");
-        assert_eq!(
-            views(&row),
-            [
-                ("data", "/apps/gbae".into(), "apps/gbae".into(), false),
-                ("data", "/home/toy/Apps/gbae".into(), "home/toy/Apps/gbae".into(), true),
-            ]
-        );
-        // The longest name a package has is still beneath its own directories.
-        let longest = "n".repeat(MAX_PROGRAM_NAME);
-        let row = sample().app_row(&longest, &format!("/apps/{longest}/{longest}"));
-        assert_eq!(
-            views(&row).into_iter().map(|(_, _, root, write)| (root, write)).collect::<Vec<_>>(),
-            [(format!("apps/{longest}"), false), (format!("home/toy/Apps/{longest}"), true)]
-        );
-    }
-
-    /// Every row the image declares sees the whole tree read-write, the
-    /// installer and the shell included: neither has authority over `/apps`
-    /// the other lacks.
-    #[test]
-    fn every_declared_row_sees_the_whole_tree_read_write() {
-        let m = parse("program pkg /system/bin/pkg\nprogram shell /system/bin/shell\n");
-        let whole: Vec<_> = ["/apps", "/config", "/home", "/state", "/log", "/boot"]
-            .iter()
-            .zip(["apps", "config", "home", "state", "", ""])
-            .zip(["data", "data", "data", "data", "log", "boot"])
-            .map(|((dir, root), role)| (role, dir.to_string(), root.to_string(), true))
-            .collect();
-        let s = sample();
-        for row in [m.program("pkg").unwrap(), m.program("shell").unwrap(), s.program("compositor").unwrap()] {
-            assert_eq!(views(row), whole, "{}", row.name);
-        }
     }
 
     #[test]
diff --git b/toyos-manifest/src/package.rs a/toyos-manifest/src/package.rs
index b616fc62b..08e3182d5 100644
--- b/toyos-manifest/src/package.rs
+++ a/toyos-manifest/src/package.rs
@@ -4,8 +4,8 @@
 //! to resolve a launch, so the format lives beside [`crate::Manifest`] for the
 //! same reason: one renderer, one parser, one round-trip test.
 //!
-//! **Nothing here is a grant.** `/apps` is writable to every row the image
-//! declares, so a manifest is a peer's claim about itself: it says which binary
+//! **Nothing here is a grant.** `/apps` is writable to every program that can
+//! name it, so a manifest is a peer's claim about itself: it says which binary
 //! *of its own directory* a launch starts. A device, a right and another
 //! package's binary have no spelling in this file at all.
 //!
diff --git b/toyos/src/fs.rs a/toyos/src/fs.rs
index 303fc2e93..5c6b96cb7 100644
--- b/toyos/src/fs.rs
+++ a/toyos/src/fs.rs
@@ -4,8 +4,8 @@
 //! **A directory capability is a connector in the program's namespace**, named
 //! [`CAPABILITY_PREFIX`] and the absolute directory it serves (`fs:/home`).
 //! Each is a connector to its role's one port, which the supervisor minted with
-//! a [`Grant`]: the directory, whether it may be changed, and whose share of
-//! the server it spends. The kernel stamps that on every connection made through it and answers it to the
+//! a [`Grant`]: the directory, and whose share of the server it spends. The
+//! kernel stamps that on every connection made through it and answers it to the
 //! port's acceptor alone, so the server reads what was granted off the
 //! connection and nothing the client says. A program names a file only under a
 //! directory it holds, and the kernel's part is who holds which connector.
@@ -152,64 +152,44 @@ pub struct Grant<'a> {
     /// one service it starts itself or one login session, and for every
     /// launch made from it that opens no session.
     pub share: u64,
-    /// Whether a request that changes what the directory holds is served.
-    pub access: Access,
     /// The directory, as a path on the role's volume, every path on the
     /// connection is resolved beneath: `home`, or the empty path for a volume
     /// served whole. [`canonical`], and at most [`MAX_GRANT_ROOT`] bytes.
     pub root: &'a str,
 }
 
-/// What a [`Grant`] lets its holder do to the directory.
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum Access {
-    /// Read, list and stat; every request that would change what the
-    /// directory holds is refused `PermissionDenied`.
-    ReadOnly,
-    ReadWrite,
-}
-
 /// The format [`Grant::encode`] writes. Carried because a swap replaces a file
 /// server and not the supervisor, so one server reads grants another build
 /// minted, and an older one is refused by name rather than read as this one.
-const GRANT_VERSION: u8 = 3;
+const GRANT_VERSION: u8 = 2;
 
-/// The longest root a grant carries: what one badge holds past the version,
-/// the share and the access.
-pub const MAX_GRANT_ROOT: usize = MAX_BADGE - 1 - 8 - 1;
+/// The longest root a grant carries: what one badge holds past the version and
+/// the share.
+pub const MAX_GRANT_ROOT: usize = MAX_BADGE - 1 - 8;
 
 impl<'a> Grant<'a> {
-    /// The version, the share, the access, then the root: `None` for a root
-    /// no grant can carry.
+    /// The version, the share, then the root: `None` for a root no grant
+    /// can carry.
     pub fn encode<'b>(&self, out: &'b mut [u8; MAX_BADGE]) -> Option<&'b [u8]> {
         if self.root.len() > MAX_GRANT_ROOT || !canonical(self.root) {
             return None;
         }
         out[0] = GRANT_VERSION;
         out[1..9].copy_from_slice(&self.share.to_le_bytes());
-        out[9] = match self.access {
-            Access::ReadOnly => 0,
-            Access::ReadWrite => 1,
-        };
-        let end = 10 + self.root.len();
-        out[10..end].copy_from_slice(self.root.as_bytes());
+        let end = 9 + self.root.len();
+        out[9..end].copy_from_slice(self.root.as_bytes());
         Some(&out[..end])
     }
 
     /// `None` for bytes [`Self::encode`] cannot have written.
     pub fn decode(bytes: &'a [u8]) -> Option<Self> {
         let (&version, rest) = bytes.split_first()?;
-        if version != GRANT_VERSION || rest.len() < 9 || rest.len() - 9 > MAX_GRANT_ROOT {
+        if version != GRANT_VERSION || rest.len() < 8 || rest.len() - 8 > MAX_GRANT_ROOT {
             return None;
         }
         let share = u64::from_le_bytes(rest[..8].try_into().expect("eight bytes"));
-        let access = match rest[8] {
-            0 => Access::ReadOnly,
-            1 => Access::ReadWrite,
-            _ => return None,
-        };
-        let root = core::str::from_utf8(&rest[9..]).ok()?;
-        canonical(root).then_some(Self { share, access, root })
+        let root = core::str::from_utf8(&rest[8..]).ok()?;
+        canonical(root).then_some(Self { share, root })
     }
 }
 
@@ -645,12 +625,10 @@ mod tests {
     fn a_grant_round_trips_at_every_bound() {
         let longest = "r".repeat(MAX_GRANT_ROOT);
         for (share, root) in [(0, ""), (1, "home"), (u64::MAX, "home/toy/Documents"), (7, longest.as_str())] {
-            for access in [Access::ReadOnly, Access::ReadWrite] {
-                let grant = Grant { share, access, root };
-                let mut out = [0u8; MAX_BADGE];
-                let bytes = grant.encode(&mut out).expect("a root a grant carries");
-                assert_eq!(Grant::decode(bytes), Some(grant), "{root:?} {access:?}");
-            }
+            let grant = Grant { share, root };
+            let mut out = [0u8; MAX_BADGE];
+            let bytes = grant.encode(&mut out).expect("a root a grant carries");
+            assert_eq!(Grant::decode(bytes), Some(grant), "{root:?}");
         }
     }
 
@@ -659,38 +637,30 @@ mod tests {
         let mut out = [0u8; MAX_BADGE];
         let past = "r".repeat(MAX_GRANT_ROOT + 1);
         for root in ["/home", "home/", "a//b", ".", "a/../b", past.as_str()] {
-            assert_eq!(Grant { share: 1, access: Access::ReadWrite, root }.encode(&mut out), None, "{root:?}");
+            assert_eq!(Grant { share: 1, root }.encode(&mut out), None, "{root:?}");
         }
     }
 
     #[test]
     fn bytes_no_grant_was_encoded_as_are_refused() {
         let mut out = [0u8; MAX_BADGE];
-        let good = Grant { share: 3, access: Access::ReadOnly, root: "home" }.encode(&mut out).unwrap().to_vec();
-        // Shorter than a version, a share and an access.
-        for n in 0..10 {
+        let good = Grant { share: 3, root: "home" }.encode(&mut out).unwrap().to_vec();
+        // Shorter than a version and a share.
+        for n in 0..9 {
             assert_eq!(Grant::decode(&good[..n]), None, "{n} bytes");
         }
-        // Another version, and the one before this.
-        for version in [GRANT_VERSION - 1, GRANT_VERSION + 1] {
-            let mut other = good.clone();
-            other[0] = version;
-            assert_eq!(Grant::decode(&other), None, "version {version}");
-        }
-        // An access byte that is neither, which is never read as either.
-        for access in [2, 0x80, 0xff] {
-            let mut other = good.clone();
-            other[9] = access;
-            assert_eq!(Grant::decode(&other), None, "access {access}");
-        }
+        // Another version.
+        let mut other = good.clone();
+        other[0] = GRANT_VERSION + 1;
+        assert_eq!(Grant::decode(&other), None);
         // A root the wire refuses, or not UTF-8.
         for root in [&b"/home"[..], b"a/../b", b"a//b", b"\xff"] {
-            let mut bad = good[..10].to_vec();
+            let mut bad = good[..9].to_vec();
             bad.extend_from_slice(root);
             assert_eq!(Grant::decode(&bad), None, "{root:?}");
         }
         // One byte past the longest root.
-        let mut long = good[..10].to_vec();
+        let mut long = good[..9].to_vec();
         long.extend(core::iter::repeat_n(b'r', MAX_GRANT_ROOT + 1));
         assert_eq!(Grant::decode(&long), None);
     }
diff --git b/userland/fileserver/src/lib.rs a/userland/fileserver/src/lib.rs
index 0c29712db..1dc8bb7b9 100644
--- b/userland/fileserver/src/lib.rs
+++ a/userland/fileserver/src/lib.rs
@@ -2,8 +2,7 @@
 //! cache every byte of its volume passes through ([`cache`]), the volumes it
 //! can serve ([`data`] for the bcachefs DATA role, [`fat`] for FAT32's LOG and
 //! BOOT, [`absent`] for a role with no volume this boot), and the resolver that
-//! keeps every path inside the directory a connection was given ([`resolve`]),
-//! and which requests a read-only one is refused ([`rights`]).
+//! keeps every path inside the directory a connection was given ([`resolve`]).
 //!
 //! **This is the page cache.** A block of the volume — a btree node, a FAT
 //! sector, a file's data — is read into [`cache::Cache`] once and served from
@@ -19,6 +18,5 @@ pub mod data;
 pub mod disk;
 pub mod fat;
 pub mod resolve;
-pub mod rights;
 pub mod volume;
 pub mod writeback;
diff --git b/userland/fileserver/src/main.rs a/userland/fileserver/src/main.rs
index 528372ac7..a5032365e 100644
--- b/userland/fileserver/src/main.rs
+++ a/userland/fileserver/src/main.rs
@@ -13,9 +13,8 @@
 //! **A connection is what its grant says** (`toyos::fs::Grant`): the badge
 //! the supervisor minted its connector with, which the kernel stamped on it and
 //! answers this port's acceptor alone. Every path on it is resolved beneath
-//! the grant's root (`fileserver::resolve`); a request that would change what
-//! it holds, on a read-only grant or a read-only volume, is refused before the
-//! volume sees it (`fileserver::rights`).
+//! the grant's root (`fileserver::resolve`); a write on a read-only volume is
+//! refused before the volume sees it.
 //!
 //! **One share cannot take the server.** Beneath each machine-wide bound —
 //! connections waiting on their hello, connections served, streams — each
@@ -45,7 +44,6 @@ use fileserver::data::{DataVolume, Located, Probed};
 use fileserver::disk::{Claimed, Disk, Ram, Served};
 use fileserver::fat::FatVolume;
 use fileserver::resolve::{self, Found, Refusal as Escape, Resolved};
-use fileserver::rights;
 use fileserver::volume::{Kind, Meta, Node, OpenHow, Out, Volume};
 use fileserver::writeback::WriteBack;
 use toyos::endow::{self, Endowments};
@@ -140,8 +138,6 @@ struct Client {
     root: String,
     /// Its grant's share, which it spends.
     share: u64,
-    /// Its grant is read-write and the volume is: what it may change.
-    writes: bool,
     window: Option<SharedMemory>,
     fids: BTreeMap<u64, Fid>,
     next_fid: u64,
@@ -459,7 +455,6 @@ impl Server {
             rx: ipc::FrameRx::new(),
             root: grant.root.to_string(),
             share: grant.share,
-            writes: grant.access == Access::ReadWrite && self.volume.writable(),
             window: None,
             fids: BTreeMap::new(),
             next_fid: 1,
@@ -587,8 +582,8 @@ impl Server {
                 Ok(window) => client.window = Some(window),
                 Err(_) => return Answer::Drop("its window would not map"),
             }
-            let granted = if client.writes { RIGHT_WRITE } else { 0 };
-            return Answer::Reply(Reply { value: granted, ..Reply::ok() });
+            let rights = if self.volume.writable() { RIGHT_WRITE } else { 0 };
+            return Answer::Reply(Reply { value: rights, ..Reply::ok() });
         }
         if self.clients[&id].window.is_none() {
             return Answer::Drop("it asked before it lent a window");
@@ -618,7 +613,9 @@ impl Server {
     }
 
     fn serve_one(&mut self, id: u64, op: u32, r: Request) -> Result<Answer, SyscallError> {
-        if rights::changes(op, r.flags) && !self.clients[&id].writes {
+        let changes = matches!(op, WRITE | TRUNCATE | MKDIR | RMDIR | UNLINK | RENAME | SYMLINK | STREAM)
+            || (op == OPEN && r.flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0);
+        if changes && !self.volume.writable() {
             return Err(SyscallError::PermissionDenied);
         }
         match op {
diff --git b/userland/fileserver/src/rights.rs a/userland/fileserver/src/rights.rs
deleted file mode 100644
index 036a43ac8..000000000
--- b/userland/fileserver/src/rights.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-//! Which requests change what a connection's directory holds: each is refused
-//! `PermissionDenied` before the volume sees it, on a connection whose grant is
-//! read-only (`toyos::fs::Access`) or whose volume is.
-//!
-//! **Closed by default**: an operation this list does not name as one that
-//! only reads is one that changes, so a request added to the wire is refused on
-//! a read-only connection until it is named here.
-
-use toyos::fs::*;
-
-/// Whether request `op`, with an open's `flags`, may change what the directory
-/// holds.
-pub fn changes(op: u32, flags: u64) -> bool {
-    match op {
-        OPEN => flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0,
-        HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => false,
-        _ => true,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    /// Every request the wire has, by what it does to the directory: the
-    /// independent spelling `changes` is held to.
-    const READS: [u32; 10] = [HELLO, CLOSE, READ, STAT, LSTAT, FSTAT, FSYNC, SYNC, READDIR, READLINK];
-    const WRITES: [u32; 8] = [WRITE, TRUNCATE, MKDIR, RMDIR, UNLINK, RENAME, SYMLINK, STREAM];
-
-    #[test]
-    fn every_request_that_writes_changes_the_directory_and_none_that_reads_does() {
-        for op in WRITES {
-            assert!(changes(op, 0), "request {op} writes");
-        }
-        for op in READS {
-            assert!(!changes(op, 0), "request {op} only reads");
-        }
-        // Every request number the wire has is one of the two, or `OPEN`.
-        let mut named: Vec<u32> = READS.iter().chain(&WRITES).copied().chain([OPEN]).collect();
-        named.sort_unstable();
-        assert_eq!(named, (HELLO..=SYNC).collect::<Vec<_>>());
-    }
-
-    /// An open changes the directory by any one flag that writes, creates or
-    /// truncates, alone or with a read.
-    #[test]
-    fn an_open_changes_the_directory_by_every_flag_but_read() {
-        assert!(!changes(OPEN, O_READ));
-        assert!(!changes(OPEN, 0));
-        for flag in [O_WRITE, O_APPEND, O_CREATE, O_TRUNCATE, O_CREATE_NEW] {
-            assert!(changes(OPEN, flag), "flag {flag}");
-            assert!(changes(OPEN, flag | O_READ), "flag {flag} with a read");
-        }
-    }
-
-    /// A request number the wire does not have is refused on a read-only
-    /// connection, never served as a read.
-    #[test]
-    fn a_request_the_wire_does_not_have_changes_the_directory() {
-        for op in [0, SYNC + 1, REPLY, LINK, u32::MAX] {
-            assert!(changes(op, 0), "request {op}");
-        }
-    }
-}
diff --git b/userland/supervisor/src/main.rs a/userland/supervisor/src/main.rs
index c5e50e840..888a98181 100644
--- b/userland/supervisor/src/main.rs
+++ a/userland/supervisor/src/main.rs
@@ -42,15 +42,8 @@
 //! ([`FILES_BOUND`]).
 //!
 //! **Every program it starts gets `HOME` from its row** (`Program::home`), over
-//! anything a launching caller carried: a service its own `/state/<name>` and
-//! an installed package its own `Apps/<name>` folder of the session user's
-//! home, each made before it runs, and everything else the session user's
-//! home, made at boot.
-//!
-//! **Every program it starts holds the directories its row's view names**
-//! (`Program::view`), each a grant minted for that start ([`Grants`]): an
-//! installed package its own directory read-only and its own folder
-//! read-write, and every other row the whole tree.
+//! anything a launching caller carried: a service its own `/state/<name>`, made
+//! before it runs, and everything else the session user's home, made at boot.
 //! A launch of a program no row names is answered with the session's, which
 //! the caller's direct spawn carries in place of its own.
 //!
@@ -77,9 +70,9 @@ use toyos_swap::{Refusal, Request as SwapRequest, Word};
 
 use toyos_manifest::launch::{self as authority, Authority, Session, Sessions, Target};
 use toyos_manifest::package::{self, Package};
-use toyos_manifest::{Manifest, Program, View};
+use toyos_manifest::{Manifest, Program};
 use toyos::endow::Endowments;
-use toyos::fs::{Access, Grant, CAPABILITY_PREFIX};
+use toyos::fs::{Grant, CAPABILITY_PREFIX};
 use toyos::ipc::{self, Connection, RxStep};
 use toyos::launch::{self, Parent, Request, LAUNCHER};
 use toyos::namespace::{self, Namespace};
@@ -390,14 +383,9 @@ fn main() {
     // process nobody endows a namespace: std resolves through this one, and
     // the stop's syncs through the second.
     let files: &'static Namespace = {
-        let whole = toyos_manifest::whole_tree();
-        let own: Vec<(String, Connector)> = whole
+        let own: Vec<(String, Connector)> = role_acceptors
             .iter()
-            .filter_map(|view| Some((role_acceptors.get(view.role)?, view)))
-            .map(|(acceptor, view)| {
-                mint(acceptor, authority::SUPERVISOR_SHARE, view)
-                    .unwrap_or_else(|why| panic!("supervisor: no grant of its own: {why}"))
-            })
+            .flat_map(|(role, acceptor)| mint_grants(role, acceptor, authority::SUPERVISOR_SHARE))
             .collect();
         let build = || {
             let mut builder = namespace::build();
@@ -837,22 +825,14 @@ impl<'a> Supervisor<'a> {
         }
     }
 
-    /// A service's own `HOME`, or an installed package's and its
-    /// [`toyos_manifest::APP_FOLDERS`], made before it runs.
+    /// A service's own `HOME`, made before it first runs.
     fn make_home(&mut self, program: &Program) {
-        let folders: &'static [&str] = match (program.service, program.package()) {
-            (true, _) if is_storage(program) => return,
-            (true, _) => &[],
-            (false, Some(_)) => &toyos_manifest::APP_FOLDERS,
-            (false, None) => return,
-        };
+        if !program.service || is_storage(program) {
+            return;
+        }
         let home = program.home();
         let asked = home.clone();
-        let made = self.files("a program's home", move || {
-            make_dir(&asked)?;
-            folders.iter().try_for_each(|folder| make_dir(&format!("{asked}/{folder}")))
-        });
-        match made {
+        match self.files("a service's home", move || make_dir(&asked)) {
             Ok(Ok(())) => {}
             Ok(Err(e)) => say!("supervisor: {}: {home} could not be made: {e}", program.name),
             Err(why) => say!("supervisor: {}: {home} was not made: {why}", program.name),
@@ -1927,7 +1907,7 @@ fn start<'a>(
         command.endow(&label, raw.0);
         held.0.push(raw);
     }
-    if let Some(ns) = build_namespace(program, system, connectors, grants.view(program, launcher.1)?, extras)? {
+    if let Some(ns) = build_namespace(program, system, connectors, grants.view(program, launcher.1), extras)? {
         let raw = ns.into_raw();
         command.endow(SVC_LABEL, raw.0);
         held.0.push(raw);
@@ -2261,7 +2241,7 @@ fn build_namespace(
 /// file-server role's port.
 ///
 /// **Each start is minted grants naming its session's share** (`toyos::fs::Grant`,
-/// [`Session::share`]), one per directory of its row's view: a service has a
+/// [`Session::share`]), one per directory of every role: a service has a
 /// share of its own through every start of it, so the servers count it, every
 /// child it spawns directly and every launch made from it that opens no
 /// session against one share, and a login session's processes against one
@@ -2273,40 +2253,45 @@ struct Grants<'a> {
 
 impl Grants<'_> {
     /// The directory capabilities `program` is endowed for one start in
-    /// `session`, by namespace name: its row's view (`Program::view`), but
-    /// that a storage row sees none, since a file server resolving a path of
-    /// its own through itself waits for ever. Asked before anything is locked,
-    /// since a storage row's start holds its own kept state.
-    fn view(&self, program: &Program, session: Session) -> std::io::Result<Vec<(String, Connector)>> {
+    /// `session`, by namespace name.
+    ///
+    /// **Every program sees the whole tree the file servers serve**, which is
+    /// the kernel's old view kept whole until each row declares its own
+    /// (`issues/every-program-sees-only-the-files-it-was-given.md`, stage 2),
+    /// with one exception: a storage row sees none, since a file server
+    /// resolving a path of its own through itself waits for ever. Asked before
+    /// anything is locked, since a storage row's start holds its own kept state.
+    fn view(&self, program: &Program, session: Session) -> Vec<(String, Connector)> {
         if is_storage(program) {
-            return Ok(Vec::new());
+            return Vec::new();
         }
-        let wanted = program.view();
         let mut view = Vec::new();
         for (role, kept) in &self.roles {
             let kept = kept.lock().expect("supervisor: a service's state is poisoned");
-            let Some((_, acceptor)) = kept.acceptors.first() else { continue };
-            for dir in wanted.iter().filter(|dir| dir.role == *role) {
-                view.push(mint(acceptor, session.share(), dir).map_err(std::io::Error::other)?);
+            if let Some((_, acceptor)) = kept.acceptors.first() {
+                view.extend(mint_grants(role, acceptor, session.share()));
             }
         }
-        Ok(view)
+        view
     }
 }
 
-/// A grant on `acceptor`, `dir`'s role's port, naming `share`, by the
-/// namespace name a program opens it under. A package's name is part of its
-/// directories', so one no grant carries is refused, by name.
-fn mint(acceptor: &Acceptor, share: u64, dir: &View) -> Result<(String, Connector), String> {
-    let access = if dir.write { Access::ReadWrite } else { Access::ReadOnly };
-    let mut badge = [0u8; MAX_BADGE];
-    let badge = Grant { share, access, root: &dir.root }
-        .encode(&mut badge)
-        .ok_or_else(|| format!("{}'s root {:?} is no grant's", dir.dir, dir.root))?;
-    let connector = acceptor
-        .mint(badge)
-        .unwrap_or_else(|e| panic!("supervisor: no grant on {} for share {share}: {e:?}", dir.dir));
-    Ok((format!("{CAPABILITY_PREFIX}{}", dir.dir), connector))
+/// A grant on `acceptor`, the `role`'s port, for each of its directories,
+/// naming `share`: each by the namespace name a program opens it under.
+fn mint_grants(role: &str, acceptor: &Acceptor, share: u64) -> Vec<(String, Connector)> {
+    let dirs = toyos_manifest::role_dirs(role).expect("supervisor: the build refuses a role it does not know");
+    dirs.iter()
+        .map(|dir| {
+            let mut badge = [0u8; MAX_BADGE];
+            let badge = Grant { share, root: dir.root }
+                .encode(&mut badge)
+                .unwrap_or_else(|| panic!("supervisor: {}'s root {:?} is no grant's", dir.dir, dir.root));
+            let connector = acceptor
+                .mint(badge)
+                .unwrap_or_else(|e| panic!("supervisor: no grant on {} for share {share}: {e:?}", dir.dir));
+            (format!("{CAPABILITY_PREFIX}{}", dir.dir), connector)
+        })
+        .collect()
 }
 
 /// [`toyos_swap::PORT`] in a namespace of its own, for a program whose row

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Mutation runs on head bb091478f: host mutations ran cargo test --lib in the crate named; guest mutations ran cargo test --test toyos-build -- app_view. Logs whole (paths sanitized).

G1-fileserver-ignores-access EXIT=1
G2-supervisor-mints-every-dir-writable EXIT=1
NC-whole-change-reverted EXIT=1
DONE

mut-H1-rename-reads.log

   Compiling toyos v0.18.0 (<worktree>/toyos)
   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling diskserver v0.0.0 (<worktree>/userland/diskserver)
   Compiling fileserver v0.0.0 (<worktree>/userland/fileserver)
    Finished `test` profile [optimized + debuginfo] target(s) in 1.45s
     Running unittests src/lib.rs (<worktree>/target/debug/deps/fileserver-eb78af729d24b848)

running 36 tests
test cache::tests::a_write_past_the_disk_is_refused ... ok
test cache::tests::a_write_reaches_the_disk_at_the_flush_and_not_before ... ok
test cache::tests::dirty_runs_go_out_in_runs ... ok
test cache::tests::contiguous_misses_are_one_request_and_a_second_read_is_none ... ok
test data::tests::a_shrink_zeroes_what_it_cut_and_regrowth_reads_zeros ... ok
test data::tests::a_closed_file_keeps_its_length_across_a_remount ... ok
test data::tests::data_is_one_partition_counted_over_both_sources ... ok
test data::tests::a_file_reads_back_what_was_written_across_pages_and_holes ... ok
test data::tests::an_entry_refused_costs_only_its_own_file ... ok
test data::tests::a_file_unlinked_while_open_answers_gone ... ok
test data::tests::a_rename_moves_an_open_file_and_a_directory_with_its_contents ... ok
test data::tests::directories_are_listed_and_refuse_what_posix_refuses ... ok
test fat::tests::a_device_error_is_io_and_not_not_found ... ok
test fat::tests::a_partial_write_over_an_unreadable_block_writes_nothing ... ok
test fat::tests::a_refused_read_is_an_error_and_never_zeros ... ok
test resolve::tests::a_cycle_is_refused_and_not_followed_for_ever ... ok
test resolve::tests::a_link_that_stays_inside_by_going_up_and_back_resolves ... ok
test resolve::tests::a_plain_path_lands_under_the_root ... ok
test resolve::tests::a_relative_link_is_read_against_its_own_directory ... ok
test resolve::tests::an_absolute_link_is_handed_back_with_the_rest_of_the_path ... ok
test resolve::tests::an_empty_target_names_nothing ... ok
test resolve::tests::no_link_climbs_out_of_the_root ... ok
test resolve::tests::the_last_link_is_left_alone_when_asked ... ok
test rights::tests::a_request_the_wire_does_not_have_changes_the_directory ... ok
test rights::tests::an_open_changes_the_directory_by_every_flag_but_read ... ok
test rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does ... FAILED
test volume::tests::the_anchor_is_the_kernels ... ok
test writeback::tests::a_sync_that_left_a_file_unwritten_is_due_again ... ok
test volume::tests::a_clock_that_always_straddles_is_refused_by_name - should panic ... ok
test cache::tests::at_the_dirty_limit_a_refused_flush_refuses_the_write_and_the_cache_grows_no_larger ... ok
test cache::tests::clean_blocks_are_bounded_and_dirty_ones_are_kept ... ok
test data::tests::every_file_reads_back_as_a_plain_map_of_its_accepted_writes ... ok
test fat::tests::a_file_that_will_not_level_costs_only_its_own_file ... ok
test fat::tests::an_unreadable_entry_is_io_and_not_undated ... ok
test data::tests::a_refused_rename_over_an_open_file_keeps_its_unsynced_writes ... ok
test data::tests::a_write_its_entry_could_not_name_is_refused_and_every_accepted_one_kept ... ok

failures:

---- rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does stdout ----

thread 'rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does' (221519351) panicked at userland/fileserver/src/rights.rs:33:13:
request 15 writes
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does

test result: FAILED. 35 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.17s

error: test failed, to rerun pass `--lib`
H1-rename-reads EXIT=101

mut-H2-any-access-byte-reads-write.log

   Compiling toyos v0.18.0 (<worktree>/toyos)
    Finished `test` profile [optimized + debuginfo] target(s) in 0.82s
     Running unittests src/lib.rs (<worktree>/target/debug/deps/toyos-f91d9b0c3685a5e7)

running 47 tests
test fs::tests::a_listing_entry_round_trips_and_a_short_one_is_refused ... ok
test ipc::tests::a_batch_past_the_bound_is_refused_and_closed ... ok
test fs::tests::no_grant_carries_a_root_the_wire_refuses_or_one_past_the_badge ... ok
test fs::tests::a_path_is_canonical_only_without_empty_dot_or_dotdot_components ... ok
test ipc::tests::a_refused_move_closes_every_handle_it_consumed ... ok
test ipc::tests::a_taken_move_closes_nothing ... ok
test launch::tests::a_request_names_its_parent_by_one_word ... ok
test launch::tests::a_batch_names_each_handle_once_and_never_its_connection ... ok
test fs::tests::a_grant_round_trips_at_every_bound ... ok
test log::proof::a_position_that_never_lands_is_counted_by_the_sweep ... ok
test fs::tests::bytes_no_grant_was_encoded_as_are_refused ... FAILED
test log::proof::a_ring_has_four_lanes_to_claim ... ok
test log::proof::a_placeholder_owned_ring_keeps_its_slots_from_every_pid_until_named ... ok
test log::proof::scribbled_words_bound_the_reader_and_never_panic_it ... ok
test log::proof::slots_left_to_others_are_theirs ... ok
test fs::tests::the_window_copies_every_length_at_every_offset ... ok
test log::proof::the_region_carries_a_record_whole ... ok
test log::stdio::tests::a_formatted_line_is_one_ended_record ... ok
test log::stdio::tests::a_line_a_flush_opened_is_closed_at_its_end ... ok
test log::stdio::tests::a_line_in_pieces_is_one_record ... ok
test log::stdio::tests::a_long_line_is_records_in_order_with_nothing_lost ... ok
test log::stdio::tests::a_newline_ends_a_record_and_a_carriage_return_before_it_goes ... ok
test log::stdio::tests::a_stream_held_by_two_writers_keeps_each_line_whole ... ok
test net::tests::a_binds_request_ends_in_its_listeners_options ... ok
test net::tests::a_code_netstack_chose_is_still_its_own_answer ... ok
test net::tests::a_gone_handle_transfer_is_a_netstack_that_is_not_there ... ok
test net::tests::a_not_found_is_not_a_netstack_that_is_not_there ... ok
test net::tests::a_read_that_hung_up_is_a_netstack_that_is_not_there ... ok
test net::tests::an_accepts_answer_ends_in_its_connections_options ... ok
test log::proof::a_lane_gives_its_reader_every_record_in_order_or_counts_it_refused ... ok
test net::tests::an_option_request_is_three_words_on_the_wire ... ok
test net::tests::nothing_else_becomes_a_missing_netstack ... ok
test poller::tests::a_submission_is_the_whole_entry_where_the_kernel_reads_it ... ok
test poller::tests::a_tail_the_kernel_publishes_mid_drain_is_observed ... ok
test poller::tests::every_accessor_lands_on_the_abi_offset ... ok
test poller::tests::pending_counts_what_the_kernel_has_not_claimed ... ok
test poller::tests::the_drop_counter_is_read_from_the_page ... ok
test syscap::tests::a_machine_that_grew_once_is_read_grown ... ok
test syscap::tests::a_machine_that_grows_every_round_is_refused_by_name ... ok
test syscap::tests::a_record_that_does_not_decode_is_refused_and_not_dropped ... ok
test syscap::tests::a_refused_count_is_refused_and_not_an_empty_inventory ... ok
test syscap::tests::a_refused_read_is_refused_and_not_an_empty_inventory ... ok
test syscap::tests::an_empty_inventory_is_its_count ... ok
test syscap::tests::every_record_is_read ... ok
test log::proof::every_record_is_read_once_in_its_writers_order_or_counted_refused ... ok
test log::proof::a_real_time_write_allocates_nothing ... ok
test log::proof::a_full_ring_or_lane_refuses_at_once_and_never_waits ... ok

failures:

---- fs::tests::bytes_no_grant_was_encoded_as_are_refused stdout ----

thread 'fs::tests::bytes_no_grant_was_encoded_as_are_refused' (221519656) panicked at toyos/src/fs.rs:684:13:
assertion `left == right` failed: access 2
  left: Some(Grant { share: 3, access: ReadWrite, root: "home" })
 right: None
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    fs::tests::bytes_no_grant_was_encoded_as_are_refused

test result: FAILED. 46 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

error: test failed, to rerun pass `--lib`
H2-any-access-byte-reads-write EXIT=101

mut-H3-package-dir-writable.log

   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
    Finished `test` profile [optimized + debuginfo] target(s) in 1.03s
     Running unittests src/lib.rs (<worktree>/target/debug/deps/toyos_manifest-59b28a8000e6c464)

running 27 tests
test launch::tests::what_encode_cannot_have_written_is_refused ... ok
test package::tests::a_launch_path_names_one_package_or_none ... ok
test launch::tests::an_authority_reads_back_as_written ... ok
test package::tests::a_path_the_normalizer_would_change_is_not_canonical_and_classifies_as_nothing ... ok
test package::tests::a_manifest_cannot_name_a_binary_outside_its_own_directory ... ok
test package::tests::what_a_shell_resolves_is_what_the_launcher_accepts ... ok
test tests::a_device_class_name_is_the_abi_s ... ok
test package::tests::what_the_installer_writes_is_what_the_supervisor_reads ... ok
test package::tests::a_field_that_is_not_one_is_refused_by_name ... ok
test launch::tests::nothing_listed_is_nothing_started_and_apps_names_no_row ... ok
test launch::tests::a_caller_starts_what_its_row_lists_and_swap_and_update_only_in_a_login_session ... ok
test launch::tests::a_sessions_launches_spend_its_one_share ... ok
test package::tests::a_listing_shows_only_what_the_supervisor_would_start_and_never_more_than_the_bound ... ok
test tests::a_name_that_would_not_survive_the_round_trip_is_refused ... ok
test tests::a_package_row_is_connectors_and_nothing_else ... ok
test tests::a_package_s_home_is_its_own_folder ... ok
test tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder ... FAILED
test tests::a_service_s_home_is_its_state_and_every_other_row_s_the_session_s ... ok
test tests::a_row_that_serves_a_port_and_is_no_service_is_refused ... ok
test tests::a_role_is_one_of_the_three_and_its_directories_are_fixed ... ok
test tests::a_syscap_set_always_carries_transfer_and_never_an_invented_right ... ok
test tests::every_declared_row_sees_the_whole_tree_read_write ... ok
test tests::logread_carries_both_halves_of_reading_a_stream_that_never_blocks ... ok
test tests::records_attach_to_the_program_above_them ... ok
test tests::the_process_roster_is_its_own_name_and_its_own_bit ... ok
test tests::the_same_manifest_renders_to_the_same_bytes ... ok
test tests::what_the_build_writes_is_what_the_supervisor_reads ... ok

failures:

---- tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder stdout ----

thread 'tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder' (221519996) panicked at toyos-manifest/src/lib.rs:649:9:
assertion `left == right` failed
  left: [("data", "/apps/gbae", "apps/gbae", true), ("data", "/home/toy/Apps/gbae", "home/toy/Apps/gbae", true)]
 right: [("data", "/apps/gbae", "apps/gbae", false), ("data", "/home/toy/Apps/gbae", "home/toy/Apps/gbae", true)]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder

test result: FAILED. 26 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`
H3-package-dir-writable EXIT=101

mut-G1-fileserver-ignores-access.log

   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling toyos-swap v0.1.0 (<worktree>/toyos-swap)
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 3.55s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

14:35:52 running 1 tests, 12 wide

14:35:52   RUN   app_view
14:35:52   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view
14:35:58   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view  (6s)
14:35:58   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view
14:36:04   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view  (6s)
14:36:14 FAIL app_view: `test_rs_app_view` ended Some(101):
  its own package reads
  RED std's write into its own package was answered Ok(())
  RED fs:/apps/appview says it is writable
  RED an open to write in its own package was answered Ok(())
  RED an open to append in its own package was answered Ok(())
  RED an open to truncate in its own package was answered Ok(())
  RED an open to create in its own package was answered Ok(())
  RED an open to create anew in its own package was answered Err(Error(AlreadyExists))
  RED mkdir in its own package was answered Err(Error(AlreadyExists))
  RED rmdir in its own package was answered Ok(())
  RED unlink in its own package was answered Ok(())
  RED rename in its own package was answered Err(Error(NotFound))
  RED symlink in its own package was answered Ok(())
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:87:9:
app_view: [
    "the app ended ExitStatus(ExitStatus(1))",
    "the package's manifest is not what was installed: Err(Kind(NotFound))",
    "the package holds [\"appview\", \"link\", \"made\"], not what was installed",
]
stack backtrace:
   0:      0x1000007b830 - _Unwind_Backtrace
   1:      0x10000064273 - <<std[a00b235bfdc63e32]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x1000007fa67 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000683eb - <std[a00b235bfdc63e32]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x1000003efd3 - std[a00b235bfdc63e32]::panicking::default_hook::{closure#0}
   5:      0x1000005afee - std[a00b235bfdc63e32]::panicking::default_hook
   6:      0x1000005b1a9 - std[a00b235bfdc63e32]::panicking::panic_with_hook
   7:      0x1000003f08d - std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}
   8:      0x10000037249 - std[a00b235bfdc63e32]::sys::backtrace::__rust_end_short_backtrace::<std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}, !>
   9:      0x1000003f928 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008018b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x100000255df - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000025e46 - std[a00b235bfdc63e32]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020630 - std[a00b235bfdc63e32]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005a3c6 - std[a00b235bfdc63e32]::rt::lang_start_internal
  15:      0x10000025e31 - main
  16:      0x10000061633 - std[a00b235bfdc63e32]::sys::pal::toyos::start_rust
  17:      0x1000002a02e - _start

14:36:14   FAIL  app_view  (10s)
14:36:14   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

14:36:14 host: fastest boot 9043 ms against the reference 1424 ms — liveness ceilings paid at 6.35x
14:36:14 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
14:36:14 failures:
14:36:14     app_view: `test_rs_app_view` ended Some(101):

14:36:14 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (21.8s; workers: 12s building, 10s testing)
14:36:14 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-80051-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view` (exit status: 1)

mut-G2-supervisor-mints-every-dir-writable.log

    Finished `test` profile [optimized + debuginfo] target(s) in 0.61s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

14:36:15 running 1 tests, 12 wide

14:36:15   RUN   app_view
14:36:15   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view
14:36:18   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view  (3s)
14:36:18   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view
14:36:25   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view  (7s)
14:36:34 FAIL app_view: `test_rs_app_view` ended Some(101):
  its own package reads
  RED std's write into its own package was answered Ok(())
  RED fs:/apps/appview says it is writable
  RED an open to write in its own package was answered Ok(())
  RED an open to append in its own package was answered Ok(())
  RED an open to truncate in its own package was answered Ok(())
  RED an open to create in its own package was answered Ok(())
  RED an open to create anew in its own package was answered Err(Error(AlreadyExists))
  RED mkdir in its own package was answered Err(Error(AlreadyExists))
  RED rmdir in its own package was answered Ok(())
  RED unlink in its own package was answered Ok(())
  RED rename in its own package was answered Err(Error(NotFound))
  RED symlink in its own package was answered Ok(())
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:87:9:
app_view: [
    "the app ended ExitStatus(ExitStatus(1))",
    "the package's manifest is not what was installed: Err(Kind(NotFound))",
    "the package holds [\"appview\", \"link\", \"made\"], not what was installed",
]
stack backtrace:
   0:      0x1000007b830 - _Unwind_Backtrace
   1:      0x10000064273 - <<std[a00b235bfdc63e32]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x1000007fa67 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000683eb - <std[a00b235bfdc63e32]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x1000003efd3 - std[a00b235bfdc63e32]::panicking::default_hook::{closure#0}
   5:      0x1000005afee - std[a00b235bfdc63e32]::panicking::default_hook
   6:      0x1000005b1a9 - std[a00b235bfdc63e32]::panicking::panic_with_hook
   7:      0x1000003f08d - std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}
   8:      0x10000037249 - std[a00b235bfdc63e32]::sys::backtrace::__rust_end_short_backtrace::<std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}, !>
   9:      0x1000003f928 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008018b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x100000255df - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000025e46 - std[a00b235bfdc63e32]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020630 - std[a00b235bfdc63e32]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005a3c6 - std[a00b235bfdc63e32]::rt::lang_start_internal
  15:      0x10000025e31 - main
  16:      0x10000061633 - std[a00b235bfdc63e32]::sys::pal::toyos::start_rust
  17:      0x1000002a02e - _start

14:36:34   FAIL  app_view  (9s)
14:36:34   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

14:36:34 host: fastest boot 8549 ms against the reference 1424 ms — liveness ceilings paid at 6.00x
14:36:34 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
14:36:34 failures:
14:36:34     app_view: `test_rs_app_view` ended Some(101):

14:36:34 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (19.4s; workers: 10s building, 9s testing)
14:36:34 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-81546-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view` (exit status: 1)

mut-NC-whole-change-reverted.log

   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling toyos-swap v0.1.0 (<worktree>/toyos-swap)
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 15.50s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

14:36:51 running 1 tests, 12 wide

14:36:51   RUN   app_view
14:36:51   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view
14:36:55 external deps changed: cleaning <worktree>/tests/toyos-rust-tests/target/x86_64-unknown-toyos
14:37:03   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view  (12s)
14:37:03   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view
14:37:08 external deps changed: cleaning <worktree>/target/x86_64-unknown-toyos
14:37:28   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view  (25s)
14:37:48 FAIL app_view: `test_rs_app_view` ended Some(101):
  its own package reads
  RED home_dir() is Some("/home/toy"), not /home/toy/Apps/appview
  RED /home/toy/Apps/appview/Config is not a directory
  RED /home/toy/Apps/appview/Data is not a directory
  RED /home/toy/Apps/appview/Cache is not a directory
  RED /home/toy/Apps/appview/State is not a directory
  RED std's write into its own package was answered Ok(())
  RED fs:/apps/appview would not connect: NotFound
  RED fs:/home/toy/Apps/appview would not connect: NotFound
  RED it holds fs:/apps
  RED it holds fs:/home
  RED it holds fs:/config
  RED it holds fs:/state
  RED it holds fs:/log
  RED it holds fs:/boot
  RED /apps/other/kept read 13 bytes
  RED /home/toy/Apps/other/Data/kept read 13 bytes
  RED /apps lists ["appview", "other"]
  RED /home lists ["toy"]
  RED /home/toy lists ["Apps", "Desktop", "Documents", "Downloads", "Fonts", "Music", "Pictures", "Videos"]
  RED /home/toy/Apps lists ["appview", "other"]
  RED /state lists ["logkeeper"]
  RED /log lists ["loader.log", "attempts", "2026-10-09-143746.log"]
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:87:9:
app_view: [
    "the app ended ExitStatus(ExitStatus(1))",
    "the package holds [\"appview\", \"made\", \"manifest.toml\", \"sub\"], not what was installed",
]
stack backtrace:
   0:      0x1000007b960 - _Unwind_Backtrace
   1:      0x100000643a3 - <<std[74ee82b21fc1507c]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x1000007fb97 - core[73b405bef30eeff1]::fmt::write
   3:      0x1000006851b - <std[74ee82b21fc1507c]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x1000003f103 - std[74ee82b21fc1507c]::panicking::default_hook::{closure#0}
   5:      0x1000005b11e - std[74ee82b21fc1507c]::panicking::default_hook
   6:      0x1000005b2d9 - std[74ee82b21fc1507c]::panicking::panic_with_hook
   7:      0x1000003f1bd - std[74ee82b21fc1507c]::panicking::panic_handler::{closure#0}
   8:      0x10000037379 - std[74ee82b21fc1507c]::sys::backtrace::__rust_end_short_backtrace::<std[74ee82b21fc1507c]::panicking::panic_handler::{closure#0}, !>
   9:      0x1000003fa58 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x100000802bb - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x1000002570f - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000025f76 - std[74ee82b21fc1507c]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020760 - std[74ee82b21fc1507c]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005a4f6 - std[74ee82b21fc1507c]::rt::lang_start_internal
  15:      0x10000025f61 - main
  16:      0x10000061763 - std[74ee82b21fc1507c]::sys::pal::toyos::start_rust
  17:      0x1000002a15e - _start

14:37:48   FAIL  app_view  (20s)
14:37:48   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

14:37:48 host: fastest boot 18617 ms against the reference 1424 ms — liveness ceilings paid at 8.00x
14:37:48 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
14:37:48 failures:
14:37:48     app_view: `test_rs_app_view` ended Some(101):

14:37:48 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (57.1s; workers: 37s building, 20s testing)
14:37:48 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-85011-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view` (exit status: 1)

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

cargo test --test toyos-build -- app_view on head bb091478f, and cargo run -- --build-only before it (paths sanitized).

appview1.log

   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling toyos-bootmap v0.1.0 (<worktree>/toyos-bootmap)
   Compiling kernel v0.1.0 (<worktree>/kernel)
   Compiling toyos-xhci v0.1.0 (<worktree>/toyos-xhci)
   Compiling acpiserver-api v0.1.0 (<worktree>/userland/acpiserver-api)
   Compiling toyos-swap v0.1.0 (<worktree>/toyos-swap)
   Compiling toyos-userbound v0.1.0 (<worktree>/toyos-userbound)
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 17.71s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

14:34:08 running 1 tests, 12 wide

14:34:08   RUN   app_view
14:34:08   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view
14:34:19   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view  (11s)
14:34:19   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view
14:34:39   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view  (21s)
14:34:52   its own package reads
  std's write into its package: refused
  an open to write: refused
  an open to append: refused
  an open to truncate: refused
  an open to create: refused
  an open to create anew: refused
  mkdir: refused
  rmdir: refused
  unlink: refused
  rename: refused
  symlink: refused
  every arm held
  the app's write is in /home/toy/Apps/appview/Data
app_view: the app saw its own package read-only and its own folder as HOME, and nothing else

14:34:53   PASS  app_view  (13s)
14:34:53   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

14:34:53 host: fastest boot 11715 ms against the reference 1424 ms — liveness ceilings paid at 8.00x
14:34:53 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
14:34:53 test result: ok. 1 passed, 1 total (44.4s; workers: 31s building, 13s testing)
EXIT=0

metal-stage.log (--metal --metal-readback <dir> boot:testcases)

    Finished `test` profile [optimized + debuginfo] target(s) in 0.46s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)
15:07:06   BUILD x86_64 binaries of tests/toyos-rust-tests
15:08:55   BUILT x86_64 binaries of tests/toyos-rust-tests  (109s)
15:08:55 [toyos] Compiling 137 C tests, and attempting 24 declared ones...
15:09:05 [metal] 23 registration(s) and 225 shared member(s) over 2 boot(s)
15:09:06   BUILD x86_64 kernel, loader, ROOT of <scratch>/metal/testcases
15:09:18   BUILT x86_64 kernel, loader, ROOT of <scratch>/metal/testcases  (11s)
15:09:22 [metal] testcases: 234 job(s) under a list bound of 100100 ms, armed with [] and a boot deadline of 200200 ms — <scratch>/metal/testcases/image.img
15:09:22   BUILD x86_64 ROOT of <scratch>/metal/testcases-watchdog
15:09:28   BUILT x86_64 ROOT of <scratch>/metal/testcases-watchdog  (6s)
15:09:28 [metal] testcases-watchdog: 0 job(s) under a list bound of 60000 ms, armed with ["watchdog"] and a boot deadline of 120000 ms — <scratch>/metal/testcases-watchdog/image.img
# One boot per image. Each invocation is `cargo <words>` from this worktree.

testcases
  image: <scratch>/metal/testcases/image.img
  cargo run --bin toyos-metal -- --image <scratch>/metal/testcases/image.img --readback <scratch>/metal/testcases --fat32-check

testcases-watchdog
  image: <scratch>/metal/testcases-watchdog/image.img
  cargo run --bin toyos-metal -- --image <scratch>/metal/testcases-watchdog/image.img --readback <scratch>/metal/testcases-watchdog --fat32-check
15:09:28 [metal] staged 2 image(s); <scratch>/metal/request.txt lists them. The machine was not touched, so this run establishes nothing about it.
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 --metal --metal-readback <scratch>/metal/ 'boot:testcases'` (exit status: 2)
EXIT=2

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Whole guest suite, cargo test --test toyos-build on head bb091478f, with uptime before and after (paths sanitized).

UPTIME-BEFORE: 17:03  up 10 days,  4:48, 6 users, load averages: 118.92 101.46 81.82
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 3.58s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:03:52 running 39 tests, 12 wide

15:03:52   RUN   libc_sockets
15:03:52   RUN   nested_nmi_is_loud
15:03:52   RUN   bar_map_again
15:03:52   RUN   console_image_boots
15:03:52   RUN   netstack_socket_churn
15:03:52   RUN   acpi_mediated_access
15:03:52   BUILD x86_64 acpi_mediated of tests/toyos-rust-tests, for acpi_mediated_access
15:03:52   BUILD x86_64 netstack_socket_churn of tests/toyos-rust-tests, for netstack_socket_churn
15:03:52   RUN   acpi_lock_given_back_on_one_cpu
15:03:52   RUN   acpi_supply_outlives_holder
15:03:52   RUN   machine_shutdown_short_stop
15:03:52   BUILD x86_64 stop_short of tests/toyos-rust-tests, for machine_shutdown_short_stop
15:03:52   RUN   iommu_virtio_platform
15:03:52   RUN   machine_shutdown
15:03:52   RUN   acpi_power_button
15:03:52   BUILD x86_64 bar_map_again of tests/toyos-rust-tests, for bar_map_again
15:03:52   BUILD x86_64 kernel, loader, ROOT of tests/testcases, for machine_shutdown
15:03:52   BUILD x86_64 kernel, loader, ROOT of console, for console_image_boots
15:03:52   BUILD x86_64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for nested_nmi_is_loud
15:03:52   BUILD x86_64 kernel, loader, ROOT of tests/testcases, for acpi_power_button
15:03:52   BUILD x86_64 kernel, loader, ROOT of tests/netcase, for iommu_virtio_platform
15:03:58 [build-lock] waiting for the build lock (exclusive, clean crate targets against changed external deps) — held by other builds in this tree
15:03:58 [build-lock] waiting for the build lock (shared, test image) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, test image) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, test image) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (shared, test image) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] acquired (exclusive, clean crate targets against changed external deps) after 101.8ms
15:03:58 external deps changed: cleaning <worktree>/target/x86_64-unknown-toyos
15:03:58   BUILD x86_64 nodelay_accepted of tests/toyos-rust-tests, for libc_sockets
15:03:58 [build-lock] waiting for the build lock (shared, a test binary) — held by pid 83014 (clean crate targets against changed external deps), 0s so far
15:03:58 [build-lock] acquired (shared, test image) after 547.0ms
15:03:58 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:03:58 [build-lock] acquired (shared, test image) after 515.7ms
15:03:58 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:03:58 [build-lock] acquired (shared, test image) after 512.5ms
15:03:58 [build-lock] acquired (shared, a test binary) after 509.0ms
15:03:58 [build-lock] acquired (shared, a test binary) after 500.5ms
15:03:58 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:03:58 [build-lock] acquired (shared, a test binary) after 509.1ms
15:03:58 [build-lock] acquired (shared, a test binary) after 499.0ms
15:03:58 [build-lock] acquired (shared, test image) after 478.6ms
15:03:58 [build-lock] waiting for the build lock (exclusive, clean crate targets against changed external deps) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:03:58 [build-lock] waiting for the build lock (exclusive, clean crate targets against changed external deps) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (exclusive, clean crate targets against changed external deps) — an exclusive phase is queued ahead of it
15:03:58 [build-lock] waiting for the build lock (exclusive, clean crate targets against changed external deps) — an exclusive phase is queued ahead of it
15:04:00 [build-lock] artifact staging acquired after 1.6s
15:04:00 [build-lock] artifact staging acquired after 1.6s
15:04:00 [build-lock] artifact staging acquired after 1.6s
15:04:00 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:04:00 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:04:00 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:04:28 [build-lock] still waiting for the build lock (shared, a test binary), 30s so far — the holder left no readable note
15:04:28 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 30s so far — the holder left no readable note
15:04:28 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 30s so far — the holder left no readable note
15:04:28 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 30s so far — the holder left no readable note
15:04:28 [build-lock] still waiting for the artifact lock (artifact staging), 30s so far — held by pid 83014 (artifact staging), 28s so far
15:04:28 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 30s so far — the holder left no readable note
15:04:30 [build-lock] still waiting for the artifact lock (artifact staging), 30s so far — held by pid 83014 (artifact staging), 30s so far
15:04:30 [build-lock] still waiting for the artifact lock (artifact staging), 30s so far — held by pid 83014 (artifact staging), 30s so far
15:04:30 [build-lock] still waiting for the artifact lock (artifact staging), 30s so far — held by pid 83014 (artifact staging), 30s so far
15:04:39 [build-lock] artifact staging acquired after 40.9s
15:04:39 [build-lock] artifact staging acquired after 39.3s
15:04:39 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:04:39 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:04:52 [build-lock] artifact staging acquired after 51.9s
15:04:52 assets: leaving out assets/soundfont.sf2 — only /system/bin/doom opens it and this image builds no doom
15:04:52 assets: leaving out assets/DOOM1.WAD — only /system/bin/doom opens it and this image builds no doom
15:04:52   BUILT x86_64 kernel, loader, ROOT of console, for console_image_boots  (60s)
15:04:56   PASS  console_image_boots  (4s)
15:04:56   RUN   nvme_disk_keeps_log_and_home
15:04:56   BUILD x86_64 ROOT of tests/testcases, for nvme_disk_keeps_log_and_home
15:04:58 [build-lock] still waiting for the build lock (shared, a test binary), 60s so far — the holder left no readable note
15:04:58 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 60s so far — the holder left no readable note
15:04:58 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 60s so far — the holder left no readable note
15:04:58 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 60s so far — the holder left no readable note
15:04:58 [build-lock] still waiting for the build lock (exclusive, clean crate targets against changed external deps), 60s so far — the holder left no readable note
15:04:59 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 7s so far
15:05:00 [build-lock] still waiting for the artifact lock (artifact staging), 60s so far — held by pid 83014 (artifact staging), 8s so far
15:05:00 [build-lock] artifact staging acquired after 60.2s
15:05:00   BUILT x86_64 kernel, loader, ROOT of tests/testcases, for machine_shutdown  (68s)
15:05:00 [build-lock] acquired (shared, a test binary) after 62.0s
15:05:00 [build-lock] artifact staging acquired after 21.1s
15:05:00   BUILT x86_64 kernel, loader, ROOT of tests/testcases, for acpi_power_button  (68s)
15:05:01 [build-lock] artifact staging acquired after 21.3s
15:05:01   BUILT x86_64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for nested_nmi_is_loud  (68s)
15:05:02 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:05:04   [nmi] nested: [nmi] NESTED NMI on cpu 0: a second NMI entered while IST2 was still in use.
15:05:04   PASS  nested_nmi_is_loud  (3s)
15:05:04   RUN   app_view
15:05:05 [build-lock] artifact staging acquired after 5.6s
15:05:05   BUILT x86_64 ROOT of tests/testcases, for nvme_disk_keeps_log_and_home  (9s)
15:05:05   BUILT x86_64 kernel, loader, ROOT of tests/netcase, for iommu_virtio_platform  (73s)
15:05:05 [build-lock] acquired (exclusive, clean crate targets against changed external deps) after 66.6s
15:05:05 external deps changed: cleaning <worktree>/tests/toyos-rust-tests/target/x86_64-unknown-toyos
15:05:05 [build-lock] acquired (exclusive, clean crate targets against changed external deps) after 66.7s
15:05:05 [build-lock] waiting for the build lock (shared, a test binary) — held by other builds in this tree
15:05:05 [build-lock] acquired (shared, a test binary) after 253.2µs
15:05:05 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:05:06   [power] shutdown: QEMU stopped the guest for guest-shutdown
15:05:06   PASS  machine_shutdown  (6s)
15:05:06   RUN   screen_panic_muted
15:05:06   [power] 1 table(s) loaded; [ 5.717 acpiserver] acpiserver: \_S5 handed to the kernel: SLP_TYPa=0 beside [ 5.717 cpu0 kernel] power: S5 is PM1a 0x604 with SLP_TYPa=0, as the acpi claim's holder supplied it
15:05:06   [power] the press: acpiserver: the power button was pressed, on SCI 1 of this boot; asking the supervisor to power off
15:05:06   PASS  acpi_power_button  (6s)
15:05:06   RUN   screen_fatal_behind_a_painter
15:05:08 [build-lock] acquired (exclusive, clean crate targets against changed external deps) after 69.4s
15:05:08   BUILT x86_64 stop_short of tests/toyos-rust-tests, for machine_shutdown_short_stop  (76s)
15:05:08 [build-lock] acquired (shared, a test binary) after 6.1s
15:05:08 [build-lock] waiting for the build lock (shared, a test binary) — an exclusive phase is queued ahead of it
15:05:08   BUILD x86_64 ROOT of tests/testcases, for machine_shutdown_short_stop
15:05:08 [build-lock] waiting for the build lock (shared, test image) — an exclusive phase is queued ahead of it
15:05:08 [build-lock] acquired (exclusive, clean crate targets against changed external deps) after 69.8s
15:05:08 [build-lock] acquired (shared, a test binary) after 3.1s
15:05:08 [build-lock] acquired (shared, a test binary) after 366.7ms
15:05:08 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:08 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:08 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:08 [build-lock] acquired (shared, test image) after 366.5ms
15:05:08   BUILT x86_64 nodelay_accepted of tests/toyos-rust-tests, for libc_sockets  (70s)
15:05:08 [build-lock] artifact staging acquired after 334.0ms
15:05:08   BUILD x86_64 ROOT of tests/netcase, for libc_sockets
15:05:09   BUILT x86_64 bar_map_again of tests/toyos-rust-tests, for bar_map_again  (77s)
15:05:09 [build-lock] artifact staging acquired after 612.9ms
15:05:09   BUILD x86_64 ROOT of tests/testcases, for bar_map_again
15:05:10   BUILT x86_64 acpi_mediated of tests/toyos-rust-tests, for acpi_mediated_access  (77s)
15:05:10 [build-lock] artifact staging acquired after 1.5s
15:05:10   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view
15:05:10   BUILD x86_64 ROOT of tests/acpicase, for acpi_supply_outlives_holder
15:05:10   BUILD x86_64 ROOT of tests/acpicase, for acpi_mediated_access
15:05:10   BUILD x86_64 ROOT of tests/acpicase, for acpi_lock_given_back_on_one_cpu
15:05:10   BUILT x86_64 netstack_socket_churn of tests/toyos-rust-tests, for netstack_socket_churn  (78s)
15:05:10   BUILD x86_64 ROOT of tests/netcase, for netstack_socket_churn
15:05:11   PASS  screen_panic_muted  (5s)
15:05:11   RUN   screen_fatal_halt_composited
15:05:11   BUILD x86_64 ROOT of tests/panelcase, for screen_fatal_halt_composited
15:05:11   BUILT x86_64 ROOT of tests/testcases, for machine_shutdown_short_stop  (3s)
15:05:12   BUILT x86_64 ROOT of tests/netcase, for libc_sockets  (4s)
15:05:12   [iommu] headless: 3 virtio function(s) behind a unit = true, the audio function 00:04.0 among them
15:05:12   BUILT x86_64 ROOT of tests/testcases, for bar_map_again  (4s)
15:05:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:13 [build-lock] artifact staging acquired after 225.2ms
15:05:13   BUILT x86_64 ROOT of tests/acpicase, for acpi_lock_given_back_on_one_cpu  (4s)
15:05:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:13 [build-lock] artifact staging acquired after 382.2ms
15:05:14   BUILT x86_64 ROOT of tests/acpicase, for acpi_supply_outlives_holder  (4s)
15:05:14 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 1s so far
15:05:14   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view  (5s)
15:05:14 [build-lock] artifact staging acquired after 905.4ms
15:05:14   BUILT x86_64 ROOT of tests/acpicase, for acpi_mediated_access  (5s)
15:05:14   BUILD x86_64 ROOT of tests/proctreecase, for app_view
15:05:14 [build-lock] artifact staging acquired after 647.9ms
15:05:14 [build-lock] artifact staging acquired after 185.2ms
15:05:14 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:14 [build-lock] artifact staging acquired after 196.2ms
15:05:14   BUILT x86_64 ROOT of tests/netcase, for netstack_socket_churn  (4s)
15:05:17   PASS  screen_fatal_behind_a_painter  (10s)
15:05:17   RUN   screen_loader_clears
15:05:18 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 4s so far
15:05:22   [iommu] headless-no-iommu: 2 virtio function(s) behind a unit = false, the audio function 00:04.0 among them; the NIC's claim refused for want of a unit
15:05:23   [claims] bar_map_again: two answers held at once map apart, and the later outlives the earlier
15:05:23   [claims] bar_map_again: answered with a handle that maps
15:05:23   [claims] bar_map_again: answered after the refusal with a handle that maps
15:05:23   PASS  bar_map_again  (10s)
15:05:23   RUN   virt_early_panic
15:05:23   BUILD aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_early_panic
15:05:23   [power] a short stop, then the power-off: stop: 17 of 19 userland thread(s) stopped across 1 cpu(s) in 0 ms of a 0 ms budget over 1 sweep(s), 0 of 0 userland block operation(s) still open; this reset lands wherever the other 2 are
15:05:23   PASS  machine_shutdown_short_stop  (12s)
15:05:23   RUN   virt_early_fault
15:05:23   BUILD aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_early_fault
15:05:23   [acpi] [ 6.835 cpu0 kernel] mtrr: the boot processor's range registers: IA32_MTRR_DEF_TYPE 0xc06 and 8 variable pairs
15:05:23   [acpi] [ 9.542 test-runner pid=8] acpi: an unbound claim was refused its access, the lock and the power-off's sleep type
15:05:23   [acpi] [ 9.543 test-runner pid=8] acpi: RAM was refused both ways as UsableMemory
15:05:23   [acpi] [ 9.543 test-runner pid=8] acpi: the RSDP read through as type 9 and its write was refused TableWrite
15:05:23   [acpi] [ 9.544 test-runner pid=8] acpi: an unlisted register was read and refused its write MemoryType, an unlisted address below 1 MiB was refused UnlistedCached, and the interrupt controllers, the HPET and a function's BAR DeviceMemory
15:05:23   [acpi] [ 9.544 test-runner pid=8] acpi: the FACS read through as type 10, its write was refused FacsWrite, and the memory after it was written and put back
15:05:23   [acpi] [ 9.545 test-runner pid=8] acpi: COM1, the CMOS index, the 8259 and the configuration mechanism were refused KernelPort; the i8042's row ClaimedPort; PM1a_CNT read and refused its write ReadOnlyPort; the POST port was written
15:05:23   [acpi] [ 9.650 test-runner pid=8] acpi: ACPI_ENABLE and ACPI_DISABLE were refused SMI_CMD as KernelCommand and SCI_EN stayed set, a write wider than a byte was refused CommandSpan, every firmware call made was read back from the port and counted on the boot processor, and a storm of them was refused CommandRate
15:05:23   [acpi] [ 9.673 test-runner pid=8] acpi: the host bridge read as 0x29c08086 by its address and through ECAM, and every write to configuration space was refused ConfigWrite
15:05:23   [acpi] [ 9.674 test-runner pid=8] acpi: the lock a dead holder left taken read free; it was taken and given back, given back with GBL_RLS where the firmware had asked, and found pending while the firmware owned it
15:05:23   [acpi] [ 9.674 test-runner pid=8] acpi: a sleep type wider than three bits was refused InvalidArgument, the next holder's replaced a dead one's, and a second under one claim was refused AlreadyExists
15:05:23   [acpi] [ 9.650 test-runner pid=8] acpi: firmware calls of 0x51, 0x52, 0x53 were asked from the CPUs of x2APIC id [0, 0, 0]; this chipset's SMI_EN reads 0x00000000, APMC_EN clear, so no call interrupts its firmware
15:05:23   [acpi] [ 9.650 test-runner pid=8] acpi: 8 firmware calls were made before call 6 of the storm was refused
15:05:23   [acpi] [ 9.622 cpu0 kernel] acpi: firmware call 0x51 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 21103ns, its SMI count unread before the write and unread after; the first of that byte
15:05:23   [acpi] [ 9.632 cpu0 kernel] acpi: firmware call 0x52 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 2010ns, its SMI count unread before the write and unread after; the first of that byte
15:05:23   [acpi] [ 9.632 cpu0 kernel] acpi: firmware call 0x53 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 0ns, its SMI count unread before the write and unread after; the first of that byte
15:05:23   [acpi] [ 9.632 cpu0 kernel] acpi: firmware call 0x54 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 0ns, its SMI count unread before the write and unread after; the first of that byte
15:05:23   [acpi] [ 9.632 cpu0 kernel] acpi: firmware call 0x55 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 1005ns, its SMI count unread before the write and unread after; the first of that byte
15:05:23   [acpi] [ 9.488 cpu0 kernel] shutdown: no ACPI server supplied S5 — refused
15:05:23   [acpi] [ 9.804 cpu0 kernel] acpi: the Global Lock given back for a holder that left it taken (the machine is stopping)
15:05:23   [acpi] QEMU stopped the guest for guest-shutdown, on the probe's sleep type and not the keeper's
15:05:23   PASS  acpi_lock_given_back_on_one_cpu  (87s)
15:05:23   RUN   virt_el2_drop
15:05:23   BUILD aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_el2_drop
15:05:24 [build-lock] artifact staging acquired after 6.0s
15:05:24   [acpi] [ 9.908 cpu1 kernel] Shutting down.
15:05:24   [acpi] QEMU stopped the guest for guest-shutdown, with no claim held, on the sleep type a holder that is gone supplied
15:05:24 assets: leaving out assets/soundfont.sf2 — only /system/bin/doom opens it and this image builds no doom
15:05:24   PASS  acpi_supply_outlives_holder  (88s)
15:05:24   RUN   virt_user_mode
15:05:24 assets: leaving out assets/DOOM1.WAD — only /system/bin/doom opens it and this image builds no doom
15:05:24   BUILD aarch64 kernel, loader, ROOT of tests/testcases, for virt_user_mode
15:05:24   BUILT x86_64 ROOT of tests/panelcase, for screen_fatal_halt_composited  (13s)
15:05:24   PASS  libc_sockets  (18s)
15:05:24   RUN   virt_timer_preempts
15:05:24   BUILD aarch64 abuse_readonly_copyout of tests/toyos-rust-tests, for virt_timer_preempts
15:05:25   BUILT x86_64 ROOT of tests/proctreecase, for app_view  (11s)
15:05:25   [acpi] [ 8.192 cpu0 kernel] mtrr: the boot processor's range registers: IA32_MTRR_DEF_TYPE 0xc06 and 8 variable pairs
15:05:25   [acpi] [ 8.207 cpu1 kernel] mtrr: cpu1's range registers are off (IA32_MTRR_DEF_TYPE 0x0) and not the boot processor's: every read this CPU makes is uncached
15:05:25   [acpi] [10.968 test-runner pid=8] acpi: an unbound claim was refused its access, the lock and the power-off's sleep type
15:05:25   [acpi] [10.969 test-runner pid=8] acpi: RAM was refused both ways as UsableMemory
15:05:25   [acpi] [10.970 test-runner pid=8] acpi: the RSDP read through as type 9 and its write was refused TableWrite
15:05:25   [acpi] [10.971 test-runner pid=8] acpi: an unlisted register was read and refused its write MemoryType, an unlisted address below 1 MiB was refused UnlistedCached, and the interrupt controllers, the HPET and a function's BAR DeviceMemory
15:05:25   [acpi] [10.971 test-runner pid=8] acpi: the FACS read through as type 10, its write was refused FacsWrite, and the memory after it was written and put back
15:05:25   [acpi] [10.972 test-runner pid=8] acpi: COM1, the CMOS index, the 8259 and the configuration mechanism were refused KernelPort; the i8042's row ClaimedPort; PM1a_CNT read and refused its write ReadOnlyPort; the POST port was written
15:05:25   [acpi] [11.046 test-runner pid=8] acpi: ACPI_ENABLE and ACPI_DISABLE were refused SMI_CMD as KernelCommand and SCI_EN stayed set, a write wider than a byte was refused CommandSpan, every firmware call made was read back from the port and counted on the boot processor, and a storm of them was refused CommandRate
15:05:25   [acpi] [11.047 test-runner pid=8] acpi: the host bridge read as 0x29c08086 by its address and through ECAM, and every write to configuration space was refused ConfigWrite
15:05:25   [acpi] [11.047 test-runner pid=8] acpi: the lock a dead holder left taken read free; it was taken and given back, given back with GBL_RLS where the firmware had asked, and found pending while the firmware owned it
15:05:25   [acpi] [11.048 test-runner pid=8] acpi: a sleep type wider than three bits was refused InvalidArgument, the next holder's replaced a dead one's, and a second under one claim was refused AlreadyExists
15:05:25   [acpi] [11.046 test-runner pid=8] acpi: firmware calls of 0x51, 0x52, 0x53 were asked from the CPUs of x2APIC id [1, 1, 1]; this chipset's SMI_EN reads 0x00000000, APMC_EN clear, so no call interrupts its firmware
15:05:25   [acpi] [11.046 test-runner pid=8] acpi: 8 firmware calls were made before call 6 of the storm was refused
15:05:25   [acpi] [10.989 cpu1 kernel tid=2] acpi: firmware call 0x51 written to SMI_CMD 0xb2 on cpu0, asked from cpu1; the write held cpu0 41109ns, its SMI count unread before the write and unread after; the first of that byte
15:05:25   [acpi] [11.031 cpu1 kernel tid=6] acpi: firmware call 0x52 written to SMI_CMD 0xb2 on cpu0, asked from cpu1; the write held cpu0 8021ns, its SMI count unread before the write and unread after; the first of that byte
15:05:25   [acpi] [11.043 cpu1 kernel tid=8] acpi: firmware call 0x53 written to SMI_CMD 0xb2 on cpu0, asked from cpu1; the write held cpu0 9024ns, its SMI count unread before the write and unread after; the first of that byte
15:05:25   [acpi] [11.044 cpu0 kernel] acpi: firmware call 0x54 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 1002ns, its SMI count unread before the write and unread after; the first of that byte
15:05:25   [acpi] [11.044 cpu0 kernel] acpi: firmware call 0x55 written to SMI_CMD 0xb2 on cpu0, asked from cpu0; the write held cpu0 0ns, its SMI count unread before the write and unread after; the first of that byte
15:05:25   [acpi] [10.938 cpu0 kernel] shutdown: no ACPI server supplied S5 — refused
15:05:25   [acpi] [11.107 cpu0 kernel] acpi: the Global Lock given back for a holder that left it taken (the machine is stopping)
15:05:25   [acpi] QEMU stopped the guest for guest-shutdown, on the probe's sleep type and not the keeper's
15:05:25 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:25   PASS  acpi_mediated_access  (11s)
15:05:25   RUN   virt_irq_storm
15:05:25   BUILD aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_irq_storm
15:05:27   PASS  netstack_socket_churn  (12s)
15:05:27   RUN   virt_timer_floor
15:05:27   BUILD aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_timer_floor
15:05:27 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 2s so far
15:05:28 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 3s so far
15:05:28 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 3s so far
15:05:28   PASS  screen_loader_clears  (11s)
15:05:28   RUN   virt_fp_isolation
15:05:30 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 5s so far
15:05:30 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 5s so far
15:05:31   [disk] after the reboot /home/kept-83014-1791558296531105000/release is the 10 lines of /system/etc/os-release, and /log/2026-10-09-150510.log: test-runner said kept-83014-1791558296531105000
15:05:31   PASS  nvme_disk_keeps_log_and_home  (26s)
15:05:31   RUN   virt_first_entry
15:05:32   [iommu] declined: [ 9.359 cpu0 kernel] virtio-sound: NOT INITIALISED — PCI 00:04.0 refused the feature set 0x100000000 the driver accepted, leaving DEVICE_STATUS=0x3 without FEATURES_OK
15:05:32   PASS  iommu_virtio_platform  (27s)
15:05:32   RUN   virt_unmap_touch
15:05:34   its own package reads
  std's write into its package: refused
  an open to write: refused
  an open to append: refused
  an open to truncate: refused
  an open to create: refused
  an open to create anew: refused
  mkdir: refused
  rmdir: refused
  unlink: refused
  rename: refused
  symlink: refused
  every arm held
  the app's write is in /home/toy/Apps/appview/Data
app_view: the app saw its own package read-only and its own folder as HOME, and nothing else

15:05:34   PASS  app_view  (15s)
15:05:34   RUN   virt_debug_refused
15:05:36   [panic] the fatal report is on the panel and sealed in the black box (14243 bytes)
15:05:36   PASS  screen_fatal_halt_composited  (12s)
15:05:36   RUN   virt_readonly_copyout
15:05:45 [build-lock] artifact staging acquired after 19.1s
15:05:45 [build-lock] artifact staging acquired after 17.1s
15:05:45 [build-lock] artifact staging acquired after 16.9s
15:05:45 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:45 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:45 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:57 [build-lock] artifact staging acquired after 28.8s
15:05:57 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:59   BUILT aarch64 abuse_readonly_copyout of tests/toyos-rust-tests, for virt_timer_preempts  (35s)
15:05:59   BUILD aarch64 ring0_timer_in_syscall of tests/toyos-rust-tests, for virt_timer_preempts
15:05:59 [build-lock] artifact staging acquired after 29.1s
15:05:59 [build-lock] artifact staging acquired after 28.8s
15:05:59 [build-lock] artifact staging acquired after 14.4s
15:05:59 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:05:59 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:03 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 4s so far
15:06:08 [build-lock] artifact staging acquired after 23.9s
15:06:09   BUILT aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_early_fault  (46s)
15:06:09 [build-lock] artifact staging acquired after 24.1s
15:06:09   BUILT aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_early_panic  (46s)
15:06:09 [build-lock] artifact staging acquired after 12.3s
15:06:09   BUILT aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_el2_drop  (46s)
15:06:09 [build-lock] artifact staging acquired after 10.1s
15:06:09   BUILT aarch64 kernel, loader, ROOT of tests/testcases, for virt_user_mode  (45s)
15:06:09 [build-lock] artifact staging acquired after 10.2s
15:06:09   BUILT aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_timer_floor  (42s)
15:06:09 [build-lock] artifact staging acquired after 6.4s
15:06:09   BUILT aarch64 kernel boot-actuators,test-actuators, loader, ROOT of tests/testcases, for virt_irq_storm  (44s)
15:06:09   PASS  virt_early_fault  (900ms)
15:06:09   RUN   virt_ring0_timer_in_syscall
15:06:09   PASS  virt_early_panic  (706ms)
15:06:09   RUN   virt_mask_windows
15:06:09   BUILT aarch64 ring0_timer_in_syscall of tests/toyos-rust-tests, for virt_timer_preempts  (11s)
15:06:09   BUILD aarch64 counters_read of tests/toyos-rust-tests, for virt_mask_windows
15:06:10   BUILD aarch64 ROOT of tests/virtjobcase, for virt_first_entry
15:06:10   BUILD aarch64 ROOT of tests/virtjobcase, for virt_timer_preempts
15:06:10   BUILD aarch64 ROOT of tests/virtjobcase, for virt_fp_isolation
15:06:10   BUILD aarch64 ROOT of tests/virtjobcase, for virt_debug_refused
15:06:10   PASS  virt_el2_drop  (1s)
15:06:10   RUN   virt_smp
15:06:12 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:12 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:12 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:12 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:13 [build-lock] artifact staging acquired after 648.0ms
15:06:13 [build-lock] artifact staging acquired after 643.8ms
15:06:13 [build-lock] artifact staging acquired after 638.6ms
15:06:13 [build-lock] artifact staging acquired after 635.5ms
15:06:13   BUILT aarch64 ROOT of tests/virtjobcase, for virt_debug_refused  (3s)
15:06:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:13   PASS  virt_user_mode  (4s)
15:06:13   RUN   virt_el1_smp
15:06:13 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:13   [virt] [ 3.285 cpu0 kernel] timer-floor: PASS span=10000 floor=10000 ticks: the comparator past the counter it was set from
15:06:13   PASS  virt_timer_floor  (3s)
15:06:13   RUN   virt_failed_ap_leaves_no_hole
15:06:13   BUILT aarch64 counters_read of tests/toyos-rust-tests, for virt_mask_windows  (4s)
15:06:13 [build-lock] artifact staging acquired after 591.7ms
15:06:13   BUILD aarch64 trace_read of tests/toyos-rust-tests, for virt_mask_windows
15:06:13 [build-lock] artifact staging acquired after 729.3ms
15:06:13   BUILT aarch64 ROOT of tests/virtjobcase, for virt_fp_isolation  (4s)
15:06:13 [build-lock] artifact staging acquired after 862.7ms
15:06:13   BUILT aarch64 ROOT of tests/virtjobcase, for virt_first_entry  (4s)
15:06:14   BUILT aarch64 ROOT of tests/virtjobcase, for virt_timer_preempts  (4s)
15:06:16   BUILT aarch64 trace_read of tests/toyos-rust-tests, for virt_mask_windows  (3s)
15:06:16   BUILD aarch64 kernel mask-windows, ROOT of tests/virtsmpcase, for virt_mask_windows
15:06:16   BUILD aarch64 ROOT of tests/virtsmpcase, for virt_el1_smp
15:06:16   BUILD aarch64 ROOT of tests/virtsmpcase, for virt_smp
15:06:16   BUILD aarch64 ROOT of tests/virtsmpcase, for virt_failed_ap_leaves_no_hole
15:06:17   [virt] [ 3.297 test-runner pid=4] preempt: the counting thread was preempted twice, at counts 5194185 and 9148370
15:06:17   PASS  virt_timer_preempts  (4s)
15:06:17   RUN   virt_fatal_halts_the_others_first
15:06:17   BUILD aarch64 panic_halts_first of tests/toyos-rust-tests, for virt_fatal_halts_the_others_first
15:06:17   [virt] [ 3.564 test-runner pid=6] first_entry: x1-x30 were zero at a new thread's first instruction
15:06:17   [virt] [ 3.732 test-runner pid=5] fp_isolation: v0-v31, FPCR and FPSR survived 3 switches to a thread that loads another state
15:06:17   PASS  virt_first_entry  (42s)
15:06:17   RUN   virt_reboot
15:06:17   BUILD aarch64 ROOT of tests/virtrebootcase, for virt_reboot
15:06:17   PASS  virt_fp_isolation  (45s)
15:06:17   RUN   virt_off_names_the_cpus_left_on
15:06:17   BUILD aarch64 ROOT of tests/virtsmpcase, for virt_off_names_the_cpus_left_on
15:06:18   [virt] [ 5.205 test-runner pid=16] debug_refused: SYS_DEBUG's double fault and TLB acknowledgement delay were refused
15:06:18   PASS  virt_debug_refused  (41s)
15:06:18   RUN   virt_reboot_refused_without_psci
15:06:18   BUILD aarch64 ROOT of tests/virtrebootcase, for virt_reboot_refused_without_psci
15:06:21 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:21 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:21 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:22 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 1s so far
15:06:22 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 1s so far
15:06:22 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 1s so far
15:06:22 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 1s so far
15:06:23   [virt] [ 6.346 test-runner pid=17] ring0_timer_in_syscall: the timer interrupted the syscall's body and re-armed a quantum
15:06:23   PASS  virt_ring0_timer_in_syscall  (14s)
15:06:23   [virt] [ 6.478 test-runner pid=7] unmap_touch: 4 reads of a page just unmapped on the unmapping thread, and 4 on another, each ended their process
15:06:23   PASS  virt_unmap_touch  (51s)
15:06:23   [virt] [ 6.771 test-runner pid=18] a syscall writes only where its caller could store
15:06:23   PASS  virt_readonly_copyout  (47s)
15:06:30   [virt] [20.206 cpu0 kernel] irq-storm: PASS sgis=6053568/6053568 ticks=1000: the timer fired through the flood, and every SGI sent was taken
15:06:30   PASS  virt_irq_storm  (20s)
15:06:34 [build-lock] artifact staging acquired after 12.8s
15:06:34 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:34 [build-lock] artifact staging acquired after 13.0s
15:06:34   BUILT aarch64 ROOT of tests/virtsmpcase, for virt_smp  (18s)
15:06:34 [build-lock] artifact staging acquired after 13.2s
15:06:34   BUILT aarch64 ROOT of tests/virtsmpcase, for virt_el1_smp  (18s)
15:06:34 [build-lock] artifact staging acquired after 12.5s
15:06:34   BUILT aarch64 ROOT of tests/virtsmpcase, for virt_failed_ap_leaves_no_hole  (18s)
15:06:35   BUILT aarch64 panic_halts_first of tests/toyos-rust-tests, for virt_fatal_halts_the_others_first  (17s)
15:06:35 [build-lock] artifact staging acquired after 12.8s
15:06:35   BUILT aarch64 ROOT of tests/virtsmpcase, for virt_off_names_the_cpus_left_on  (17s)
15:06:35 [build-lock] artifact staging acquired after 12.8s
15:06:35 [build-lock] artifact staging acquired after 12.2s
15:06:35   BUILD aarch64 ROOT of tests/virtpaniccase, for virt_fatal_halts_the_others_first
15:06:35 [build-lock] artifact staging acquired after 832.7ms
15:06:35 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:35 [build-lock] waiting for the artifact lock (artifact staging) — held by pid 83014 (artifact staging), 0s so far
15:06:35 [build-lock] artifact staging acquired after 232.9ms
15:06:35   BUILT aarch64 kernel mask-windows, ROOT of tests/virtsmpcase, for virt_mask_windows  (19s)
15:06:35 [build-lock] artifact staging acquired after 514.8ms
15:06:35   BUILT aarch64 ROOT of tests/virtrebootcase, for virt_reboot  (18s)
15:06:35   BUILT aarch64 ROOT of tests/virtrebootcase, for virt_reboot_refused_without_psci  (17s)
15:06:39   BUILT aarch64 ROOT of tests/virtpaniccase, for virt_fatal_halts_the_others_first  (4s)
15:06:39   [virt] [ 5.120 test-runner pid=4] unmap_touch: 4 reads of a page just unmapped on the unmapping thread, and 4 on another, each ended their process
15:06:39   [virt] 8 CPUs entered at EL2, started through SMC, and scheduling
15:06:40   [virt] [ 5.335 test-runner pid=13] counters_read: every cpu answered for itself, and each counter is a right's
15:06:40   [virt] [ 5.426 test-runner pid=15] trace_read: a wake precedes its pick, a cursor is its reader's own, and the diary is TRACE's
15:06:40   [virt] stop: 5 of 5 userland thread(s) stopped across 8 cpu(s) in 0 ms of a 2010 ms budget over 1 sweep(s), 0 of 0 userland block operation(s) still open; every CPU but 0x0 called CPU_OFF, then 0x0 SYSTEM_OFF
15:06:40   PASS  virt_smp  (11s)
15:06:40   [virt] [ 4.838 test-runner pid=4] unmap_touch: 4 reads of a page just unmapped on the unmapping thread, and 4 on another, each ended their process
15:06:40   [virt] [ 5.403 test-runner pid=4] unmap_touch: 4 reads of a page just unmapped on the unmapping thread, and 4 on another, each ended their process
15:06:40   [virt] 8 CPUs entered at EL1, started through HVC, and scheduling
15:06:40   [virt] Rebooting., then one SYSTEM_RESET, and QEMU stopped for guest-reset
15:06:40   PASS  virt_reboot  (5s)
15:06:40   [virt] [ 5.544 test-runner pid=13] counters_read: every cpu answered for itself, and each counter is a right's
15:06:40   [virt] [ 5.622 test-runner pid=15] trace_read: a wake precedes its pick, a cursor is its reader's own, and the diary is TRACE's
15:06:40   [virt] stop: 5 of 5 userland thread(s) stopped across 8 cpu(s) in 0 ms of a 2010 ms budget over 1 sweep(s), 0 of 0 userland block operation(s) still open; every CPU but 0x0 called CPU_OFF, then 0x0 SYSTEM_OFF
15:06:40   PASS  virt_el1_smp  (9s)
15:06:40   [virt] reboot: this machine has no reset this kernel performs — refused, and the job ended exit 1
15:06:40   PASS  virt_reboot_refused_without_psci  (5s)
15:06:40   [virt] [ 5.185 test-runner pid=4] unmap_touch: 4 reads of a page just unmapped on the unmapping thread, and 4 on another, each ended their process
15:06:40   [windows] cpu0 irqs_off_ns=969000 preempt_off_ns=10224000
15:06:40   [windows] cpu1 irqs_off_ns=6405000 preempt_off_ns=3197000
15:06:40   [windows] cpu2 irqs_off_ns=2388000 preempt_off_ns=10594000
15:06:40   [windows] cpu3 irqs_off_ns=1845000 preempt_off_ns=21994000
15:06:40   [windows] cpu4 irqs_off_ns=999000 preempt_off_ns=29634000
15:06:40   [windows] cpu5 irqs_off_ns=1067000 preempt_off_ns=25175000
15:06:40   [windows] cpu6 irqs_off_ns=50039000 preempt_off_ns=50487000
15:06:40   [windows] cpu7 irqs_off_ns=5159000 preempt_off_ns=7588000
15:06:40   PASS  virt_mask_windows  (6s)
15:06:42   [panic] the fatal path on cpu2 left every other CPU halted with interrupts masked
15:06:42   PASS  virt_fatal_halts_the_others_first  (3s)
15:06:45   [virt] [6, 7] left on and named; the rest CPU_OFF, then 0x0 SYSTEM_OFF; 3176 PSCI call(s) traced
15:06:45   PASS  virt_off_names_the_cpus_left_on  (10s)
15:06:45   [virt] [10.679 test-runner pid=4] unmap_touch: 4 reads of a page just unmapped on the unmapping thread, and 4 on another, each ended their process
15:06:45   [virt] a non-last AP never started and the dense machine ran its job
15:06:45   PASS  virt_failed_ap_leaves_no_hole  (14s)
15:06:45   --- 41 guests, 25 of them not the shipping kernel, 3 kernel build(s): ["", "boot-actuators,test-actuators", "mask-windows"]

15:06:45 host: fastest boot 451 ms against the reference 1424 ms — liveness ceilings paid at 1.00x
15:06:45 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:06:45 test result: ok. 39 passed, 39 total (173.0s; workers: 1262s building, 704s testing)
UPTIME-AFTER: 17:06  up 10 days,  4:50, 6 users, load averages: 55.65 81.83 77.53
EXIT=0

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

cargo run -- --ci host on head bb091478f: every [ci] line of the run and its exit line (the whole log is 525 kB, past one comment; paths sanitized).

14:38:19 === [ci] the build system
14:39:51 [ci] the build system: cargo test --lib
14:39:51 === [ci] the harness's own checks
14:40:06 [ci] the harness's own checks: cargo test --test toyos-checks
14:40:06 === [ci] the workspace's host members
14:47:28 [ci] the workspace's host members: cargo test --workspace --exclude toyos-build --exclude bootloader --exclude kernel --exclude toyos --exclude acpiserver --exclude acpiserver-api --exclude toyos-aml --exclude calc --exclude compositor --exclude toyos-desktop --exclude console --exclude diskserver --exclude doom --exclude editor --exclude filepicker --exclude filepicker-api --exclude files --exclude fileserver --exclude host --exclude inspect --exclude kernelprobe --exclude logkeeper --exclude logkeeper-api --exclude metalprobe --exclude netstack --exclude toyos-mdns --exclude toyos-net-node --exclude paint --exclude pkg --exclude proctest --exclude shell --exclude snake --exclude soundserver --exclude toyos-mixer --exclude sprite --exclude sshserver --exclude supervisor --exclude swap --exclude symbolize --exclude terminal --exclude test-runner --exclude toybox --exclude toyos-font --exclude toyos-window --exclude trace --exclude update
14:47:28 === [ci] the kernel's library
14:47:48 [ci] the kernel's library: cargo test -p kernel --lib --features sched-check
14:47:48 === [ci] the licences of what ships
14:47:52 [ci] the licences of what ships: 6 exception(s) stand, and nothing else is refused
14:47:52 === [ci] clippy and the bare targets
14:47:52 [ci] clippy and the bare targets: installed
14:47:52 === [ci] clippy, warnings denied
14:51:27 [ci] clippy, warnings denied: clean
14:51:27 === [ci] kernel-loom without loom
14:51:29 [ci] kernel-loom without loom: cargo test -p kernel-loom --no-default-features --test log_zeroed_init --test log_body_words --test log_cursor
14:51:29 === [ci] control `wake-fence-off`
14:51:34 [ci] control `wake-fence-off`: 1 verdict(s) reached
14:51:34 === [ci] control `lock-acquire-off`
14:51:38 [ci] control `lock-acquire-off`: 1 verdict(s) reached
14:51:38 === [ci] control `owed-fence-off`
14:51:42 [ci] control `owed-fence-off`: 1 verdict(s) reached
14:51:42 === [ci] control `seqlock-writer-fence-off`
14:51:47 [ci] control `seqlock-writer-fence-off`: 1 verdict(s) reached
14:51:47 === [ci] control `serial-try-lock-then-some`
14:51:52 [ci] control `serial-try-lock-then-some`: 2 verdict(s) reached
14:51:52 === [ci] control `reap-raise-relaxed`
14:51:59 [ci] control `reap-raise-relaxed`: 1 verdict(s) reached
14:51:59 === [ci] control `shootdown-serve-relaxed`
14:52:06 [ci] control `shootdown-serve-relaxed`: 2 verdict(s) reached
14:52:06 === [ci] control `shootdown-served-relaxed`
14:52:10 [ci] control `shootdown-served-relaxed`: 1 verdict(s) reached
14:52:10 === [ci] control `roster-commit-relaxed`
14:52:19 [ci] control `roster-commit-relaxed`: 1 verdict(s) reached
14:52:19 === [ci] control `smp-ready-split`
14:52:27 [ci] control `smp-ready-split`: 1 verdict(s) reached
14:52:27 === [ci] control `log-commit-release-off`
14:52:31 [ci] control `log-commit-release-off`: 2 verdict(s) reached
14:52:31 === [ci] control `shard-publish-relaxed`
14:52:35 [ci] control `shard-publish-relaxed`: 1 verdict(s) reached
14:52:35 === [ci] control `log-ring-publish-relaxed`
14:52:41 [ci] control `log-ring-publish-relaxed`: 3 verdict(s) reached
14:52:41 === [ci] control `log-ring-tail-relaxed`
14:52:48 [ci] control `log-ring-tail-relaxed`: 3 verdict(s) reached
14:52:48 === [ci] control `log-ring-loads-swapped`
14:52:58 [ci] control `log-ring-loads-swapped`: 1 verdict(s) reached
14:52:58 === [ci] control `post-is-an-answer`
14:53:04 [ci] control `post-is-an-answer`: 3 verdict(s) reached
14:53:04 === [ci] control `poll-fire-load-store`
14:53:10 [ci] control `poll-fire-load-store`: 2 verdict(s) reached
14:53:10 === [ci] control `sleeplock-acquire-off`
14:53:15 [ci] control `sleeplock-acquire-off`: 2 verdict(s) reached
14:53:15 === [ci] control `device-irq-lossy`
14:53:18 [ci] control `device-irq-lossy`: 1 verdict(s) reached
14:53:18 === [ci] control `dump-report-relaxed`
14:53:22 [ci] control `dump-report-relaxed`: 1 verdict(s) reached
14:53:22 === [ci] control `no-preempt-guard`
14:53:26 [ci] control `no-preempt-guard`: 1 verdict(s) reached
14:53:26 === [ci] control `doorbell-kick-relaxed`
14:53:30 [ci] control `doorbell-kick-relaxed`: 1 verdict(s) reached
14:53:30 === [ci] control `push-fence-relaxed`
14:53:32 [ci] control `push-fence-relaxed`: 1 verdict(s) reached
14:53:32 === [ci] control `commit-ignores-notify`
14:53:38 [ci] control `commit-ignores-notify`: 2 verdict(s) reached
14:53:38 === [ci] control `notify-flag-load-only`
14:53:52 [ci] control `notify-flag-load-only`: 1 verdict(s) reached
14:53:52 === [ci] control `gate-fence-off`
14:54:00 [ci] control `gate-fence-off`: 1 verdict(s) reached
14:54:00 === [ci] control `poll-fire-load-store`
14:54:06 [ci] control `poll-fire-load-store`: 3 verdict(s) reached
14:54:06 === [ci] control `fault-posted-before-it-is-set`
14:54:10 [ci] control `fault-posted-before-it-is-set`: 3 verdict(s) reached
14:54:10 === [ci] control `victim-retires-mid-probe`
14:54:14 [ci] control `victim-retires-mid-probe`: 1 verdict(s) reached
14:54:14 === [ci] control `counting-allocator`
14:54:24 [ci] control `counting-allocator`: 2 verdict(s) reached
14:54:24 === [ci] control `mutate-spawn-skips-the-insert-recheck`
14:54:39 [ci] control `mutate-spawn-skips-the-insert-recheck`: 2 verdict(s) reached
14:54:39 === [ci] control `mutate-claim-teardown-always-wins`
14:54:56 [ci] control `mutate-claim-teardown-always-wins`: 1 verdict(s) reached
14:54:56 === [ci] control `mutate-kill-waits-for-its-victims`
14:55:16 [ci] control `mutate-kill-waits-for-its-victims`: 2 verdict(s) reached
14:55:16 === [ci] control `mutate-first-out-tears-down`
14:55:38 [ci] control `mutate-first-out-tears-down`: 1 verdict(s) reached
14:55:38 === [ci] control `mutate-join-collects-in-a-teardown`
14:55:53 [ci] control `mutate-join-collects-in-a-teardown`: 1 verdict(s) reached
14:55:53 === [ci] control `mutate-last-out-leaves-before-its-teardown`
14:56:01 [ci] control `mutate-last-out-leaves-before-its-teardown`: 2 verdict(s) reached
14:56:01 === [ci] control `mutate-place-skips-the-insert-recheck`
14:56:11 [ci] control `mutate-place-skips-the-insert-recheck`: 1 verdict(s) reached
14:56:11 === [ci] control `mutate-refused-spawn-keeps-the-count`
14:56:20 [ci] control `mutate-refused-spawn-keeps-the-count`: 1 verdict(s) reached
14:56:20 === [ci] control `mutate-landed-child-retires-nothing`
14:56:35 [ci] control `mutate-landed-child-retires-nothing`: 2 verdict(s) reached
14:56:35 === [ci] control `mutate-publish-before-the-children`
14:56:48 [ci] control `mutate-publish-before-the-children`: 1 verdict(s) reached
14:56:48 === [ci] control `mutate-walk-in-one-hold`
14:57:02 [ci] control `mutate-walk-in-one-hold`: 1 verdict(s) reached
14:57:02 === [ci] control `mutate-spawner-handle-after-the-landing`
14:57:13 [ci] control `mutate-spawner-handle-after-the-landing`: 2 verdict(s) reached
14:57:13 === [ci] control `mutate-spawner-handle-before-the-childs-own`
14:57:22 [ci] control `mutate-spawner-handle-before-the-childs-own`: 1 verdict(s) reached
14:57:22 === [ci] control `placement-ignores-staleness`
14:57:33 [ci] control `placement-ignores-staleness`: 1 verdict(s) reached
14:57:33 === [ci] control `mutate-session-end-forgets`
14:57:46 [ci] control `mutate-session-end-forgets`: 1 verdict(s) reached
14:57:46 === [ci] control `mutate-abort-keeps-inflight`
14:57:49 [ci] control `mutate-abort-keeps-inflight`: 1 verdict(s) reached
14:57:49 === [ci] control `mutate-no-reissue-after-loss`
14:57:52 [ci] control `mutate-no-reissue-after-loss`: 1 verdict(s) reached
14:57:52 === [ci] control `publish-relaxed`
14:57:54 [ci] control `publish-relaxed`: 1 verdict(s) reached
14:57:54 === [ci] control `no-clamp`
14:57:56 [ci] control `no-clamp`: 1 verdict(s) reached
14:57:56 === [ci] control `end-keeps-inflight`
14:57:57 [ci] control `end-keeps-inflight`: 1 verdict(s) reached
14:57:57 === [ci] userland/acpiserver
14:58:04 [ci] userland/acpiserver: cargo test --manifest-path userland/acpiserver/Cargo.toml
14:58:04 === [ci] userland/acpiserver/aml
14:58:11 [ci] userland/acpiserver/aml: cargo test --manifest-path userland/acpiserver/aml/Cargo.toml
14:58:11 === [ci] userland/calc
14:58:26 [ci] userland/calc: cargo test --manifest-path userland/calc/Cargo.toml
14:58:26 === [ci] userland/compositor/desktop
14:58:29 [ci] userland/compositor/desktop: cargo test --manifest-path userland/compositor/desktop/Cargo.toml
14:58:29 === [ci] userland/diskserver
14:58:31 [ci] userland/diskserver: cargo test --manifest-path userland/diskserver/Cargo.toml
14:58:31 === [ci] userland/fileserver
14:58:35 [ci] userland/fileserver: cargo test --manifest-path userland/fileserver/Cargo.toml
14:58:35 === [ci] userland/logkeeper
14:58:37 [ci] userland/logkeeper: cargo test --manifest-path userland/logkeeper/Cargo.toml
14:58:37 === [ci] userland/netstack
14:58:47 [ci] userland/netstack: cargo test --manifest-path userland/netstack/Cargo.toml
14:58:47 === [ci] userland/netstack/mdns
14:58:53 [ci] userland/netstack/mdns: cargo test --manifest-path userland/netstack/mdns/Cargo.toml
14:58:53 === [ci] userland/netstack/node
14:59:12 [ci] userland/netstack/node: cargo test --manifest-path userland/netstack/node/Cargo.toml
14:59:12 === [ci] userland/pkg
14:59:18 [ci] userland/pkg: cargo test --manifest-path userland/pkg/Cargo.toml
14:59:18 === [ci] userland/soundserver
14:59:32 [ci] userland/soundserver: cargo test --manifest-path userland/soundserver/Cargo.toml
14:59:32 === [ci] userland/soundserver/mixer
14:59:44 [ci] userland/soundserver/mixer: cargo test --manifest-path userland/soundserver/mixer/Cargo.toml
14:59:44 === [ci] userland/sshserver
15:00:26 [ci] userland/sshserver: cargo test --manifest-path userland/sshserver/Cargo.toml
15:00:26 === [ci] userland/symbolize
15:00:27 [ci] userland/symbolize: cargo test --manifest-path userland/symbolize/Cargo.toml
15:00:27 === [ci] the apps for linux
15:01:54 [ci] the apps for linux: 10 app(s) pass `cargo check --target x86_64-unknown-linux-gnu`; userland/doom, userland/proctest, userland/shell, userland/terminal, userland/toybox not attempted, as their manifests declare
15:01:54 === [ci] the apps for macos
15:02:41 [ci] the apps for macos: 10 app(s) pass `cargo build`; userland/doom, userland/proctest, userland/shell, userland/terminal, userland/toybox not attempted, as their manifests declare
15:02:41 === [ci] the apps for windows
15:03:39 [ci] the apps for windows: 10 app(s) pass `cargo check --target x86_64-pc-windows-msvc`; userland/doom, userland/proctest, userland/shell, userland/terminal, userland/toybox not attempted, as their manifests declare
15:03:39 === [ci] the toyos SDK
15:03:41 [ci] the toyos SDK: cargo test -p toyos
15:03:41 === [ci] nothing left in $TMPDIR or /tmp
15:03:41 [ci] nothing left in $TMPDIR or /tmp: every test took its scratch with it
15:03:41 [ci] Host: 78 step(s), all green
EXIT=0

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #807 at bb091478f (round 1).

Net lines: git diff --shortstat origin/main...bb091478f: 14 files, +570 −92. Production is about +202 −73, tests about +353 −16, and issue files +15 −3.

BLOCKER

  • toyos-manifest/src/lib.rs:69, :278, :292: a package's folder can be another program's folder. app_home(name) is /home/toy/Apps/<name>. The shell keeps its history in that same directory, Apps/shell/State/history (userland/shell/src/main.rs:14). Nothing stops a package being named after a declared row: package::name_is_bad doesn't check for it, and neither does the supervisor's resolve (userland/supervisor/src/main.rs:1736-1762). So a package installed as /apps/shell/shell is minted read-write on the shell's history, and the same goes for every row that later keeps an Apps/<row> folder. That breaks the isolation this change claims, and it makes the layout stage-2 exit ("it cannot name another app's folder") false. Fix: refuse such a launch by name, with a host test that refuses app_row("shell", "/apps/shell/shell") and a refusal arm in the guest test.

  • userland/supervisor/src/main.rs:842-859: a package launch goes ahead when its folder was not made. For a package, make_home logs the failure (say!) and the launch continues. The app then runs with a HOME grant on a root that doesn't exist, so its first write gets NotFound. Nothing on the launcher's reply says why. This failure is reachable, not just in theory: files() returns Err at once whenever the worker still owes an earlier call (:872-873), so under a slow file server every launch in that window runs without its folder. Refuse the launch (MSG_REFUSED) when the folder or one of its APP_FOLDERS is not made. Root CLAUDE.md, "Fail fast".

  • tests/toyos.rs:326 (MACHINE_TESTS "app_view"), :131: app_view belongs on a metal row, not in QEMU. The pull request body itself says a metal row on tests/proctreecase reaches the same behaviour. Its two reasons don't answer the cheapest-tier rule:

    • "the T14 is the orchestrator's": the orchestrator runs it.
    • "the merge queue holds it": the rule doesn't take that as a reason.

    The tree already has this test's siblings: fs_share, port_badge, launch_toctou and launch_authority are PROCTREECASE metal rows (tests/toyos.rs:1000-1012). As written, app_view adds a whole QEMU boot to guest / suite, right after Every shipping-kernel member rides testcases behind its rows' jobs, and the two debug timing rows ride shared-debug: 23 boots to 20 #799 cut the boots from 23 to 20. Move it: add test_rs_app_view to PROCTREECASE's jobs, add a metal::Metal { arms: PROCTREECASE, judge: |b| b[0].job_passed("test_rs_app_view") } row, and delete the MACHINE_TESTS entry, the run_machine_test arm and fn app_view.

  • Evidence: gbae was guessed at, not measured. The body says "I could not read gbae's source here", but the source is public at github.com/Japabu/gbae and can be fetched with gh api (I read bfe8dabf8). On ToyOS, gbae with no ROM argument browses std::env::current_dir() (src/main.rs:475, src/menu.rs:308-335). That is the launching caller's cwd (supervisor/src/main.rs:1545), and it is outside the new view. /home and /home/toy are kernel mount points that list nothing, so the menu can't reach /home/toy/Apps/gbae either.

    • Launched from the desktop, gbae opens its window and can list and load no ROM. A ROM given on argv loads only from inside its own folder.
    • Its config goes to $HOME/.config/gbae/config, a dotfile inside its folder.

    This contradicts the package track's recorded running behaviour ("It lists a directory itself and reads the ROM the user picks out of it", issues/a-package-is-a-directory-under-apps-and-the-installer-is-a-program.md:65-67). Measure gbae under this view and post the reading. Then either make the recorded behaviour hold, or record the loss as a defect with an exit and take it to the owner. Shipping it unrecorded is not one of the options.

  • Evidence: the T14 readings this change reaches are not posted. The QEMU guest suite no longer runs the testcases jobs, so the T14 is the only place the 234 testcases jobs and the PROCTREECASE rows run against the version-3 grants and the changed tests/proctreecase/system.toml. Two readings are owed, both at the final head, since this head will move:

    • boot:testcases: request.txt's sha256 for both images matches what that head stages. Every one of the 234 jobs is passed in the readback, with none failed or missing. The 23 registrations are judged green, the testcases-watchdog arm's verdict is green, and --fat32-check is clean. A connection let go with its badge is no grant anywhere in the log is red.
    • boot:proctreecase: process_tree, launch_toctou, launch_authority, port_badge, fs_share and, once moved, app_view are all passed. app_view's log carries every arm held and the job's own closing line.

    The reading the orchestrator is taking now, boot:testcases at bb091478f, covers neither the moved row nor the fixes above.

NOTE

  • userland/fileserver/src/main.rs:621: an unknown request number now gets different handling by grant. On a read-only connection it is answered PermissionDenied, a reply the client can ignore; on a writable one the client is dropped. Check the refusal after the op is recognised, so that a word off the wire is dropped on every connection.
  • userland/supervisor/src/main.rs:2280, :2300: the Result paths of Grants::view and mint cannot fire. MAX_PROGRAM_NAME (32) plus home/toy/Apps/ (14 bytes) fits within MAX_GRANT_ROOT (54), and the host test at toyos-manifest/src/lib.rs asserts the longest name. Use expect with that bound as its reason, as the supervisor's own namespace does.
  • toyos-manifest/src/lib.rs:635: assert_eq!(APP_FOLDERS, [...]) compares a constant with its own spelling and tests nothing. Delete it.
  • The brief's goal "pkg alone writes /apps" is not met. The body names two owner rulings that pull against each other: "the installer has no authority a shell lacks" and "nothing writes a committed package" (issues/a-package-is-...md:41-43, :50-56). That conflict is the owner's to decide, so file it as a kind: question rather than leaving it as a sentence in the isolation track. The plan says the same ("needs an owner ruling").
  • issues/every-program-sees-only-the-files-it-was-given.md:101: "and nothing else" is false of the tree. A package still reaches the kernel-served /tmp and /system, as the body itself says.
  • The brief's stop condition ("a grant rights bit changes the grant's format version for a running swap") fired, and the branch went on instead of stopping. The bump to version 3 is right: an older version is refused by name and tested in both directions. But the call to proceed was the orchestrator's to make.

SEND BACK

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

T14 boot:testcases at bb091478f (superseded once the fix round moves the head; recorded for comparison): images' sha256 checked against request.txt (testcases 28f8bc3a…0202bc1c, testcases-watchdog 553c55ca…22e8690e), both toyos-metal --fat32-check exit 0, verdict passed; judge EXIT=0, [metal] 248 passed, 0 failed, 2 boot(s); 137 C cases each ccheck: 0. proctreecase was not booted.

Japabu and others added 4 commits October 9, 2026 17:19
…m end's order (#803) and the .local name's re-probe (#800), into the app grants

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
… refused, and app_view is a proctreecase metal row

Review of #807, round 1:

- A package's folder is `/home/toy/Apps/<name>`, and the shell keeps its
  history in `Apps/shell`. `Manifest::app_row` now refuses a package named
  after any row the image declares, by `row_named`, and the supervisor says
  it as the launch's refusal.
- A package's launch is refused (`MSG_REFUSED`) when its folder or one of
  `APP_FOLDERS` is not a directory after the supervisor made it, the file
  worker's being busy included. A service's home keeps its old, logged path.
- `app_view` moves from a QEMU machine test to a metal row on
  `tests/proctreecase`, whose judge reads the job's pass, `every arm held`,
  its closing line, and the supervisor's two refusals; the job gains both
  refusal arms.
- fileserver recognises a request before it judges the grant: a number the
  wire does not have drops the client on every connection.
- `Grants::view` and `mint` cannot fail: a compile-time assertion holds the
  longest package folder within `MAX_GRANT_ROOT`.
- The tautological `APP_FOLDERS` assertion goes; the isolation track says
  what a package still reaches (`/tmp`, `/system`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…as a defect

gbae bfe8dabf8, started with no ROM, opens its file menu on its cwd. Run
under a package's view in a QEMU guest (its `list_directory` verbatim, a
temporary arm of app_view, posted on #807), the menu reaches no ROM from `/`
or `/home/toy`; a ROM given by path loads only from its own folder; its
config is kept in its own folder. The exit is the file picker of the package
track's stage 6, and the track's running line now points at the defect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…n panicking

With the whole change reverted, the launch of the row-named package goes
ahead and leaves the app's folder behind, so the plant failed and the job
panicked before it named any hole. The negative control is now red by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

gbae measured under a package's view (BLOCKER 4 of round 1), at head 1c8490e4e: gbae-measure.patch adds a temporary QEMU machine test and, to the app_view package, gbae bfe8dabf8's src/menu.rs list_directory verbatim walked from the app's cwd (/, then /home/toy), its ROM load (std::fs::read) and its config save (src/config.rs). ROMs planted at /home/toy/Downloads/measure.gba and in the package's own Data folder. Applied, run once (cargo test --test toyos-build -- app_view_qemu, EXIT=0), restored; the reading is the GBAE lines. Paths sanitized.

gbae-measure.patch

diff --git a/tests/toyos-rust-tests/src/bin/app_view.rs b/tests/toyos-rust-tests/src/bin/app_view.rs
index e299fbfb9..fe17e9d28 100644
--- a/tests/toyos-rust-tests/src/bin/app_view.rs
+++ b/tests/toyos-rust-tests/src/bin/app_view.rs
@@ -51,6 +51,7 @@ const APP: &str = "app";
 fn main() {
     match std::env::args().nth(1).as_deref() {
         Some(APP) => app(),
+        Some("gbae") => gbae(),
         _ => job(),
     }
 }
@@ -86,6 +87,17 @@ fn job() {
     }
     fs::remove_file(HOME).expect("take the planted file away");
 
+    // MEASURE gbae: ROMs where a user keeps them, and in the app's own folder.
+    for rom in ["/home/toy/Downloads/measure.gba", "/home/toy/Apps/appview/Data/measure.gba"] {
+        let dir = rom.rsplit_once('/').unwrap().0;
+        fs::create_dir_all(dir).unwrap_or_else(|e| panic!("make {dir}: {e}"));
+        fs::write(rom, b"rom").unwrap_or_else(|e| panic!("write {rom}: {e}"));
+    }
+    for cwd in ["/", "/home/toy"] {
+        let ran = Command::new(PROGRAM).arg("gbae").current_dir(cwd).output().expect("launch for gbae");
+        print!("{}", String::from_utf8_lossy(&ran.stdout));
+        print!("{}", String::from_utf8_lossy(&ran.stderr));
+    }
     let ran = Command::new(PROGRAM).arg(APP).output().expect("launch the package through the launcher");
     print!("{}", String::from_utf8_lossy(&ran.stdout));
     print!("{}", String::from_utf8_lossy(&ran.stderr));
@@ -226,3 +238,70 @@ fn app() {
     }
     println!("  every arm held");
 }
+
+/// gbae bfe8dabf8 `src/menu.rs` `list_directory`, verbatim but its type.
+struct FileEntry {
+    name: String,
+    path: std::path::PathBuf,
+    is_directory: bool,
+}
+fn list_directory(directory: &std::path::Path) -> Vec<FileEntry> {
+    let mut directories = Vec::new();
+    let mut roms = Vec::new();
+    for entry in std::fs::read_dir(directory).into_iter().flatten().flatten() {
+        let path = entry.path();
+        let name = entry.file_name().to_string_lossy().to_string();
+        if name.starts_with('.') {
+            continue;
+        }
+        if path.is_dir() {
+            directories.push(FileEntry { name: format!("{}/", name), path, is_directory: true });
+        } else if path.extension().is_some_and(|extension| extension.eq_ignore_ascii_case("gba")) {
+            roms.push(FileEntry { name, path, is_directory: false });
+        }
+    }
+    let by_name = |a: &FileEntry, b: &FileEntry| a.name.to_lowercase().cmp(&b.name.to_lowercase());
+    directories.sort_by(by_name);
+    roms.sort_by(by_name);
+    let parent = directory.parent().map(|parent| FileEntry {
+        name: "../".to_string(),
+        path: parent.to_path_buf(),
+        is_directory: true,
+    });
+    parent.into_iter().chain(directories).chain(roms).collect()
+}
+
+/// What gbae, launched with no ROM, can browse to from its cwd, load, and
+/// keep its config in.
+fn gbae() {
+    let cwd = std::env::current_dir().unwrap_or_default();
+    println!("GBAE cwd {}", cwd.display());
+    let mut seen = std::collections::BTreeSet::new();
+    let mut queue = vec![(cwd.clone(), 0)];
+    let mut roms = Vec::new();
+    while let Some((dir, depth)) = queue.pop() {
+        if !seen.insert(dir.clone()) || depth > 6 {
+            continue;
+        }
+        let listed = list_directory(&dir);
+        let names: Vec<&str> = listed.iter().map(|e| e.name.as_str()).collect();
+        println!("GBAE menu {} -> {:?}", dir.display(), names);
+        for e in listed {
+            if e.is_directory {
+                queue.push((e.path, depth + 1));
+            } else {
+                roms.push(e.path);
+            }
+        }
+    }
+    println!("GBAE browsable roms {:?}", roms);
+    for rom in ["/home/toy/Downloads/measure.gba", "/home/toy/Apps/appview/Data/measure.gba", "Data/measure.gba"] {
+        println!("GBAE load_rom({rom}) -> {:?}", std::fs::read(rom).map(|b| b.len()));
+    }
+    let base = std::env::var_os("XDG_CONFIG_HOME")
+        .map(std::path::PathBuf::from)
+        .or_else(|| std::env::var_os("HOME").map(|home| std::path::PathBuf::from(home).join(".config")));
+    let path = base.map(|base| base.join("gbae").join("config")).unwrap();
+    let written = path.parent().map_or(Ok(()), std::fs::create_dir_all).and_then(|()| std::fs::write(&path, "volume = 5\n"));
+    println!("GBAE config {} save -> {:?}, load -> {:?}", path.display(), written, std::fs::read_to_string(&path));
+}
diff --git a/tests/toyos.rs b/tests/toyos.rs
index 008c522ec..92be51b79 100644
--- a/tests/toyos.rs
+++ b/tests/toyos.rs
@@ -334,6 +334,7 @@ const MACHINE_TESTS: &[&str] = &[
     // reads it have no host build, and the T14 boots from a stick beside an
     // NVMe disk that is another system's.
     "nvme_disk_keeps_log_and_home",
+    "app_view_qemu",
 ];
 
 /// **The metal profile**: which registrations run on the ThinkPad T14, what
@@ -3200,6 +3201,7 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> {
         "bar_map_again" => bar_map_again(test_config),
         "console_image_boots" => console_image_boots(),
         "nvme_disk_keeps_log_and_home" => nvme_disk_keeps_log_and_home(test_config),
+        "app_view_qemu" => app_view_qemu(),
         other => Err(format!("unknown machine test {other}")),
     }
 }
@@ -3239,6 +3241,18 @@ fn served_by_diskserver(qemu: &mut QemuInstance, console: &mut String) -> Result
     Ok(())
 }
 
+fn app_view_qemu() -> Result<(), String> {
+    let case = compile::repo_root().join("tests/proctreecase");
+    let (_, job) = suite_bin(qemu::SUITE_ARCH, "app_view");
+    let mut qemu =
+        QemuInstance::boot_with_options(&case, &[], &[("app_view".to_string(), job)], BootOptions::default());
+    let said = job_said(&mut qemu, "test_rs_app_view")?;
+    eprintln!("{said}");
+    let rest = qemu.drain_serial(Duration::from_secs(5));
+    eprintln!("{rest}");
+    Ok(())
+}
+
 /// Run `command` as a job of the boot, to exit 0: what it said.
 fn job_said(qemu: &mut QemuInstance, command: &str) -> Result<String, String> {
     let result = qemu.run_test(command, Duration::from_secs(60));

gbae-measure.log

17:31  up 10 days,  5:16, 6 users, load averages: 36.16 52.85 71.59
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 5.70s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:31:58 running 1 tests, 12 wide

15:31:58   RUN   app_view_qemu
15:31:58   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:32:01 external deps changed: cleaning <worktree>/tests/toyos-rust-tests/target/aarch64-unknown-toyos
15:32:01 external deps changed: cleaning <worktree>/tests/toyos-rust-tests/target/x86_64-unknown-toyos
15:32:05   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (7s)
15:32:05   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:32:18   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (13s)
15:32:24 supervisor: launcher: the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder
  a package named after the shell's row: refused (other error)
supervisor: launcher: appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory
  a package whose folder is a file: refused (other error)
GBAE cwd /
GBAE menu / -> ["apps/", "boot/", "config/", "home/", "log/", "media/", "state/", "system/", "tmp/"]
GBAE menu /tmp -> ["../"]
GBAE menu /system -> ["../", "bin/", "etc/"]
GBAE menu /system/etc -> ["../"]
GBAE menu /system/bin -> ["../"]
GBAE menu /state -> ["../"]
GBAE menu /media -> ["../"]
GBAE menu /log -> ["../"]
GBAE menu /home -> ["../"]
GBAE menu /config -> ["../"]
GBAE menu /boot -> ["../"]
GBAE menu /apps -> ["../"]
GBAE browsable roms []
GBAE load_rom(/home/toy/Downloads/measure.gba) -> Err(Kind(NotFound))
GBAE load_rom(/home/toy/Apps/appview/Data/measure.gba) -> Ok(3)
GBAE load_rom(Data/measure.gba) -> Err(Kind(NotFound))
GBAE config /home/toy/Apps/appview/.config/gbae/config save -> Ok(()), load -> Ok("volume = 5\n")
GBAE cwd /home/toy
GBAE menu /home/toy -> ["../"]
GBAE menu /home -> ["../"]
GBAE menu / -> ["apps/", "boot/", "config/", "home/", "log/", "media/", "state/", "system/", "tmp/"]
GBAE menu /tmp -> ["../"]
GBAE menu /system -> ["../", "bin/", "etc/"]
GBAE menu /system/etc -> ["../"]
GBAE menu /system/bin -> ["../"]
GBAE menu /state -> ["../"]
GBAE menu /media -> ["../"]
GBAE menu /log -> ["../"]
GBAE menu /config -> ["../"]
GBAE menu /boot -> ["../"]
GBAE menu /apps -> ["../"]
GBAE browsable roms []
GBAE load_rom(/home/toy/Downloads/measure.gba) -> Err(Kind(NotFound))
GBAE load_rom(/home/toy/Apps/appview/Data/measure.gba) -> Ok(3)
GBAE load_rom(Data/measure.gba) -> Err(Kind(NotFound))
GBAE config /home/toy/Apps/appview/.config/gbae/config save -> Ok(()), load -> Ok("volume = 5\n")
  its own package reads
  std's write into its package: refused
  an open to write: refused
  an open to append: refused
  an open to truncate: refused
  an open to create: refused
  an open to create anew: refused
  mkdir: refused
  rmdir: refused
  unlink: refused
  rename: refused
  symlink: refused
  every arm held
  the app's write is in /home/toy/Apps/appview/Data
app_view: the app saw its own package read-only and its own folder as HOME, and nothing else


15:32:29   PASS  app_view_qemu  (11s)
15:32:29   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:32:29 host: fastest boot 5466 ms against the reference 1424 ms — liveness ceilings paid at 3.84x
15:32:29 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:32:29 test result: ok. 1 passed, 1 total (31.6s; workers: 21s building, 11s testing)
EXIT=0
17:32  up 10 days,  5:16, 6 users, load averages: 33.84 50.16 69.75

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Round 2 mutation patches and runner, at head 0ad87a593. Each patch is checked with git apply --check, applied, run, and restored with git checkout -- . in the same script (run.sh below), and the tree was clean after each (summary.txt). Guest arms (G*) run app_view under QEMU through harness.patch, a temporary machine test app_view_qemu: the committed verdict is the app_view metal row. Paths sanitized.

H1-rename-reads

diff --git a/userland/fileserver/src/rights.rs b/userland/fileserver/src/rights.rs
index 474f8e356..35f0bdfcc 100644
--- a/userland/fileserver/src/rights.rs
+++ b/userland/fileserver/src/rights.rs
@@ -13,7 +13,7 @@ use toyos::fs::*;
 pub fn changes(op: u32, flags: u64) -> Option<bool> {
     match op {
         OPEN => Some(flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0),
-        HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => Some(false),
+        RENAME | HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => Some(false),
         WRITE | TRUNCATE | MKDIR | RMDIR | UNLINK | RENAME | SYMLINK | STREAM => Some(true),
         _ => None,
     }

H2-any-access-byte-reads-write

diff --git a/toyos/src/fs.rs b/toyos/src/fs.rs
index 303fc2e93..3eccf459f 100644
--- a/toyos/src/fs.rs
+++ b/toyos/src/fs.rs
@@ -206,7 +206,7 @@ impl<'a> Grant<'a> {
         let access = match rest[8] {
             0 => Access::ReadOnly,
             1 => Access::ReadWrite,
-            _ => return None,
+            _ => Access::ReadWrite,
         };
         let root = core::str::from_utf8(&rest[9..]).ok()?;
         canonical(root).then_some(Self { share, access, root })

H3-package-dir-writable

diff --git a/toyos-manifest/src/lib.rs b/toyos-manifest/src/lib.rs
index 13ffce908..62b5a75ef 100644
--- a/toyos-manifest/src/lib.rs
+++ b/toyos-manifest/src/lib.rs
@@ -297,7 +297,7 @@ impl Program {
     /// (`issues/every-program-sees-only-the-files-it-was-given.md`, stage 2).
     pub fn view(&self) -> Vec<View> {
         match self.package() {
-            Some(name) => vec![data_dir(package::Package::dir(name), false), data_dir(app_home(name), true)],
+            Some(name) => vec![data_dir(package::Package::dir(name), true), data_dir(app_home(name), true)],
             None => whole_tree(),
         }
     }

H4-row-named-package-allowed

diff --git a/toyos-manifest/src/lib.rs b/toyos-manifest/src/lib.rs
index 13ffce908..9d224f4c9 100644
--- a/toyos-manifest/src/lib.rs
+++ b/toyos-manifest/src/lib.rs
@@ -332,9 +332,6 @@ impl Manifest {
     /// named after a row the image declares is refused** ([`row_named`]): its
     /// folder of the home would be that row's.
     pub fn app_row(&self, name: &str, program: &str) -> Result<Program, String> {
-        if self.program(name).is_some() {
-            return Err(row_named(name));
-        }
         Ok(Program {
             name: name.to_string(),
             path: program.to_string(),

H5-unknown-request-is-a-write

diff --git a/userland/fileserver/src/rights.rs b/userland/fileserver/src/rights.rs
index 474f8e356..b72d97903 100644
--- a/userland/fileserver/src/rights.rs
+++ b/userland/fileserver/src/rights.rs
@@ -15,7 +15,7 @@ pub fn changes(op: u32, flags: u64) -> Option<bool> {
         OPEN => Some(flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0),
         HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => Some(false),
         WRITE | TRUNCATE | MKDIR | RMDIR | UNLINK | RENAME | SYMLINK | STREAM => Some(true),
-        _ => None,
+        _ => Some(true),
     }
 }
 

harness

diff --git a/tests/toyos.rs b/tests/toyos.rs
index 008c522ec..a10bbb6d4 100644
--- a/tests/toyos.rs
+++ b/tests/toyos.rs
@@ -334,6 +334,7 @@ const MACHINE_TESTS: &[&str] = &[
     // reads it have no host build, and the T14 boots from a stick beside an
     // NVMe disk that is another system's.
     "nvme_disk_keeps_log_and_home",
+    "app_view_qemu",
 ];
 
 /// **The metal profile**: which registrations run on the ThinkPad T14, what
@@ -3200,6 +3201,7 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> {
         "bar_map_again" => bar_map_again(test_config),
         "console_image_boots" => console_image_boots(),
         "nvme_disk_keeps_log_and_home" => nvme_disk_keeps_log_and_home(test_config),
+        "app_view_qemu" => app_view_qemu(),
         other => Err(format!("unknown machine test {other}")),
     }
 }
@@ -3239,6 +3241,18 @@ fn served_by_diskserver(qemu: &mut QemuInstance, console: &mut String) -> Result
     Ok(())
 }
 
+/// `app_view` under QEMU, for mutations and the negative control only: the
+/// verdict is the `app_view` metal row's.
+fn app_view_qemu() -> Result<(), String> {
+    let case = compile::repo_root().join("tests/proctreecase");
+    let (_, job) = suite_bin(qemu::SUITE_ARCH, "app_view");
+    let mut qemu =
+        QemuInstance::boot_with_options(&case, &[], &[("app_view".to_string(), job)], BootOptions::default());
+    let said = job_said(&mut qemu, "test_rs_app_view")?;
+    eprintln!("{said}");
+    Ok(())
+}
+
 /// Run `command` as a job of the boot, to exit 0: what it said.
 fn job_said(qemu: &mut QemuInstance, command: &str) -> Result<String, String> {
     let result = qemu.run_test(command, Duration::from_secs(60));

G1-fileserver-ignores-access

diff --git a/userland/fileserver/src/main.rs b/userland/fileserver/src/main.rs
index 662115a16..5c4e470d5 100644
--- a/userland/fileserver/src/main.rs
+++ b/userland/fileserver/src/main.rs
@@ -463,7 +463,7 @@ impl Server {
             rx: ipc::FrameRx::new(),
             root: grant.root.to_string(),
             share: grant.share,
-            writes: grant.access == Access::ReadWrite && self.volume.writable(),
+            writes: self.volume.writable(),
             window: None,
             fids: BTreeMap::new(),
             next_fid: 1,

G2-supervisor-mints-every-dir-writable

diff --git a/userland/supervisor/src/main.rs b/userland/supervisor/src/main.rs
index 62fcba2c7..d6fe18176 100644
--- a/userland/supervisor/src/main.rs
+++ b/userland/supervisor/src/main.rs
@@ -2327,7 +2327,7 @@ const _: () = assert!(
 /// A grant on `acceptor`, `dir`'s role's port, naming `share`, by the
 /// namespace name a program opens it under.
 fn mint(acceptor: &Acceptor, share: u64, dir: &View) -> (String, Connector) {
-    let access = if dir.write { Access::ReadWrite } else { Access::ReadOnly };
+    let access = Access::ReadWrite;
     let mut badge = [0u8; MAX_BADGE];
     let badge = Grant { share, access, root: &dir.root }
         .encode(&mut badge)

G3-launch-without-its-folder

diff --git a/userland/supervisor/src/main.rs b/userland/supervisor/src/main.rs
index 62fcba2c7..0ffbc7dc2 100644
--- a/userland/supervisor/src/main.rs
+++ b/userland/supervisor/src/main.rs
@@ -1638,8 +1638,6 @@ impl Supervisor<'_> {
         if program.package().is_some() {
             if let Err(why) = self.make_app_home(program) {
                 say!("supervisor: launcher: {} was not started: {why}", program.name);
-                let _ = conn.try_signal(launch::MSG_REFUSED);
-                return;
             }
         }
         let started = start(

G4-row-named-package-allowed

diff --git a/toyos-manifest/src/lib.rs b/toyos-manifest/src/lib.rs
index 13ffce908..9d224f4c9 100644
--- a/toyos-manifest/src/lib.rs
+++ b/toyos-manifest/src/lib.rs
@@ -332,9 +332,6 @@ impl Manifest {
     /// named after a row the image declares is refused** ([`row_named`]): its
     /// folder of the home would be that row's.
     pub fn app_row(&self, name: &str, program: &str) -> Result<Program, String> {
-        if self.program(name).is_some() {
-            return Err(row_named(name));
-        }
         Ok(Program {
             name: name.to_string(),
             path: program.to_string(),

run.sh

#!/bin/zsh
# Each patch: checked, applied, run, restored, in one pass.
W=<worktree>
M=<scratch>/r2/mut
cd $W
run() { # name cmd patches...
  local name=$1 cmd=$2; shift 2
  local log=$M/mut-$name.log
  : > $log
  for p in "$@"; do
    git apply --check $M/$p.patch >> $log 2>&1 || { echo "APPLY-CHECK FAILED $p" >> $log; echo "$name EXIT=apply-failed" >> $M/summary.txt; return; }
    git apply $M/$p.patch
  done
  eval "$cmd" >> $log 2>&1
  local e=$?
  echo "EXIT=$e" >> $log
  echo "$name EXIT=$e" >> $M/summary.txt
  git checkout -- .
  git status --porcelain --ignore-submodules=none >> $M/summary.txt
}
: > $M/summary.txt
uptime >> $M/summary.txt
run H1-rename-reads "cargo test -p fileserver --lib" H1-rename-reads
run H5-unknown-request-is-a-write "cargo test -p fileserver --lib" H5-unknown-request-is-a-write
run H2-any-access-byte-reads-write "cargo test -p toyos --lib" H2-any-access-byte-reads-write
run H3-package-dir-writable "cargo test -p toyos-manifest" H3-package-dir-writable
run H4-row-named-package-allowed "cargo test -p toyos-manifest" H4-row-named-package-allowed
run G0-harness-alone "cargo test --test toyos-build -- app_view_qemu" harness
run G1-fileserver-ignores-access "cargo test --test toyos-build -- app_view_qemu" harness G1-fileserver-ignores-access
run G2-supervisor-mints-every-dir-writable "cargo test --test toyos-build -- app_view_qemu" harness G2-supervisor-mints-every-dir-writable
run G3-launch-without-its-folder "cargo test --test toyos-build -- app_view_qemu" harness G3-launch-without-its-folder
run G4-row-named-package-allowed "cargo test --test toyos-build -- app_view_qemu" harness G4-row-named-package-allowed
run NC-whole-change-reverted "cargo test --test toyos-build -- app_view_qemu" NC-whole-change-reverted
uptime >> $M/summary.txt
echo DONE >> $M/summary.txt

summary.txt

17:39  up 10 days,  5:23, 6 users, load averages: 42.31 42.36 57.78
H1-rename-reads EXIT=101
H5-unknown-request-is-a-write EXIT=101
H2-any-access-byte-reads-write EXIT=101
H3-package-dir-writable EXIT=101
H4-row-named-package-allowed EXIT=101
G0-harness-alone EXIT=0
G1-fileserver-ignores-access EXIT=1
G2-supervisor-mints-every-dir-writable EXIT=1
G3-launch-without-its-folder EXIT=1
G4-row-named-package-allowed EXIT=1
NC-whole-change-reverted EXIT=1
17:43  up 10 days,  5:27, 6 users, load averages: 45.02 44.39 55.00
DONE

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Round 2 negative control at head 0ad87a593: the whole production change (toyos/src/fs.rs, userland/fileserver, userland/supervisor, toyos-manifest, system.toml) reverted onto d6298c83e, the merge base the green arm (G0) was measured on; the test, tests/proctreecase and the harness kept, with the judge's one call to toyos_manifest::row_named spelled as its literal. Paths sanitized.

diff --git a/system.toml b/system.toml
index fdc2ef3c6..b5430d2c9 100644
--- a/system.toml
+++ b/system.toml
@@ -26,8 +26,7 @@ start = ["logkeeper", "diskserver", "fileserver", "compositor", "soundserver", "
 # a row read out of one would be a directory deciding what the machine hands
 # out; this is the image's answer, one for all of them. Connectors only —
 # `devices` and `syscap` have no spelling here, so nothing installed claims
-# hardware or enters the RT band — and no directory: a package sees its own
-# `/apps/<name>` read-only and its own `/home/toy/Apps/<name>`, its `HOME`.
+# hardware or enters the RT band.
 [apps]
 receives = ["compositor", "soundserver", "filepicker"]
 
diff --git a/tests/toyos.rs b/tests/toyos.rs
index 008c522ec..d066a6155 100644
--- a/tests/toyos.rs
+++ b/tests/toyos.rs
@@ -334,6 +334,7 @@ const MACHINE_TESTS: &[&str] = &[
     // reads it have no host build, and the T14 boots from a stick beside an
     // NVMe disk that is another system's.
     "nvme_disk_keeps_log_and_home",
+    "app_view_qemu",
 ];
 
 /// **The metal profile**: which registrations run on the ThinkPad T14, what
@@ -3200,6 +3201,7 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> {
         "bar_map_again" => bar_map_again(test_config),
         "console_image_boots" => console_image_boots(),
         "nvme_disk_keeps_log_and_home" => nvme_disk_keeps_log_and_home(test_config),
+        "app_view_qemu" => app_view_qemu(),
         other => Err(format!("unknown machine test {other}")),
     }
 }
@@ -3239,6 +3241,18 @@ fn served_by_diskserver(qemu: &mut QemuInstance, console: &mut String) -> Result
     Ok(())
 }
 
+/// `app_view` under QEMU, for mutations and the negative control only: the
+/// verdict is the `app_view` metal row's.
+fn app_view_qemu() -> Result<(), String> {
+    let case = compile::repo_root().join("tests/proctreecase");
+    let (_, job) = suite_bin(qemu::SUITE_ARCH, "app_view");
+    let mut qemu =
+        QemuInstance::boot_with_options(&case, &[], &[("app_view".to_string(), job)], BootOptions::default());
+    let said = job_said(&mut qemu, "test_rs_app_view")?;
+    eprintln!("{said}");
+    Ok(())
+}
+
 /// Run `command` as a job of the boot, to exit 0: what it said.
 fn job_said(qemu: &mut QemuInstance, command: &str) -> Result<String, String> {
     let result = qemu.run_test(command, Duration::from_secs(60));
@@ -3575,7 +3589,7 @@ fn launch_authority(back: &metal::Readback) -> Result<(), String> {
 fn app_view(back: &metal::Readback) -> Result<(), String> {
     back.job_passed("test_rs_app_view")?;
     let log = back.log();
-    let named = format!("supervisor: launcher: {}", toyos_manifest::row_named("shell"));
+    let named = format!("supervisor: launcher: {}", "the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder");
     for said in [
         "  every arm held",
         "app_view: the app saw its own package read-only and its own folder as HOME, and nothing else",
diff --git a/toyos-manifest/src/lib.rs b/toyos-manifest/src/lib.rs
index 13ffce908..00ef53351 100644
--- a/toyos-manifest/src/lib.rs
+++ b/toyos-manifest/src/lib.rs
@@ -64,22 +64,6 @@ pub fn session_home() -> String {
     format!("/home/{USER}")
 }
 
-/// An installed package's own folder in the session user's home: its `HOME`,
-/// and the one directory of the home its view holds.
-pub fn app_home(name: &str) -> String {
-    format!("{}/Apps/{name}", session_home())
-}
-
-/// Why a launch of the package `name` is refused when a row the image declares
-/// has that name: [`app_home`] is a folder that row keeps its own in.
-pub fn row_named(name: &str) -> String {
-    format!("the package {name} is named after a row the image declares, and {} is that row's folder", app_home(name))
-}
-
-/// What an app's own folder holds, made with it: where it keeps its config,
-/// data, cache and state. English on disk, as every home folder is.
-pub const APP_FOLDERS: [&str; 4] = ["Config", "Data", "Cache", "State"];
-
 /// Where each system service keeps its own persistent data, one directory per
 /// program key.
 pub const STATE: &str = "/state";
@@ -112,42 +96,6 @@ pub fn role_dirs(role: &str) -> Option<&'static [RoleDir]> {
     ROLES.iter().find(|(name, _)| *name == role).map(|(_, dirs)| *dirs)
 }
 
-/// One directory capability in a program's view: the grant the supervisor
-/// mints on `role`'s port (`toyos::fs::Grant`).
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct View {
-    pub role: &'static str,
-    /// What the program's namespace calls it, after `fs:`.
-    pub dir: String,
-    /// Where it is on the role's volume.
-    pub root: String,
-    /// Whether a request that changes what it holds is served.
-    pub write: bool,
-}
-
-/// Every directory every role serves, each read-write: the view of a program
-/// no narrower one is declared for.
-pub fn whole_tree() -> Vec<View> {
-    ROLES
-        .iter()
-        .flat_map(|(role, dirs)| {
-            dirs.iter().map(|d| View { role, dir: d.dir.to_string(), root: d.root.to_string(), write: true })
-        })
-        .collect()
-}
-
-/// The DATA directory `dir`, beneath one DATA serves.
-fn data_dir(dir: String, write: bool) -> View {
-    let role = "data";
-    let parent = role_dirs(role)
-        .expect("DATA is a role")
-        .iter()
-        .find(|d| dir.strip_prefix(d.dir).is_some_and(|rest| rest.starts_with('/')))
-        .unwrap_or_else(|| panic!("manifest: {dir} is beneath no directory DATA serves"));
-    let root = format!("{}{}", parent.root, &dir[parent.dir.len()..]);
-    View { role, dir, root, write }
-}
-
 /// How often a `restart` row is started again before the supervisor gives up on it: at
 /// most this many ends inside [`RESTART_WINDOW_SECS`]. Past it the row's ports
 /// close, and a client's next connection is answered `Gone`.
@@ -273,32 +221,12 @@ impl Program {
         self.slots || self.receives.iter().any(|r| r == SWAP_PORT)
     }
 
-    /// The installed package this row launches: a row [`Manifest::app_row`]
-    /// made.
-    pub fn package(&self) -> Option<&str> {
-        package::package_of(&self.path)
-    }
-
     /// The `HOME` the supervisor starts this row with. A location grants nothing: what
     /// the program can reach is its view's business, never this string's.
     pub fn home(&self) -> String {
-        match (self.service, self.package()) {
-            (true, _) => format!("{STATE}/{}", self.name),
-            (false, Some(name)) => app_home(name),
-            (false, None) => session_home(),
-        }
-    }
-
-    /// The directories this row's program is endowed. **An installed package
-    /// sees its own directory read-only and its own folder of the home
-    /// read-write, and nothing else any role serves**: no other package, no
-    /// other part of the home, no `/config`, `/state`, `/log` or `/boot`.
-    /// Every other row sees the whole tree, until each declares its own
-    /// (`issues/every-program-sees-only-the-files-it-was-given.md`, stage 2).
-    pub fn view(&self) -> Vec<View> {
-        match self.package() {
-            Some(name) => vec![data_dir(package::Package::dir(name), false), data_dir(app_home(name), true)],
-            None => whole_tree(),
+        match self.service {
+            true => format!("{STATE}/{}", self.name),
+            false => session_home(),
         }
     }
 }
@@ -311,11 +239,10 @@ pub struct Manifest {
     /// Names the supervisor serves itself. The supervisor is in every image and is no `[programs]`
     /// key, so these have no declaration to come from.
     pub supervisor_serves: Vec<String>,
-    /// The connectors every program launched from `/apps` is given beside its
-    /// view ([`Program::view`]), and nothing else. `/apps` is writable to
-    /// the installer, so this row is the image's rather than the package's —
-    /// which is why a device class and a `syscap` right have no spelling on
-    /// the package side at all.
+    /// The namespace every program launched from `/apps` is given: connectors,
+    /// and nothing else. A package directory is writable, so this row is the
+    /// image's rather than the package's — which is why a device class and a
+    /// `syscap` right have no spelling on the package side at all.
     pub apps: Vec<String>,
     /// Program names, in the order `[boot] start` gave them — which orders
     /// nothing, because every port exists before any server runs.
@@ -328,19 +255,14 @@ impl Manifest {
     }
 
     /// The row a launch of an installed package is built from: synthesized,
-    /// because a package has no `[programs]` key to hold one. **A package
-    /// named after a row the image declares is refused** ([`row_named`]): its
-    /// folder of the home would be that row's.
-    pub fn app_row(&self, name: &str, program: &str) -> Result<Program, String> {
-        if self.program(name).is_some() {
-            return Err(row_named(name));
-        }
-        Ok(Program {
+    /// because a package has no `[programs]` key to hold one.
+    pub fn app_row(&self, name: &str, program: &str) -> Program {
+        Program {
             name: name.to_string(),
             path: program.to_string(),
             receives: self.apps.clone(),
             ..Program::default()
-        })
+        }
     }
 
     /// Every `serves` name in the whole manifest, not only the ones [`start`]
@@ -592,7 +514,7 @@ mod tests {
     /// row's construction rather than by a check.
     #[test]
     fn a_package_row_is_connectors_and_nothing_else() {
-        let row = sample().app_row("gbae", "/apps/gbae/gbae").unwrap();
+        let row = sample().app_row("gbae", "/apps/gbae/gbae");
         assert_eq!(row.receives, ["compositor", "soundserver"]);
         assert!(row.devices.is_empty());
         assert!(row.syscap.is_empty());
@@ -628,81 +550,10 @@ mod tests {
         let m = sample();
         assert_eq!(m.program("soundserver").unwrap().home(), "/state/soundserver");
         assert_eq!(m.program("terminal").unwrap().home(), "/home/toy");
+        assert_eq!(m.app_row("gbae", "/apps/gbae/gbae").home(), "/home/toy");
         let m = parse("program sshserver /system/bin/sshserver\nservice\nprogram shell /system/bin/shell\n");
         assert!(m.program("sshserver").unwrap().service);
         assert!(!m.program("shell").unwrap().service);
-        // The shell keeps its history under the session's home, in its own
-        // `Apps/shell` (`OWN_FOLDER` in `userland/shell`).
-        assert_eq!(m.program("shell").unwrap().home(), "/home/toy");
-    }
-
-    /// **An installed package's `HOME` is its own folder** of the session
-    /// user's home, the layout's `/home/<user>/Apps/<name>`.
-    #[test]
-    fn a_package_s_home_is_its_own_folder() {
-        let row = sample().app_row("gbae", "/apps/gbae/gbae").unwrap();
-        assert_eq!(row.package(), Some("gbae"));
-        assert_eq!(row.home(), "/home/toy/Apps/gbae");
-        assert_eq!(sample().program("compositor").unwrap().package(), None);
-    }
-
-    /// **A package named after a declared row is refused**, by name: the
-    /// shell keeps its history in `Apps/shell`, which would be the package's
-    /// folder.
-    #[test]
-    fn a_package_named_after_a_declared_row_is_refused() {
-        let m = parse("program shell /system/bin/shell\n");
-        assert_eq!(m.app_row("shell", "/apps/shell/shell"), Err(row_named("shell")));
-        assert!(row_named("shell").contains("/home/toy/Apps/shell"));
-        let s = sample();
-        for row in &s.programs {
-            assert_eq!(s.app_row(&row.name, &format!("/apps/{0}/{0}", row.name)), Err(row_named(&row.name)));
-        }
-        assert!(m.app_row("gbae", "/apps/gbae/gbae").is_ok());
-    }
-
-    fn views(row: &Program) -> Vec<(&'static str, String, String, bool)> {
-        row.view().into_iter().map(|v| (v.role, v.dir, v.root, v.write)).collect()
-    }
-
-    /// **An installed package sees its own directory read-only and its own
-    /// folder read-write, and nothing else any role serves.** Spelled out
-    /// whole, so a third directory, a wider root or a writable package is red.
-    #[test]
-    fn a_package_s_view_is_its_own_directory_read_only_and_its_own_folder() {
-        let row = sample().app_row("gbae", "/apps/gbae/gbae").unwrap();
-        assert_eq!(
-            views(&row),
-            [
-                ("data", "/apps/gbae".into(), "apps/gbae".into(), false),
-                ("data", "/home/toy/Apps/gbae".into(), "home/toy/Apps/gbae".into(), true),
-            ]
-        );
-        // The longest name a package has is still beneath its own directories.
-        let longest = "n".repeat(MAX_PROGRAM_NAME);
-        let row = sample().app_row(&longest, &format!("/apps/{longest}/{longest}")).unwrap();
-        assert_eq!(
-            views(&row).into_iter().map(|(_, _, root, write)| (root, write)).collect::<Vec<_>>(),
-            [(format!("apps/{longest}"), false), (format!("home/toy/Apps/{longest}"), true)]
-        );
-    }
-
-    /// Every row the image declares sees the whole tree read-write, the
-    /// installer and the shell included: neither has authority over `/apps`
-    /// the other lacks.
-    #[test]
-    fn every_declared_row_sees_the_whole_tree_read_write() {
-        let m = parse("program pkg /system/bin/pkg\nprogram shell /system/bin/shell\n");
-        let whole: Vec<_> = ["/apps", "/config", "/home", "/state", "/log", "/boot"]
-            .iter()
-            .zip(["apps", "config", "home", "state", "", ""])
-            .zip(["data", "data", "data", "data", "log", "boot"])
-            .map(|((dir, root), role)| (role, dir.to_string(), root.to_string(), true))
-            .collect();
-        let s = sample();
-        for row in [m.program("pkg").unwrap(), m.program("shell").unwrap(), s.program("compositor").unwrap()] {
-            assert_eq!(views(row), whole, "{}", row.name);
-        }
     }
 
     #[test]
diff --git a/toyos-manifest/src/package.rs b/toyos-manifest/src/package.rs
index b616fc62b..08e3182d5 100644
--- a/toyos-manifest/src/package.rs
+++ b/toyos-manifest/src/package.rs
@@ -4,8 +4,8 @@
 //! to resolve a launch, so the format lives beside [`crate::Manifest`] for the
 //! same reason: one renderer, one parser, one round-trip test.
 //!
-//! **Nothing here is a grant.** `/apps` is writable to every row the image
-//! declares, so a manifest is a peer's claim about itself: it says which binary
+//! **Nothing here is a grant.** `/apps` is writable to every program that can
+//! name it, so a manifest is a peer's claim about itself: it says which binary
 //! *of its own directory* a launch starts. A device, a right and another
 //! package's binary have no spelling in this file at all.
 //!
diff --git a/toyos/src/fs.rs b/toyos/src/fs.rs
index 303fc2e93..5c6b96cb7 100644
--- a/toyos/src/fs.rs
+++ b/toyos/src/fs.rs
@@ -4,8 +4,8 @@
 //! **A directory capability is a connector in the program's namespace**, named
 //! [`CAPABILITY_PREFIX`] and the absolute directory it serves (`fs:/home`).
 //! Each is a connector to its role's one port, which the supervisor minted with
-//! a [`Grant`]: the directory, whether it may be changed, and whose share of
-//! the server it spends. The kernel stamps that on every connection made through it and answers it to the
+//! a [`Grant`]: the directory, and whose share of the server it spends. The
+//! kernel stamps that on every connection made through it and answers it to the
 //! port's acceptor alone, so the server reads what was granted off the
 //! connection and nothing the client says. A program names a file only under a
 //! directory it holds, and the kernel's part is who holds which connector.
@@ -152,64 +152,44 @@ pub struct Grant<'a> {
     /// one service it starts itself or one login session, and for every
     /// launch made from it that opens no session.
     pub share: u64,
-    /// Whether a request that changes what the directory holds is served.
-    pub access: Access,
     /// The directory, as a path on the role's volume, every path on the
     /// connection is resolved beneath: `home`, or the empty path for a volume
     /// served whole. [`canonical`], and at most [`MAX_GRANT_ROOT`] bytes.
     pub root: &'a str,
 }
 
-/// What a [`Grant`] lets its holder do to the directory.
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum Access {
-    /// Read, list and stat; every request that would change what the
-    /// directory holds is refused `PermissionDenied`.
-    ReadOnly,
-    ReadWrite,
-}
-
 /// The format [`Grant::encode`] writes. Carried because a swap replaces a file
 /// server and not the supervisor, so one server reads grants another build
 /// minted, and an older one is refused by name rather than read as this one.
-const GRANT_VERSION: u8 = 3;
+const GRANT_VERSION: u8 = 2;
 
-/// The longest root a grant carries: what one badge holds past the version,
-/// the share and the access.
-pub const MAX_GRANT_ROOT: usize = MAX_BADGE - 1 - 8 - 1;
+/// The longest root a grant carries: what one badge holds past the version and
+/// the share.
+pub const MAX_GRANT_ROOT: usize = MAX_BADGE - 1 - 8;
 
 impl<'a> Grant<'a> {
-    /// The version, the share, the access, then the root: `None` for a root
-    /// no grant can carry.
+    /// The version, the share, then the root: `None` for a root no grant
+    /// can carry.
     pub fn encode<'b>(&self, out: &'b mut [u8; MAX_BADGE]) -> Option<&'b [u8]> {
         if self.root.len() > MAX_GRANT_ROOT || !canonical(self.root) {
             return None;
         }
         out[0] = GRANT_VERSION;
         out[1..9].copy_from_slice(&self.share.to_le_bytes());
-        out[9] = match self.access {
-            Access::ReadOnly => 0,
-            Access::ReadWrite => 1,
-        };
-        let end = 10 + self.root.len();
-        out[10..end].copy_from_slice(self.root.as_bytes());
+        let end = 9 + self.root.len();
+        out[9..end].copy_from_slice(self.root.as_bytes());
         Some(&out[..end])
     }
 
     /// `None` for bytes [`Self::encode`] cannot have written.
     pub fn decode(bytes: &'a [u8]) -> Option<Self> {
         let (&version, rest) = bytes.split_first()?;
-        if version != GRANT_VERSION || rest.len() < 9 || rest.len() - 9 > MAX_GRANT_ROOT {
+        if version != GRANT_VERSION || rest.len() < 8 || rest.len() - 8 > MAX_GRANT_ROOT {
             return None;
         }
         let share = u64::from_le_bytes(rest[..8].try_into().expect("eight bytes"));
-        let access = match rest[8] {
-            0 => Access::ReadOnly,
-            1 => Access::ReadWrite,
-            _ => return None,
-        };
-        let root = core::str::from_utf8(&rest[9..]).ok()?;
-        canonical(root).then_some(Self { share, access, root })
+        let root = core::str::from_utf8(&rest[8..]).ok()?;
+        canonical(root).then_some(Self { share, root })
     }
 }
 
@@ -645,12 +625,10 @@ mod tests {
     fn a_grant_round_trips_at_every_bound() {
         let longest = "r".repeat(MAX_GRANT_ROOT);
         for (share, root) in [(0, ""), (1, "home"), (u64::MAX, "home/toy/Documents"), (7, longest.as_str())] {
-            for access in [Access::ReadOnly, Access::ReadWrite] {
-                let grant = Grant { share, access, root };
-                let mut out = [0u8; MAX_BADGE];
-                let bytes = grant.encode(&mut out).expect("a root a grant carries");
-                assert_eq!(Grant::decode(bytes), Some(grant), "{root:?} {access:?}");
-            }
+            let grant = Grant { share, root };
+            let mut out = [0u8; MAX_BADGE];
+            let bytes = grant.encode(&mut out).expect("a root a grant carries");
+            assert_eq!(Grant::decode(bytes), Some(grant), "{root:?}");
         }
     }
 
@@ -659,38 +637,30 @@ mod tests {
         let mut out = [0u8; MAX_BADGE];
         let past = "r".repeat(MAX_GRANT_ROOT + 1);
         for root in ["/home", "home/", "a//b", ".", "a/../b", past.as_str()] {
-            assert_eq!(Grant { share: 1, access: Access::ReadWrite, root }.encode(&mut out), None, "{root:?}");
+            assert_eq!(Grant { share: 1, root }.encode(&mut out), None, "{root:?}");
         }
     }
 
     #[test]
     fn bytes_no_grant_was_encoded_as_are_refused() {
         let mut out = [0u8; MAX_BADGE];
-        let good = Grant { share: 3, access: Access::ReadOnly, root: "home" }.encode(&mut out).unwrap().to_vec();
-        // Shorter than a version, a share and an access.
-        for n in 0..10 {
+        let good = Grant { share: 3, root: "home" }.encode(&mut out).unwrap().to_vec();
+        // Shorter than a version and a share.
+        for n in 0..9 {
             assert_eq!(Grant::decode(&good[..n]), None, "{n} bytes");
         }
-        // Another version, and the one before this.
-        for version in [GRANT_VERSION - 1, GRANT_VERSION + 1] {
-            let mut other = good.clone();
-            other[0] = version;
-            assert_eq!(Grant::decode(&other), None, "version {version}");
-        }
-        // An access byte that is neither, which is never read as either.
-        for access in [2, 0x80, 0xff] {
-            let mut other = good.clone();
-            other[9] = access;
-            assert_eq!(Grant::decode(&other), None, "access {access}");
-        }
+        // Another version.
+        let mut other = good.clone();
+        other[0] = GRANT_VERSION + 1;
+        assert_eq!(Grant::decode(&other), None);
         // A root the wire refuses, or not UTF-8.
         for root in [&b"/home"[..], b"a/../b", b"a//b", b"\xff"] {
-            let mut bad = good[..10].to_vec();
+            let mut bad = good[..9].to_vec();
             bad.extend_from_slice(root);
             assert_eq!(Grant::decode(&bad), None, "{root:?}");
         }
         // One byte past the longest root.
-        let mut long = good[..10].to_vec();
+        let mut long = good[..9].to_vec();
         long.extend(core::iter::repeat_n(b'r', MAX_GRANT_ROOT + 1));
         assert_eq!(Grant::decode(&long), None);
     }
diff --git a/userland/fileserver/src/lib.rs b/userland/fileserver/src/lib.rs
index 0c29712db..1dc8bb7b9 100644
--- a/userland/fileserver/src/lib.rs
+++ b/userland/fileserver/src/lib.rs
@@ -2,8 +2,7 @@
 //! cache every byte of its volume passes through ([`cache`]), the volumes it
 //! can serve ([`data`] for the bcachefs DATA role, [`fat`] for FAT32's LOG and
 //! BOOT, [`absent`] for a role with no volume this boot), and the resolver that
-//! keeps every path inside the directory a connection was given ([`resolve`]),
-//! and which requests a read-only one is refused ([`rights`]).
+//! keeps every path inside the directory a connection was given ([`resolve`]).
 //!
 //! **This is the page cache.** A block of the volume — a btree node, a FAT
 //! sector, a file's data — is read into [`cache::Cache`] once and served from
@@ -19,6 +18,5 @@ pub mod data;
 pub mod disk;
 pub mod fat;
 pub mod resolve;
-pub mod rights;
 pub mod volume;
 pub mod writeback;
diff --git a/userland/fileserver/src/main.rs b/userland/fileserver/src/main.rs
index 662115a16..a5032365e 100644
--- a/userland/fileserver/src/main.rs
+++ b/userland/fileserver/src/main.rs
@@ -13,9 +13,8 @@
 //! **A connection is what its grant says** (`toyos::fs::Grant`): the badge
 //! the supervisor minted its connector with, which the kernel stamped on it and
 //! answers this port's acceptor alone. Every path on it is resolved beneath
-//! the grant's root (`fileserver::resolve`); a request that would change what
-//! it holds, on a read-only grant or a read-only volume, is refused before the
-//! volume sees it (`fileserver::rights`).
+//! the grant's root (`fileserver::resolve`); a write on a read-only volume is
+//! refused before the volume sees it.
 //!
 //! **One share cannot take the server.** Beneath each machine-wide bound —
 //! connections waiting on their hello, connections served, streams — each
@@ -45,7 +44,6 @@ use fileserver::data::{DataVolume, Located, Probed};
 use fileserver::disk::{Claimed, Disk, Ram, Served};
 use fileserver::fat::FatVolume;
 use fileserver::resolve::{self, Found, Refusal as Escape, Resolved};
-use fileserver::rights;
 use fileserver::volume::{Kind, Meta, Node, OpenHow, Out, Volume};
 use fileserver::writeback::WriteBack;
 use toyos::endow::{self, Endowments};
@@ -92,10 +90,6 @@ const _: () = assert!(1 + MAX_SERVED + MAX_HANDSHAKES + MAX_STREAMS <= Poller::M
 /// How long an accepted connection may take to lend its window.
 const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(2);
 
-/// Why a client asking for a request number the wire does not have is let go,
-/// whatever its grant.
-const NO_SUCH_OPERATION: &str = "it asked for an operation this protocol does not have";
-
 /// The volume memory stands in for when DATA has no partition: 1 GiB of
 /// blocks, of which only what is written costs anything.
 const RAM_BLOCKS: u64 = 1 << 18;
@@ -144,8 +138,6 @@ struct Client {
     root: String,
     /// Its grant's share, which it spends.
     share: u64,
-    /// Its grant is read-write and the volume is: what it may change.
-    writes: bool,
     window: Option<SharedMemory>,
     fids: BTreeMap<u64, Fid>,
     next_fid: u64,
@@ -463,7 +455,6 @@ impl Server {
             rx: ipc::FrameRx::new(),
             root: grant.root.to_string(),
             share: grant.share,
-            writes: grant.access == Access::ReadWrite && self.volume.writable(),
             window: None,
             fids: BTreeMap::new(),
             next_fid: 1,
@@ -591,8 +582,8 @@ impl Server {
                 Ok(window) => client.window = Some(window),
                 Err(_) => return Answer::Drop("its window would not map"),
             }
-            let granted = if client.writes { RIGHT_WRITE } else { 0 };
-            return Answer::Reply(Reply { value: granted, ..Reply::ok() });
+            let rights = if self.volume.writable() { RIGHT_WRITE } else { 0 };
+            return Answer::Reply(Reply { value: rights, ..Reply::ok() });
         }
         if self.clients[&id].window.is_none() {
             return Answer::Drop("it asked before it lent a window");
@@ -622,10 +613,10 @@ impl Server {
     }
 
     fn serve_one(&mut self, id: u64, op: u32, r: Request) -> Result<Answer, SyscallError> {
-        match rights::changes(op, r.flags) {
-            None => return Ok(Answer::Drop(NO_SUCH_OPERATION)),
-            Some(true) if !self.clients[&id].writes => return Err(SyscallError::PermissionDenied),
-            Some(_) => {}
+        let changes = matches!(op, WRITE | TRUNCATE | MKDIR | RMDIR | UNLINK | RENAME | SYMLINK | STREAM)
+            || (op == OPEN && r.flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0);
+        if changes && !self.volume.writable() {
+            return Err(SyscallError::PermissionDenied);
         }
         match op {
             OPEN => {
@@ -835,7 +826,7 @@ impl Server {
                 self.dirtied();
                 Ok(Answer::Reply(Reply::ok()))
             }
-            _ => Ok(Answer::Drop(NO_SUCH_OPERATION)),
+            _ => Ok(Answer::Drop("it asked for an operation this protocol does not have")),
         }
     }
 
diff --git a/userland/fileserver/src/rights.rs b/userland/fileserver/src/rights.rs
deleted file mode 100644
index 474f8e356..000000000
--- a/userland/fileserver/src/rights.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-//! Which requests change what a connection's directory holds: each is refused
-//! `PermissionDenied` before the volume sees it, on a connection whose grant is
-//! read-only (`toyos::fs::Access`) or whose volume is.
-//!
-//! **Closed by default**: an operation this list does not name is one the
-//! wire does not have, and its client is let go on every connection, so a
-//! request added to the wire is served nowhere until it is named here.
-
-use toyos::fs::*;
-
-/// Whether request `op`, with an open's `flags`, may change what the directory
-/// holds: `None` for an operation the wire does not have.
-pub fn changes(op: u32, flags: u64) -> Option<bool> {
-    match op {
-        OPEN => Some(flags & (O_WRITE | O_APPEND | O_CREATE | O_TRUNCATE | O_CREATE_NEW) != 0),
-        HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => Some(false),
-        WRITE | TRUNCATE | MKDIR | RMDIR | UNLINK | RENAME | SYMLINK | STREAM => Some(true),
-        _ => None,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    /// Every request the wire has, by what it does to the directory: the
-    /// independent spelling `changes` is held to.
-    const READS: [u32; 10] = [HELLO, CLOSE, READ, STAT, LSTAT, FSTAT, FSYNC, SYNC, READDIR, READLINK];
-    const WRITES: [u32; 8] = [WRITE, TRUNCATE, MKDIR, RMDIR, UNLINK, RENAME, SYMLINK, STREAM];
-
-    #[test]
-    fn every_request_that_writes_changes_the_directory_and_none_that_reads_does() {
-        for op in WRITES {
-            assert_eq!(changes(op, 0), Some(true), "request {op} writes");
-        }
-        for op in READS {
-            assert_eq!(changes(op, 0), Some(false), "request {op} only reads");
-        }
-        // Every request number the wire has is one of the two, or `OPEN`.
-        let mut named: Vec<u32> = READS.iter().chain(&WRITES).copied().chain([OPEN]).collect();
-        named.sort_unstable();
-        assert_eq!(named, (HELLO..=SYNC).collect::<Vec<_>>());
-    }
-
-    /// An open changes the directory by any one flag that writes, creates or
-    /// truncates, alone or with a read.
-    #[test]
-    fn an_open_changes_the_directory_by_every_flag_but_read() {
-        assert_eq!(changes(OPEN, O_READ), Some(false));
-        assert_eq!(changes(OPEN, 0), Some(false));
-        for flag in [O_WRITE, O_APPEND, O_CREATE, O_TRUNCATE, O_CREATE_NEW] {
-            assert_eq!(changes(OPEN, flag), Some(true), "flag {flag}");
-            assert_eq!(changes(OPEN, flag | O_READ), Some(true), "flag {flag} with a read");
-        }
-    }
-
-    /// A request number the wire does not have is named neither, so its
-    /// client is let go whatever its grant.
-    #[test]
-    fn a_request_the_wire_does_not_have_is_neither() {
-        for op in [0, SYNC + 1, REPLY, LINK, u32::MAX] {
-            assert_eq!(changes(op, 0), None, "request {op}");
-        }
-    }
-}
diff --git a/userland/supervisor/src/main.rs b/userland/supervisor/src/main.rs
index 62fcba2c7..888a98181 100644
--- a/userland/supervisor/src/main.rs
+++ b/userland/supervisor/src/main.rs
@@ -42,15 +42,8 @@
 //! ([`FILES_BOUND`]).
 //!
 //! **Every program it starts gets `HOME` from its row** (`Program::home`), over
-//! anything a launching caller carried: a service its own `/state/<name>` and
-//! an installed package its own `Apps/<name>` folder of the session user's
-//! home, each made before it runs, and everything else the session user's
-//! home, made at boot.
-//!
-//! **Every program it starts holds the directories its row's view names**
-//! (`Program::view`), each a grant minted for that start ([`Grants`]): an
-//! installed package its own directory read-only and its own folder
-//! read-write, and every other row the whole tree.
+//! anything a launching caller carried: a service its own `/state/<name>`, made
+//! before it runs, and everything else the session user's home, made at boot.
 //! A launch of a program no row names is answered with the session's, which
 //! the caller's direct spawn carries in place of its own.
 //!
@@ -77,9 +70,9 @@ use toyos_swap::{Refusal, Request as SwapRequest, Word};
 
 use toyos_manifest::launch::{self as authority, Authority, Session, Sessions, Target};
 use toyos_manifest::package::{self, Package};
-use toyos_manifest::{Manifest, Program, View};
+use toyos_manifest::{Manifest, Program};
 use toyos::endow::Endowments;
-use toyos::fs::{Access, Grant, CAPABILITY_PREFIX};
+use toyos::fs::{Grant, CAPABILITY_PREFIX};
 use toyos::ipc::{self, Connection, RxStep};
 use toyos::launch::{self, Parent, Request, LAUNCHER};
 use toyos::namespace::{self, Namespace};
@@ -390,11 +383,9 @@ fn main() {
     // process nobody endows a namespace: std resolves through this one, and
     // the stop's syncs through the second.
     let files: &'static Namespace = {
-        let whole = toyos_manifest::whole_tree();
-        let own: Vec<(String, Connector)> = whole
+        let own: Vec<(String, Connector)> = role_acceptors
             .iter()
-            .filter_map(|view| Some((role_acceptors.get(view.role)?, view)))
-            .map(|(acceptor, view)| mint(acceptor, authority::SUPERVISOR_SHARE, view))
+            .flat_map(|(role, acceptor)| mint_grants(role, acceptor, authority::SUPERVISOR_SHARE))
             .collect();
         let build = || {
             let mut builder = namespace::build();
@@ -848,30 +839,6 @@ impl<'a> Supervisor<'a> {
         }
     }
 
-    /// An installed package's `HOME`, its folder of the session's home, and
-    /// its [`toyos_manifest::APP_FOLDERS`], made before it runs. `Err` is why
-    /// one is not a directory, and the launch is refused: its grant would
-    /// name nothing, or something else than a folder.
-    fn make_app_home(&mut self, program: &Program) -> Result<(), String> {
-        let home = program.home();
-        let asked = home.clone();
-        let made = self.files("an app's home", move || {
-            let folders = toyos_manifest::APP_FOLDERS.iter().map(|folder| format!("{asked}/{folder}"));
-            std::iter::once(asked.clone()).chain(folders).try_for_each(|dir| {
-                make_dir(&dir)?;
-                match std::fs::symlink_metadata(&dir)?.is_dir() {
-                    true => Ok(()),
-                    false => Err(std::io::Error::other(format!("{dir} is no directory"))),
-                }
-            })
-        });
-        match made {
-            Ok(Ok(())) => Ok(()),
-            Ok(Err(e)) => Err(format!("{home} could not be made: {e}")),
-            Err(why) => Err(format!("{home} was not made: {why}")),
-        }
-    }
-
     /// `work`, a call into the file servers, made on [`Worker`], and its
     /// answer — with every server that ends meanwhile started again, so a call
     /// its end left waiting in the port's queue goes on to the new process.
@@ -1635,13 +1602,6 @@ impl Supervisor<'_> {
         // `inherit_handle` duplicates into the child, so the supervisor's own copies go with
         // `slots` when this returns.
         self.make_home(program);
-        if program.package().is_some() {
-            if let Err(why) = self.make_app_home(program) {
-                say!("supervisor: launcher: {} was not started: {why}", program.name);
-                let _ = conn.try_signal(launch::MSG_REFUSED);
-                return;
-            }
-        }
         let started = start(
             command,
             program,
@@ -1777,10 +1737,7 @@ fn resolve<'a, V>(system: &'a Manifest, path: &str, judge: impl FnOnce(Target<'_
             installed.program
         ));
     }
-    let row = match system.app_row(name, path) {
-        Ok(row) => row,
-        Err(why) => return Resolved::Refused(why),
-    };
+    let row = system.app_row(name, path);
     let verdict = judge(Target::Package(&row));
     Resolved::Package(row, verdict)
 }
@@ -2284,7 +2241,7 @@ fn build_namespace(
 /// file-server role's port.
 ///
 /// **Each start is minted grants naming its session's share** (`toyos::fs::Grant`,
-/// [`Session::share`]), one per directory of its row's view: a service has a
+/// [`Session::share`]), one per directory of every role: a service has a
 /// share of its own through every start of it, so the servers count it, every
 /// child it spawns directly and every launch made from it that opens no
 /// session against one share, and a login session's processes against one
@@ -2296,46 +2253,45 @@ struct Grants<'a> {
 
 impl Grants<'_> {
     /// The directory capabilities `program` is endowed for one start in
-    /// `session`, by namespace name: its row's view (`Program::view`), but
-    /// that a storage row sees none, since a file server resolving a path of
-    /// its own through itself waits for ever. Asked before anything is locked,
-    /// since a storage row's start holds its own kept state.
+    /// `session`, by namespace name.
+    ///
+    /// **Every program sees the whole tree the file servers serve**, which is
+    /// the kernel's old view kept whole until each row declares its own
+    /// (`issues/every-program-sees-only-the-files-it-was-given.md`, stage 2),
+    /// with one exception: a storage row sees none, since a file server
+    /// resolving a path of its own through itself waits for ever. Asked before
+    /// anything is locked, since a storage row's start holds its own kept state.
     fn view(&self, program: &Program, session: Session) -> Vec<(String, Connector)> {
         if is_storage(program) {
             return Vec::new();
         }
-        let wanted = program.view();
         let mut view = Vec::new();
         for (role, kept) in &self.roles {
             let kept = kept.lock().expect("supervisor: a service's state is poisoned");
-            let Some((_, acceptor)) = kept.acceptors.first() else { continue };
-            for dir in wanted.iter().filter(|dir| dir.role == *role) {
-                view.push(mint(acceptor, session.share(), dir));
+            if let Some((_, acceptor)) = kept.acceptors.first() {
+                view.extend(mint_grants(role, acceptor, session.share()));
             }
         }
         view
     }
 }
 
-/// The longest root a view names, a package's folder of the home at the
-/// longest name a package has, is one a grant carries.
-const _: () = assert!(
-    "home/".len() + toyos_manifest::USER.len() + "/Apps/".len() + toyos_manifest::MAX_PROGRAM_NAME
-        <= toyos::fs::MAX_GRANT_ROOT
-);
-
-/// A grant on `acceptor`, `dir`'s role's port, naming `share`, by the
-/// namespace name a program opens it under.
-fn mint(acceptor: &Acceptor, share: u64, dir: &View) -> (String, Connector) {
-    let access = if dir.write { Access::ReadWrite } else { Access::ReadOnly };
-    let mut badge = [0u8; MAX_BADGE];
-    let badge = Grant { share, access, root: &dir.root }
-        .encode(&mut badge)
-        .unwrap_or_else(|| panic!("supervisor: {}'s root {:?} is no grant's, past the bound asserted above", dir.dir, dir.root));
-    let connector = acceptor
-        .mint(badge)
-        .unwrap_or_else(|e| panic!("supervisor: no grant on {} for share {share}: {e:?}", dir.dir));
-    (format!("{CAPABILITY_PREFIX}{}", dir.dir), connector)
+/// A grant on `acceptor`, the `role`'s port, for each of its directories,
+/// naming `share`: each by the namespace name a program opens it under.
+fn mint_grants(role: &str, acceptor: &Acceptor, share: u64) -> Vec<(String, Connector)> {
+    let dirs = toyos_manifest::role_dirs(role).expect("supervisor: the build refuses a role it does not know");
+    dirs.iter()
+        .map(|dir| {
+            let mut badge = [0u8; MAX_BADGE];
+            let badge = Grant { share, root: dir.root }
+                .encode(&mut badge)
+                .unwrap_or_else(|| panic!("supervisor: {}'s root {:?} is no grant's", dir.dir, dir.root));
+            let connector = acceptor
+                .mint(badge)
+                .unwrap_or_else(|e| panic!("supervisor: no grant on {} for share {share}: {e:?}", dir.dir));
+            (format!("{CAPABILITY_PREFIX}{}", dir.dir), connector)
+        })
+        .collect()
 }
 
 /// [`toyos_swap::PORT`] in a namespace of its own, for a program whose row

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Round 2 logs at head 0ad87a593, paths sanitized.

mut-H1-rename-reads.log

    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
   Compiling toyos v0.18.0 (<worktree>/toyos)
   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling diskserver v0.0.0 (<worktree>/userland/diskserver)
   Compiling fileserver v0.0.0 (<worktree>/userland/fileserver)
warning: unreachable pattern
  --> userland/fileserver/src/rights.rs:17:53
   |
16 |         RENAME | HELLO | CLOSE | READ | STAT | LSTAT | FSTAT | FSYNC | SYNC | READDIR | READLINK => Some(false),
   |         ------ matches all the relevant values
17 |         WRITE | TRUNCATE | MKDIR | RMDIR | UNLINK | RENAME | SYMLINK | STREAM => Some(true),
   |                                                     ^^^^^^ no value can reach this
   |
   = note: `#[warn(unreachable_patterns)]` (part of `#[warn(unused)]`) on by default

warning: `fileserver` (lib test) generated 1 warning
    Finished `test` profile [optimized + debuginfo] target(s) in 1.73s
     Running unittests src/lib.rs (target/debug/deps/fileserver-eb78af729d24b848)

running 36 tests
test cache::tests::a_write_past_the_disk_is_refused ... ok
test cache::tests::a_write_reaches_the_disk_at_the_flush_and_not_before ... ok
test cache::tests::contiguous_misses_are_one_request_and_a_second_read_is_none ... ok
test cache::tests::dirty_runs_go_out_in_runs ... ok
test data::tests::a_closed_file_keeps_its_length_across_a_remount ... ok
test data::tests::a_file_reads_back_what_was_written_across_pages_and_holes ... ok
test data::tests::a_shrink_zeroes_what_it_cut_and_regrowth_reads_zeros ... ok
test data::tests::a_file_unlinked_while_open_answers_gone ... ok
test data::tests::an_entry_refused_costs_only_its_own_file ... ok
test data::tests::data_is_one_partition_counted_over_both_sources ... ok
test data::tests::a_rename_moves_an_open_file_and_a_directory_with_its_contents ... ok
test fat::tests::a_device_error_is_io_and_not_not_found ... ok
test data::tests::directories_are_listed_and_refuse_what_posix_refuses ... ok
test fat::tests::a_partial_write_over_an_unreadable_block_writes_nothing ... ok
test fat::tests::a_refused_read_is_an_error_and_never_zeros ... ok
test resolve::tests::a_cycle_is_refused_and_not_followed_for_ever ... ok
test resolve::tests::a_link_that_stays_inside_by_going_up_and_back_resolves ... ok
test resolve::tests::a_plain_path_lands_under_the_root ... ok
test resolve::tests::a_relative_link_is_read_against_its_own_directory ... ok
test resolve::tests::an_absolute_link_is_handed_back_with_the_rest_of_the_path ... ok
test resolve::tests::an_empty_target_names_nothing ... ok
test resolve::tests::no_link_climbs_out_of_the_root ... ok
test resolve::tests::the_last_link_is_left_alone_when_asked ... ok
test rights::tests::a_request_the_wire_does_not_have_is_neither ... ok
test rights::tests::an_open_changes_the_directory_by_every_flag_but_read ... ok
test volume::tests::a_clock_that_always_straddles_is_refused_by_name - should panic ... ok
test rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does ... FAILED
test volume::tests::the_anchor_is_the_kernels ... ok
test writeback::tests::a_sync_that_left_a_file_unwritten_is_due_again ... ok
test cache::tests::at_the_dirty_limit_a_refused_flush_refuses_the_write_and_the_cache_grows_no_larger ... ok
test cache::tests::clean_blocks_are_bounded_and_dirty_ones_are_kept ... ok
test fat::tests::an_unreadable_entry_is_io_and_not_undated ... ok
test fat::tests::a_file_that_will_not_level_costs_only_its_own_file ... ok
test data::tests::every_file_reads_back_as_a_plain_map_of_its_accepted_writes ... ok
test data::tests::a_write_its_entry_could_not_name_is_refused_and_every_accepted_one_kept ... ok
test data::tests::a_refused_rename_over_an_open_file_keeps_its_unsynced_writes ... ok

failures:

---- rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does stdout ----

thread 'rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does' (224360619) panicked at userland/fileserver/src/rights.rs:34:13:
assertion `left == right` failed: request 15 writes
  left: Some(false)
 right: Some(true)


failures:
    rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does

test result: FAILED. 35 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.39s

error: test failed, to rerun pass `-p fileserver --lib`
EXIT=101

mut-H2-any-access-byte-reads-write.log

    Blocking waiting for file lock on package cache
   Compiling toyos v0.18.0 (<worktree>/toyos)
    Finished `test` profile [optimized + debuginfo] target(s) in 1.22s
     Running unittests src/lib.rs (target/debug/deps/toyos-f91d9b0c3685a5e7)

running 47 tests
test fs::tests::a_grant_round_trips_at_every_bound ... ok
test fs::tests::a_listing_entry_round_trips_and_a_short_one_is_refused ... ok
test fs::tests::a_path_is_canonical_only_without_empty_dot_or_dotdot_components ... ok
test fs::tests::no_grant_carries_a_root_the_wire_refuses_or_one_past_the_badge ... ok
test ipc::tests::a_batch_past_the_bound_is_refused_and_closed ... ok
test ipc::tests::a_refused_move_closes_every_handle_it_consumed ... ok
test fs::tests::bytes_no_grant_was_encoded_as_are_refused ... FAILED
test launch::tests::a_request_names_its_parent_by_one_word ... ok
test log::proof::a_position_that_never_lands_is_counted_by_the_sweep ... ok
test log::proof::a_placeholder_owned_ring_keeps_its_slots_from_every_pid_until_named ... ok
test fs::tests::the_window_copies_every_length_at_every_offset ... ok
test launch::tests::a_batch_names_each_handle_once_and_never_its_connection ... ok
test log::proof::scribbled_words_bound_the_reader_and_never_panic_it ... ok
test log::stdio::tests::a_line_a_flush_opened_is_closed_at_its_end ... ok
test log::stdio::tests::a_line_in_pieces_is_one_record ... ok
test log::stdio::tests::a_newline_ends_a_record_and_a_carriage_return_before_it_goes ... ok
test net::tests::a_gone_handle_transfer_is_a_netstack_that_is_not_there ... ok
test ipc::tests::a_taken_move_closes_nothing ... ok
test log::proof::a_ring_has_four_lanes_to_claim ... ok
test log::proof::slots_left_to_others_are_theirs ... ok
test log::stdio::tests::a_formatted_line_is_one_ended_record ... ok
test log::proof::the_region_carries_a_record_whole ... ok
test log::proof::a_full_ring_or_lane_refuses_at_once_and_never_waits ... ok
test log::stdio::tests::a_long_line_is_records_in_order_with_nothing_lost ... ok
test net::tests::a_binds_request_ends_in_its_listeners_options ... ok
test net::tests::a_code_netstack_chose_is_still_its_own_answer ... ok
test log::proof::a_real_time_write_allocates_nothing ... ok
test log::stdio::tests::a_stream_held_by_two_writers_keeps_each_line_whole ... ok
test net::tests::a_not_found_is_not_a_netstack_that_is_not_there ... ok
test net::tests::a_read_that_hung_up_is_a_netstack_that_is_not_there ... ok
test net::tests::an_accepts_answer_ends_in_its_connections_options ... ok
test net::tests::nothing_else_becomes_a_missing_netstack ... ok
test net::tests::an_option_request_is_three_words_on_the_wire ... ok
test poller::tests::a_submission_is_the_whole_entry_where_the_kernel_reads_it ... ok
test poller::tests::a_tail_the_kernel_publishes_mid_drain_is_observed ... ok
test poller::tests::every_accessor_lands_on_the_abi_offset ... ok
test poller::tests::pending_counts_what_the_kernel_has_not_claimed ... ok
test poller::tests::the_drop_counter_is_read_from_the_page ... ok
test syscap::tests::a_machine_that_grew_once_is_read_grown ... ok
test syscap::tests::a_machine_that_grows_every_round_is_refused_by_name ... ok
test syscap::tests::a_record_that_does_not_decode_is_refused_and_not_dropped ... ok
test syscap::tests::a_refused_count_is_refused_and_not_an_empty_inventory ... ok
test syscap::tests::a_refused_read_is_refused_and_not_an_empty_inventory ... ok
test syscap::tests::an_empty_inventory_is_its_count ... ok
test syscap::tests::every_record_is_read ... ok
test log::proof::a_lane_gives_its_reader_every_record_in_order_or_counts_it_refused ... ok
test log::proof::every_record_is_read_once_in_its_writers_order_or_counted_refused ... ok

failures:

---- fs::tests::bytes_no_grant_was_encoded_as_are_refused stdout ----

thread 'fs::tests::bytes_no_grant_was_encoded_as_are_refused' (224381149) panicked at toyos/src/fs.rs:684:13:
assertion `left == right` failed: access 2
  left: Some(Grant { share: 3, access: ReadWrite, root: "home" })
 right: None
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    fs::tests::bytes_no_grant_was_encoded_as_are_refused

test result: FAILED. 46 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

error: test failed, to rerun pass `-p toyos --lib`
EXIT=101

mut-H3-package-dir-writable.log

    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
    Finished `test` profile [optimized + debuginfo] target(s) in 3.37s
     Running unittests src/lib.rs (target/debug/deps/toyos_manifest-59b28a8000e6c464)

running 28 tests
test launch::tests::a_sessions_launches_spend_its_one_share ... ok
test launch::tests::an_authority_reads_back_as_written ... ok
test launch::tests::nothing_listed_is_nothing_started_and_apps_names_no_row ... ok
test launch::tests::what_encode_cannot_have_written_is_refused ... ok
test package::tests::a_launch_path_names_one_package_or_none ... ok
test package::tests::a_field_that_is_not_one_is_refused_by_name ... ok
test package::tests::a_path_the_normalizer_would_change_is_not_canonical_and_classifies_as_nothing ... ok
test package::tests::a_manifest_cannot_name_a_binary_outside_its_own_directory ... ok
test package::tests::what_a_shell_resolves_is_what_the_launcher_accepts ... ok
test package::tests::what_the_installer_writes_is_what_the_supervisor_reads ... ok
test tests::a_device_class_name_is_the_abi_s ... ok
test package::tests::a_listing_shows_only_what_the_supervisor_would_start_and_never_more_than_the_bound ... ok
test tests::a_name_that_would_not_survive_the_round_trip_is_refused ... ok
test launch::tests::a_caller_starts_what_its_row_lists_and_swap_and_update_only_in_a_login_session ... ok
test tests::a_package_named_after_a_declared_row_is_refused ... ok
test tests::a_package_row_is_connectors_and_nothing_else ... ok
test tests::a_package_s_home_is_its_own_folder ... ok
test tests::every_declared_row_sees_the_whole_tree_read_write ... ok
test tests::logread_carries_both_halves_of_reading_a_stream_that_never_blocks ... ok
test tests::records_attach_to_the_program_above_them ... ok
test tests::the_process_roster_is_its_own_name_and_its_own_bit ... ok
test tests::the_same_manifest_renders_to_the_same_bytes ... ok
test tests::what_the_build_writes_is_what_the_supervisor_reads ... ok
test tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder ... FAILED
test tests::a_role_is_one_of_the_three_and_its_directories_are_fixed ... ok
test tests::a_row_that_serves_a_port_and_is_no_service_is_refused ... ok
test tests::a_service_s_home_is_its_state_and_every_other_row_s_the_session_s ... ok
test tests::a_syscap_set_always_carries_transfer_and_never_an_invented_right ... ok

failures:

---- tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder stdout ----

thread 'tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder' (224404056) panicked at toyos-manifest/src/lib.rs:674:9:
assertion `left == right` failed
  left: [("data", "/apps/gbae", "apps/gbae", true), ("data", "/home/toy/Apps/gbae", "home/toy/Apps/gbae", true)]
 right: [("data", "/apps/gbae", "apps/gbae", false), ("data", "/home/toy/Apps/gbae", "home/toy/Apps/gbae", true)]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder

test result: FAILED. 27 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

error: test failed, to rerun pass `-p toyos-manifest --lib`
EXIT=101

mut-H4-row-named-package-allowed.log

    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
    Finished `test` profile [optimized + debuginfo] target(s) in 1.42s
     Running unittests src/lib.rs (target/debug/deps/toyos_manifest-59b28a8000e6c464)

running 28 tests
test launch::tests::an_authority_reads_back_as_written ... ok
test launch::tests::a_sessions_launches_spend_its_one_share ... ok
test launch::tests::nothing_listed_is_nothing_started_and_apps_names_no_row ... ok
test launch::tests::a_caller_starts_what_its_row_lists_and_swap_and_update_only_in_a_login_session ... ok
test launch::tests::what_encode_cannot_have_written_is_refused ... ok
test package::tests::a_field_that_is_not_one_is_refused_by_name ... ok
test package::tests::a_launch_path_names_one_package_or_none ... ok
test package::tests::a_path_the_normalizer_would_change_is_not_canonical_and_classifies_as_nothing ... ok
test package::tests::a_listing_shows_only_what_the_supervisor_would_start_and_never_more_than_the_bound ... ok
test package::tests::a_manifest_cannot_name_a_binary_outside_its_own_directory ... ok
test package::tests::what_the_installer_writes_is_what_the_supervisor_reads ... ok
test package::tests::what_a_shell_resolves_is_what_the_launcher_accepts ... ok
test tests::a_name_that_would_not_survive_the_round_trip_is_refused ... ok
test tests::a_device_class_name_is_the_abi_s ... ok
test tests::a_package_row_is_connectors_and_nothing_else ... ok
test tests::a_package_s_home_is_its_own_folder ... ok
test tests::a_package_s_view_is_its_own_directory_read_only_and_its_own_folder ... ok
test tests::a_package_named_after_a_declared_row_is_refused ... FAILED
test tests::a_role_is_one_of_the_three_and_its_directories_are_fixed ... ok
test tests::a_row_that_serves_a_port_and_is_no_service_is_refused ... ok
test tests::a_service_s_home_is_its_state_and_every_other_row_s_the_session_s ... ok
test tests::a_syscap_set_always_carries_transfer_and_never_an_invented_right ... ok
test tests::every_declared_row_sees_the_whole_tree_read_write ... ok
test tests::records_attach_to_the_program_above_them ... ok
test tests::the_process_roster_is_its_own_name_and_its_own_bit ... ok
test tests::logread_carries_both_halves_of_reading_a_stream_that_never_blocks ... ok
test tests::the_same_manifest_renders_to_the_same_bytes ... ok
test tests::what_the_build_writes_is_what_the_supervisor_reads ... ok

failures:

---- tests::a_package_named_after_a_declared_row_is_refused stdout ----

thread 'tests::a_package_named_after_a_declared_row_is_refused' (224410975) panicked at toyos-manifest/src/lib.rs:652:9:
assertion `left == right` failed
  left: Ok(Program { name: "shell", path: "/apps/shell/shell", args: [], serves: [], provides: [], receives: [], devices: [], syscap: [], slots: false, service: false, roles: [], restart: false, starts: [], login: false })
 right: Err("the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::a_package_named_after_a_declared_row_is_refused

test result: FAILED. 27 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `-p toyos-manifest --lib`
EXIT=101

mut-H5-unknown-request-is-a-write.log

    Blocking waiting for file lock on package cache
   Compiling fileserver v0.0.0 (<worktree>/userland/fileserver)
    Finished `test` profile [optimized + debuginfo] target(s) in 0.85s
     Running unittests src/lib.rs (target/debug/deps/fileserver-eb78af729d24b848)

running 36 tests
test cache::tests::a_write_past_the_disk_is_refused ... ok
test cache::tests::a_write_reaches_the_disk_at_the_flush_and_not_before ... ok
test cache::tests::contiguous_misses_are_one_request_and_a_second_read_is_none ... ok
test cache::tests::dirty_runs_go_out_in_runs ... ok
test data::tests::a_file_reads_back_what_was_written_across_pages_and_holes ... ok
test data::tests::a_closed_file_keeps_its_length_across_a_remount ... ok
test data::tests::directories_are_listed_and_refuse_what_posix_refuses ... ok
test data::tests::a_file_unlinked_while_open_answers_gone ... ok
test data::tests::a_shrink_zeroes_what_it_cut_and_regrowth_reads_zeros ... ok
test data::tests::data_is_one_partition_counted_over_both_sources ... ok
test data::tests::a_rename_moves_an_open_file_and_a_directory_with_its_contents ... ok
test fat::tests::a_device_error_is_io_and_not_not_found ... ok
test resolve::tests::a_cycle_is_refused_and_not_followed_for_ever ... ok
test resolve::tests::an_absolute_link_is_handed_back_with_the_rest_of_the_path ... ok
test data::tests::an_entry_refused_costs_only_its_own_file ... ok
test fat::tests::a_partial_write_over_an_unreadable_block_writes_nothing ... ok
test fat::tests::a_refused_read_is_an_error_and_never_zeros ... ok
test resolve::tests::a_link_that_stays_inside_by_going_up_and_back_resolves ... ok
test resolve::tests::a_plain_path_lands_under_the_root ... ok
test resolve::tests::a_relative_link_is_read_against_its_own_directory ... ok
test resolve::tests::no_link_climbs_out_of_the_root ... ok
test volume::tests::a_clock_that_always_straddles_is_refused_by_name - should panic ... ok
test resolve::tests::an_empty_target_names_nothing ... ok
test resolve::tests::the_last_link_is_left_alone_when_asked ... ok
test rights::tests::a_request_the_wire_does_not_have_is_neither ... FAILED
test rights::tests::an_open_changes_the_directory_by_every_flag_but_read ... ok
test rights::tests::every_request_that_writes_changes_the_directory_and_none_that_reads_does ... ok
test volume::tests::the_anchor_is_the_kernels ... ok
test writeback::tests::a_sync_that_left_a_file_unwritten_is_due_again ... ok
test cache::tests::at_the_dirty_limit_a_refused_flush_refuses_the_write_and_the_cache_grows_no_larger ... ok
test cache::tests::clean_blocks_are_bounded_and_dirty_ones_are_kept ... ok
test data::tests::every_file_reads_back_as_a_plain_map_of_its_accepted_writes ... ok
test fat::tests::an_unreadable_entry_is_io_and_not_undated ... ok
test fat::tests::a_file_that_will_not_level_costs_only_its_own_file ... ok
test data::tests::a_refused_rename_over_an_open_file_keeps_its_unsynced_writes ... ok
test data::tests::a_write_its_entry_could_not_name_is_refused_and_every_accepted_one_kept ... ok

failures:

---- rights::tests::a_request_the_wire_does_not_have_is_neither stdout ----

thread 'rights::tests::a_request_the_wire_does_not_have_is_neither' (224363005) panicked at userland/fileserver/src/rights.rs:62:13:
assertion `left == right` failed: request 0
  left: Some(true)
 right: None


failures:
    rights::tests::a_request_the_wire_does_not_have_is_neither

test result: FAILED. 35 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.74s

error: test failed, to rerun pass `-p fileserver --lib`
EXIT=101

mut-G0-harness-alone.log

    Blocking waiting for file lock on package cache
   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling toyos-swap v0.1.0 (<worktree>/toyos-swap)
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 13.49s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:40:03 running 1 tests, 12 wide

15:40:03   RUN   app_view_qemu
15:40:03   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:40:09 external deps changed: cleaning <worktree>/tests/toyos-rust-tests/target/x86_64-unknown-toyos
15:40:17   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (13s)
15:40:17   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:40:20 external deps changed: cleaning <worktree>/target/x86_64-unknown-toyos
15:40:32   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (15s)
15:40:42 supervisor: launcher: the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder
  a package named after the shell's row: refused (other error)
supervisor: launcher: appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory
  a package whose folder is a file: refused (other error)
  its own package reads
  std's write into its package: refused
  an open to write: refused
  an open to append: refused
  an open to truncate: refused
  an open to create: refused
  an open to create anew: refused
  mkdir: refused
  rmdir: refused
  unlink: refused
  rename: refused
  symlink: refused
  every arm held
  the app's write is in /home/toy/Apps/appview/Data
app_view: the app saw its own package read-only and its own folder as HOME, and nothing else

15:40:42   PASS  app_view_qemu  (10s)
15:40:42   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:40:42 host: fastest boot 8999 ms against the reference 1424 ms — liveness ceilings paid at 6.32x
15:40:42 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:40:42 test result: ok. 1 passed, 1 total (38.3s; workers: 29s building, 10s testing)
EXIT=0

mut-G1-fileserver-ignores-access.log

    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 2.02s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:40:45 running 1 tests, 12 wide

15:40:45   RUN   app_view_qemu
15:40:45   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:40:50   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (5s)
15:40:50   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:40:58   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (9s)
15:41:13 FAIL app_view_qemu: `test_rs_app_view` ended Some(101):
supervisor: launcher: the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder
  a package named after the shell's row: refused (other error)
supervisor: launcher: appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory
  a package whose folder is a file: refused (other error)
  its own package reads
  RED std's write into its own package was answered Ok(())
  RED fs:/apps/appview says it is writable
  RED an open to write in its own package was answered Ok(())
  RED an open to append in its own package was answered Ok(())
  RED an open to truncate in its own package was answered Ok(())
  RED an open to create in its own package was answered Ok(())
  RED an open to create anew in its own package was answered Err(Error(AlreadyExists))
  RED mkdir in its own package was answered Err(Error(AlreadyExists))
  RED rmdir in its own package was answered Ok(())
  RED unlink in its own package was answered Ok(())
  RED rename in its own package was answered Err(Error(NotFound))
  RED symlink in its own package was answered Ok(())
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:120:9:
app_view: [
    "the app ended ExitStatus(ExitStatus(1))",
    "the package's manifest is not what was installed: Err(Kind(NotFound))",
    "the package holds [\"appview\", \"link\", \"made\"], not what was installed",
]
stack backtrace:
   0:      0x1000007caf0 - _Unwind_Backtrace
   1:      0x100000652c3 - <<std[a00b235bfdc63e32]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x10000080d27 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000696ab - <std[a00b235bfdc63e32]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x10000040013 - std[a00b235bfdc63e32]::panicking::default_hook::{closure#0}
   5:      0x1000005c02e - std[a00b235bfdc63e32]::panicking::default_hook
   6:      0x1000005c1e9 - std[a00b235bfdc63e32]::panicking::panic_with_hook
   7:      0x100000400cd - std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}
   8:      0x10000038289 - std[a00b235bfdc63e32]::sys::backtrace::__rust_end_short_backtrace::<std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}, !>
   9:      0x10000040968 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008144b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x1000002650a - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000026e86 - std[a00b235bfdc63e32]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020cb0 - std[a00b235bfdc63e32]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005b406 - std[a00b235bfdc63e32]::rt::lang_start_internal
  15:      0x10000026e71 - main
  16:      0x10000062683 - std[a00b235bfdc63e32]::sys::pal::toyos::start_rust
  17:      0x1000002b06e - _start

15:41:13   FAIL  app_view_qemu  (15s)
15:41:13   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:41:13 host: fastest boot 13456 ms against the reference 1424 ms — liveness ceilings paid at 8.00x
15:41:13 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:41:13 failures:
15:41:13     app_view_qemu: `test_rs_app_view` ended Some(101):

15:41:13 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (28.5s; workers: 14s building, 15s testing)
15:41:13 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-44419-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view_qemu` (exit status: 1)
EXIT=1

mut-G2-supervisor-mints-every-dir-writable.log

   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 2.59s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:41:17 running 1 tests, 12 wide

15:41:17   RUN   app_view_qemu
15:41:17   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:41:23   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (6s)
15:41:23   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:41:35   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (13s)
15:41:50 FAIL app_view_qemu: `test_rs_app_view` ended Some(101):
supervisor: launcher: the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder
  a package named after the shell's row: refused (other error)
supervisor: launcher: appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory
  a package whose folder is a file: refused (other error)
  its own package reads
  RED std's write into its own package was answered Ok(())
  RED fs:/apps/appview says it is writable
  RED an open to write in its own package was answered Ok(())
  RED an open to append in its own package was answered Ok(())
  RED an open to truncate in its own package was answered Ok(())
  RED an open to create in its own package was answered Ok(())
  RED an open to create anew in its own package was answered Err(Error(AlreadyExists))
  RED mkdir in its own package was answered Err(Error(AlreadyExists))
  RED rmdir in its own package was answered Ok(())
  RED unlink in its own package was answered Ok(())
  RED rename in its own package was answered Err(Error(NotFound))
  RED symlink in its own package was answered Ok(())
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:120:9:
app_view: [
    "the app ended ExitStatus(ExitStatus(1))",
    "the package's manifest is not what was installed: Err(Kind(NotFound))",
    "the package holds [\"appview\", \"link\", \"made\"], not what was installed",
]
stack backtrace:
   0:      0x1000007caf0 - _Unwind_Backtrace
   1:      0x100000652c3 - <<std[a00b235bfdc63e32]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x10000080d27 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000696ab - <std[a00b235bfdc63e32]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x10000040013 - std[a00b235bfdc63e32]::panicking::default_hook::{closure#0}
   5:      0x1000005c02e - std[a00b235bfdc63e32]::panicking::default_hook
   6:      0x1000005c1e9 - std[a00b235bfdc63e32]::panicking::panic_with_hook
   7:      0x100000400cd - std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}
   8:      0x10000038289 - std[a00b235bfdc63e32]::sys::backtrace::__rust_end_short_backtrace::<std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}, !>
   9:      0x10000040968 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008144b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x1000002650a - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000026e86 - std[a00b235bfdc63e32]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020cb0 - std[a00b235bfdc63e32]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005b406 - std[a00b235bfdc63e32]::rt::lang_start_internal
  15:      0x10000026e71 - main
  16:      0x10000062683 - std[a00b235bfdc63e32]::sys::pal::toyos::start_rust
  17:      0x1000002b06e - _start

15:41:50   FAIL  app_view_qemu  (15s)
15:41:50   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:41:50 host: fastest boot 14044 ms against the reference 1424 ms — liveness ceilings paid at 8.00x
15:41:50 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:41:50 failures:
15:41:50     app_view_qemu: `test_rs_app_view` ended Some(101):

15:41:50 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (33.4s; workers: 19s building, 15s testing)
15:41:50 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-57339-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view_qemu` (exit status: 1)
EXIT=1

mut-G3-launch-without-its-folder.log

   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 1.52s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:41:53 running 1 tests, 12 wide

15:41:53   RUN   app_view_qemu
15:41:53   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:41:55   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (2s)
15:41:55   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:42:01   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (6s)
15:42:06 FAIL app_view_qemu: `test_rs_app_view` ended Some(101):
supervisor: launcher: the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder
  a package named after the shell's row: refused (other error)
supervisor: launcher: appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory
  its own package reads
  std's write into its package: refused
  an open to write: refused
  an open to append: refused
  an open to truncate: refused
  an open to create: refused
  an open to create anew: refused
  mkdir: refused
  rmdir: refused
  unlink: refused
  rename: refused
  symlink: refused
  every arm held
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:120:9:
app_view: [
    "a package whose folder is a file ran: Output { status: ExitStatus(ExitStatus(1)), stdout: \"  its own package reads\\n  std's write into its package: refused\\n  an open to write: refused\\n  an open to append: refused\\n  an open to truncate: refused\\n  an open to create: refused\\n  an open to create anew: refused\\n  mkdir: refused\\n  rmdir: refused\\n  unlink: refused\\n  rename: refused\\n  symlink: refused\\n  RED /home/toy/Apps/appview/Config is not a directory\\n  RED /home/toy/Apps/appview/Data is not a directory\\n  RED /home/toy/Apps/appview/Cache is not a directory\\n  RED /home/toy/Apps/appview/State is not a directory\\n  RED a write in its own folder was refused: entity not found\\n\", stderr: \"\" }",
]
stack backtrace:
   0:      0x1000007caf0 - _Unwind_Backtrace
   1:      0x100000652c3 - <<std[a00b235bfdc63e32]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x10000080d27 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000696ab - <std[a00b235bfdc63e32]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x10000040013 - std[a00b235bfdc63e32]::panicking::default_hook::{closure#0}
   5:      0x1000005c02e - std[a00b235bfdc63e32]::panicking::default_hook
   6:      0x1000005c1e9 - std[a00b235bfdc63e32]::panicking::panic_with_hook
   7:      0x100000400cd - std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}
   8:      0x10000038289 - std[a00b235bfdc63e32]::sys::backtrace::__rust_end_short_backtrace::<std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}, !>
   9:      0x10000040968 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008144b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x1000002650a - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000026e86 - std[a00b235bfdc63e32]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020cb0 - std[a00b235bfdc63e32]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005b406 - std[a00b235bfdc63e32]::rt::lang_start_internal
  15:      0x10000026e71 - main
  16:      0x10000062683 - std[a00b235bfdc63e32]::sys::pal::toyos::start_rust
  17:      0x1000002b06e - _start

15:42:06   FAIL  app_view_qemu  (5s)
15:42:06   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:42:06 host: fastest boot 4787 ms against the reference 1424 ms — liveness ceilings paid at 3.36x
15:42:06 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:42:06 failures:
15:42:06     app_view_qemu: `test_rs_app_view` ended Some(101):

15:42:06 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (13.6s; workers: 8s building, 5s testing)
15:42:06 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-73377-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view_qemu` (exit status: 1)
EXIT=1

mut-G4-row-named-package-allowed.log

   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling toyos-swap v0.1.0 (<worktree>/toyos-swap)
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 6.39s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:42:13 running 1 tests, 12 wide

15:42:13   RUN   app_view_qemu
15:42:13   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:42:16   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (2s)
15:42:16   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:42:27   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (11s)
15:42:39 FAIL app_view_qemu: `test_rs_app_view` ended Some(101):
supervisor: launcher: appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory
  a package whose folder is a file: refused (other error)
  its own package reads
  std's write into its package: refused
  an open to write: refused
  an open to append: refused
  an open to truncate: refused
  an open to create: refused
  an open to create anew: refused
  mkdir: refused
  rmdir: refused
  unlink: refused
  rename: refused
  symlink: refused
  every arm held
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:120:9:
app_view: [
    "a package named after the shell's row ran: Output { status: ExitStatus(ExitStatus(1)), stdout: \"  std's write into its package: refused\\n  RED home_dir() is Some(\\\"/home/toy/Apps/shell\\\"), not /home/toy/Apps/appview\\n  RED /home/toy/Apps/appview/Config is not a directory\\n  RED /home/toy/Apps/appview/Data is not a directory\\n  RED /home/toy/Apps/appview/Cache is not a directory\\n  RED /home/toy/Apps/appview/State is not a directory\\n  RED a write in its own folder was refused: permission denied\\n  RED its own manifest did not read back: Err(Kind(NotFound))\\n  RED fs:/apps/appview would not connect: NotFound\\n  RED fs:/home/toy/Apps/appview would not connect: NotFound\\n\", stderr: \"\" }",
]
stack backtrace:
   0:      0x1000007caf0 - _Unwind_Backtrace
   1:      0x100000652c3 - <<std[a00b235bfdc63e32]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x10000080d27 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000696ab - <std[a00b235bfdc63e32]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x10000040013 - std[a00b235bfdc63e32]::panicking::default_hook::{closure#0}
   5:      0x1000005c02e - std[a00b235bfdc63e32]::panicking::default_hook
   6:      0x1000005c1e9 - std[a00b235bfdc63e32]::panicking::panic_with_hook
   7:      0x100000400cd - std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}
   8:      0x10000038289 - std[a00b235bfdc63e32]::sys::backtrace::__rust_end_short_backtrace::<std[a00b235bfdc63e32]::panicking::panic_handler::{closure#0}, !>
   9:      0x10000040968 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008144b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x1000002650a - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000026e86 - std[a00b235bfdc63e32]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020cb0 - std[a00b235bfdc63e32]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005b406 - std[a00b235bfdc63e32]::rt::lang_start_internal
  15:      0x10000026e71 - main
  16:      0x10000062683 - std[a00b235bfdc63e32]::sys::pal::toyos::start_rust
  17:      0x1000002b06e - _start

15:42:39   FAIL  app_view_qemu  (11s)
15:42:39   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:42:39 host: fastest boot 10650 ms against the reference 1424 ms — liveness ceilings paid at 7.48x
15:42:39 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:42:39 failures:
15:42:39     app_view_qemu: `test_rs_app_view` ended Some(101):

15:42:39 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (25.3s; workers: 14s building, 11s testing)
15:42:39 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-75485-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view_qemu` (exit status: 1)
EXIT=1

mut-NC-whole-change-reverted.log

    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
   Compiling toyos-manifest v0.1.0 (<worktree>/toyos-manifest)
   Compiling toyos-swap v0.1.0 (<worktree>/toyos-swap)
   Compiling toyos-build v0.1.0 (<worktree>)
    Finished `test` profile [optimized + debuginfo] target(s) in 17.16s
     Running tests/toyos.rs (target/debug/deps/toyos_build-497f416569e2c965)

15:42:57 running 1 tests, 12 wide

15:42:57   RUN   app_view_qemu
15:42:57   BUILD x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu
15:43:01 external deps changed: cleaning <worktree>/tests/toyos-rust-tests/target/x86_64-unknown-toyos
15:43:07   BUILT x86_64 app_view of tests/toyos-rust-tests, for app_view_qemu  (10s)
15:43:07   BUILD x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu
15:43:10 external deps changed: cleaning <worktree>/target/x86_64-unknown-toyos
15:43:21   BUILT x86_64 kernel, loader, ROOT of tests/proctreecase, for app_view_qemu  (14s)
15:43:30 FAIL app_view_qemu: `test_rs_app_view` ended Some(101):
  its own package reads
  RED home_dir() is Some("/home/toy"), not /home/toy/Apps/appview
  RED /home/toy/Apps/appview/Config is not a directory
  RED /home/toy/Apps/appview/Cache is not a directory
  RED /home/toy/Apps/appview/State is not a directory
  RED std's write into its own package was answered Ok(())
  RED fs:/apps/appview would not connect: NotFound
  RED fs:/home/toy/Apps/appview would not connect: NotFound
  RED it holds fs:/apps
  RED it holds fs:/home
  RED it holds fs:/config
  RED it holds fs:/state
  RED it holds fs:/log
  RED it holds fs:/boot
  RED /apps/other/kept read 13 bytes
  RED /home/toy/Apps/other/Data/kept read 13 bytes
  RED /apps lists ["appview", "other", "shell"]
  RED /home lists ["toy"]
  RED /home/toy lists ["Apps", "Desktop", "Documents", "Downloads", "Fonts", "Music", "Pictures", "Videos"]
  RED /home/toy/Apps lists ["appview", "other"]
  RED /state lists ["logkeeper"]
  RED /log lists ["loader.log", "attempts", "2026-10-09-154330.log"]
  the app's write is in /home/toy/Apps/appview/Data

thread 'main' (1) panicked at src/bin/app_view.rs:120:9:
app_view: [
    "a package named after the shell's row ran: Output { status: ExitStatus(ExitStatus(1)), stdout: \"  its own package reads\\n  RED home_dir() is Some(\\\"/home/toy\\\"), not /home/toy/Apps/appview\\n  RED /home/toy/Apps/appview/Config is not a directory\\n  RED /home/toy/Apps/appview/Data is not a directory\\n  RED /home/toy/Apps/appview/Cache is not a directory\\n  RED /home/toy/Apps/appview/State is not a directory\\n  RED std's write into its own package was answered Ok(())\\n  RED fs:/apps/appview would not connect: NotFound\\n  RED fs:/home/toy/Apps/appview would not connect: NotFound\\n  RED it holds fs:/apps\\n  RED it holds fs:/home\\n  RED it holds fs:/config\\n  RED it holds fs:/state\\n  RED it holds fs:/log\\n  RED it holds fs:/boot\\n  RED /apps/other/kept read 13 bytes\\n  RED /home/toy/Apps/other/Data/kept read 13 bytes\\n  RED /apps lists [\\\"appview\\\", \\\"other\\\", \\\"shell\\\"]\\n  RED /home lists [\\\"toy\\\"]\\n  RED /home/toy lists [\\\"Apps\\\", \\\"Desktop\\\", \\\"Documents\\\", \\\"Downloads\\\", \\\"Fonts\\\", \\\"Music\\\", \\\"Pictures\\\", \\\"Videos\\\"]\\n  RED /home/toy/Apps lists [\\\"appview\\\", \\\"other\\\"]\\n  RED /state lists [\\\"logkeeper\\\"]\\n  RED /log lists [\\\"loader.log\\\", \\\"attempts\\\", \\\"2026-10-09-154330.log\\\"]\\n\", stderr: \"\" }",
    "no file could be planted where the app's folder goes: invalid input parameter",
    "the app ended ExitStatus(ExitStatus(1))",
    "the package holds [\"appview\", \"made\", \"manifest.toml\", \"sub\"], not what was installed",
]
stack backtrace:
   0:      0x1000007cbf0 - _Unwind_Backtrace
   1:      0x100000653c3 - <<std[4345d1fde8fc8ae8]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[73b405bef30eeff1]::fmt::Display>::fmt
   2:      0x10000080e27 - core[73b405bef30eeff1]::fmt::write
   3:      0x100000697ab - <std[4345d1fde8fc8ae8]::sys::stdio::toyos::Stderr as core[73b405bef30eeff1]::io::write::Write>::write_fmt
   4:      0x10000040113 - std[4345d1fde8fc8ae8]::panicking::default_hook::{closure#0}
   5:      0x1000005c12e - std[4345d1fde8fc8ae8]::panicking::default_hook
   6:      0x1000005c2e9 - std[4345d1fde8fc8ae8]::panicking::panic_with_hook
   7:      0x100000401cd - std[4345d1fde8fc8ae8]::panicking::panic_handler::{closure#0}
   8:      0x10000038389 - std[4345d1fde8fc8ae8]::sys::backtrace::__rust_end_short_backtrace::<std[4345d1fde8fc8ae8]::panicking::panic_handler::{closure#0}, !>
   9:      0x10000040a68 - __rustc[d0c72ce299f394e5]::rust_begin_unwind
  10:      0x1000008154b - core[73b405bef30eeff1]::panicking::panic_fmt
  11:      0x1000002660a - app_view[7e7d960f5edd7f5c]::main
  12:      0x10000026f86 - std[4345d1fde8fc8ae8]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  13:      0x10000020db0 - std[4345d1fde8fc8ae8]::rt::lang_start::<()>::{closure#0}
  14:      0x1000005b506 - std[4345d1fde8fc8ae8]::rt::lang_start_internal
  15:      0x10000026f71 - main
  16:      0x10000062783 - std[4345d1fde8fc8ae8]::sys::pal::toyos::start_rust
  17:      0x1000002b16e - _start

15:43:30   FAIL  app_view_qemu  (10s)
15:43:30   --- 1 guests, 0 of them not the shipping kernel, 1 kernel build(s): [""]

15:43:30 host: fastest boot 8838 ms against the reference 1424 ms — liveness ceilings paid at 6.21x
15:43:30 host: 14 core(s); a guest wider than that waits vcpus/cores longer again
15:43:30 failures:
15:43:30     app_view_qemu: `test_rs_app_view` ended Some(101):

15:43:30 test result: FAILED. 0 passed, 1 failed, 0 invalidated, 1 total (33.6s; workers: 24s building, 10s testing)
15:43:30 [toyos] this red run's serial logs are kept at <worktree>/target/red-run-serial/toyos-tmp-92721-0
error: test failed, to rerun pass `--test toyos-build`

Caused by:
  process didn't exit successfully: `<worktree>/target/debug/deps/toyos_build-497f416569e2c965 app_view_qemu` (exit status: 1)
EXIT=1

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

T14 at 0ad87a593, run by the orchestrator: boot:testcases boot:proctreecase, three boots, each image's sha256 checked against request.txt before its flash (proctreecase 68e6a454…e9e5f450bb, testcases-watchdog 784e6e5c…7274e44dd, testcases 3efa3bad…c2629e4d4), each toyos-metal --fat32-check exit 0. Judge cargo test --test toyos-build -- --metal --metal-readback <dir> boot:testcases boot:proctreecase: EXIT=0, [metal] 255 passed, 0 failed, 3 boot(s), 29 row PASS lines.

  • process_tree, launch_toctou, launch_authority, port_badge, fs_share, app_view: PASS.
  • app_view's log: the supervisor's two refusals (the package shell is named after a row the image declares, and /home/toy/Apps/shell is that row's folder; appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory), the job's arms (its own package reads; std's write, open to write/append/truncate/create/create anew, mkdir, rmdir, unlink, rename, symlink into its package each refused; …), every arm held, then ===TEST_END test_rs_app_view exit=0===.
  • testcases: every TEST_END exit 0; the 137 C cases each ccheck: 0.
  • its badge is no grant: 0 occurrences in all three boot logs.

@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #807 at 0ad87a593 (round 2).

Net lines: git diff --shortstat origin/main...0ad87a593 gives 16 files, +753 −94. Production files come to +387 −87, which is +247 −71 once their in-file tests are taken out. Test files (tests/) are +273 −3, and issue files +93 −4. The growth is the new boundary itself: the access byte, rights::changes, Program::view, make_app_home and the row-name refusal. None of it has one caller or one value, and mint_grants was deleted into mint.

Round 1 BLOCKERs

  • A package's folder could be another row's: CLOSED.
    • Manifest::app_row refuses a package named after any [programs] row, with row_named, and resolve answers Resolved::Refused.
    • The shell is the only program in the tree that keeps an Apps/<x> folder (git grep 'Apps/\|OWN_FOLDER' 0ad87a593 -- userland toyos toyos-manifest).
    • Measured by host mutation H4 (red, EXIT=101) and guest mutation G4 (red, EXIT=1).
    • The T14 proctreecase log at this head carries the supervisor's refusal line, and app_view is PASS.
  • A package launch went ahead without its folder: CLOSED.
    • make_app_home makes the folder and each of APP_FOLDERS, checks each one with symlink_metadata(..).is_dir(), and refuses with MSG_REFUSED on any Err. That includes files() answering that the worker is still busy (userland/supervisor/src/main.rs:1638-1644).
    • Measured by guest mutation G3 (red, EXIT=1).
    • The T14 log at this head carries appview was not started: /home/toy/Apps/appview could not be made: /home/toy/Apps/appview is no directory.
  • app_view belonged on a metal row: CLOSED.
    • test_rs_app_view is in PROCTREECASE's jobs, the app_view row judges it, it is in RUST_SKIP, and no MACHINE_TESTS entry or QEMU arm is left.
    • The T14 judge at this head says PASS app_view (metal-r2/judge.log).
  • gbae was guessed at: CLOSED.
    • Measured at 1c8490e4e (gbae-measure.log, EXIT=0): GBAE browsable roms [] from both / and /home/toy; load_rom gives NotFound from Downloads and Ok(3) from its own folder.
    • Recorded as issues/an-installed-gbae-browses-to-no-rom.md (kind: defect, status: open), with an exit a run can check.
    • The package track's running line now points at that issue. Per the orchestrator's ruling, the issue goes to the owner.
  • The T14 readings were not posted: CLOSED. The orchestrator's reading at 0ad87a593 covers both:
    • Every image's sha256 matches request.txt (proctreecase 68e6a454…5f450bb, testcases 3efa3bad…2629e4d4, testcases-watchdog 784e6e5c…274e44dd).
    • --fat32-check gives 0 on all three.
    • The judge gives EXIT=0, [metal] 255 passed, 0 failed, 3 boot(s).
    • process_tree, launch_toctou, launch_authority, port_badge, fs_share and app_view are all PASS.
    • its badge is no grant occurs 0 times.

The round-1 NOTEs are answered too:

  • An unknown request is now None and is dropped on every connection; mutation H5 shows it.
  • mint cannot fail, and a const assertion now states why.
  • The APP_FOLDERS self-comparison is deleted.
  • The /apps conflict is filed as issues/whether-pkg-alone-writes-apps.md (kind: question, status: owner).
  • The isolation track's "nothing else" now names /tmp and /system.
  • The stop-condition NOTE is moot by the orchestrator's ruling.

Measured at this head:

  • cargo run -- --ci host: EXIT=0, 78 step(s), all green (r2/logs/ci-host.log, which opens with 0ad87a593).
  • cargo test --test toyos-build: EXIT=0, 41/41 (r2/logs/suite.log, which opens with 0ad87a593).
  • The negative control, with the whole production change reverted onto d6298c83e: app_view red, EXIT=1, and each hole named.
  • No independent oracle exists for this boundary, and the body says so.

Read for this round, with no defect found:

  • A grant's root that has been replaced by a symlink cannot escape. DATA's namespace is flat and keyed by the whole path (fileserver/src/data.rs:4), and resolve never looks up the root itself, so a symlink there leaves only paths that do not exist.
  • volume.writable() is fixed for a volume's lifetime (data.rs:347, fat.rs:92/147), so folding it into Client::writes at accept loses no check.
  • STREAM only ever appends (toyos/src/fs.rs:459), so a read-only grant loses no read by refusing it.
  • Grant::decode checks the length before it subtracts.

BLOCKER

None open.

NOTE

  • PR body, Gates table: the "T14 staging" row says "neither reading exists yet", and the next paragraph points at the bb091478f reading. Both are false now: the orchestrator's reading at 0ad87a593 (255 passed) is the evidence.
  • issues/an-installed-gbae-browses-to-no-rom.md:11-12: "that menu reaches no ROM anywhere" is false of the tree. /tmp is kernel-served to every program, and the measured walk lists it (GBAE menu /tmp -> ["../"]), so a ROM placed there is browsable. The empty listing came from an empty /tmp, not from the view.

LAND

@Japabu
Japabu marked this pull request as ready for review October 9, 2026 16:23
@Japabu

Japabu commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

CI at 0ad87a593, read from each job's log: host 79 steps all green; toolchain (llvm a2cc063281d9f279, compiler 6c76c19e5ffcc869, freestanding 0f5a5faab7bbc78c restored; sysroot 35ede11740341aa6 built); guest suite 41/41 ok. Merges clean with main d6298c83e. Queued.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant