Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,14 @@ jobs:
- name: Install selected Rust
run: rustup toolchain install "${{ matrix.rust }}" --profile minimal --component clippy --component rustfmt
- name: Validate owned code
run: sh scripts/validate.sh
run: |
# App Center fails closed when a same-user process cannot be inspected.
# Exclude unrelated runner processes while keeping all fixture processes
# visible. Use the unprivileged runner user and private fixture modes.
sudo unshare --pid --fork --mount-proc --kill-child \
sudo --user="$(id -un)" -- \
env HOME="$HOME" PATH="$PATH" \
RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" \
CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" \
RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN" \
sh -c 'umask 077; exec sh scripts/validate.sh'
176 changes: 119 additions & 57 deletions README.md

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions docs/images/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# README screenshot provenance

The six images linked from the [project README](../../README.md) were captured on
2026-10-06 from **Vitrallis 1.0.4 running on a physical PocketCHIP** over USB SSH.
They are real 480×272 display captures, not simulator images or mockups.

## Build and environment

- PocketCHIP, Debian 13 ARMv7 hard-float; X11/Awesome, native 480×272 display.
- Published `v1.0.4` ARMv7 complete bundle, downloaded from the
[release](https://github.com/csd113/Vitrallis-Shell/releases/tag/v1.0.4).
Bundle SHA-256: `eb2b9be59066112668f2802a91bfb5205ba3c7ded6f181dbda2b5d378b62212c`.
The sidecar checksum and all five embedded executable hashes were verified before
extraction and execution.
- Release source: `874526879bf0defb25d68fd493d066623c0f3fd8`. Current `main` at
capture time was `1fd29aecd26546866f002a176207d4b0ed7fca9e`; only README/release
documentation differs between those commits. Application code is identical.
- The release binaries ran from a private temporary directory. The existing
installed 1.0.3 generation, session, preferences and apps were preserved.
- Auto selected SDL `opengles2`; startup reported Mesa `Mali400`, OpenGL ES 2.0
Mesa 25.0.7, 480×272 window/output/display and requested SDL VSync.
These diagnostics and still images do not measure frame pacing or endurance.

## Actual states

| Asset | State shown |
| --- | --- |
| [Launcher](launcher-480x272.png) | Terminal, Notepad, Files, App Center and System Settings. Battery/charging, Wi-Fi and clock readings come from the live device. |
| [Terminal](terminal-480x272.png) | Real Bash PTY: a colored greeting, `ls` and `cat todo.txt` against sample files. The command waits for input after displaying its output. Terminal's static `--screenshot` preview fixture was not used. |
| [Notepad](notepad-480x272.png) | Sample `notes.txt` containing a weekend checklist, opened without edits. |
| [Files](files-480x272.png) | Sample Documents directory with empty Notes/Projects folders, `notes.txt` and `todo.txt`. |
| [Settings](settings-480x272.png) | Category overview with live device status; no setting or power action was activated. |
| [App Center](app-center-480x272.png) | Before first refresh in an isolated profile. No catalog entries or third-party apps are shown; no package operation was performed. |

A private temporary HOME and XDG configuration/data directories kept existing app
installs, catalogs, folders and personal data out of the captures. A temporary
PocketHome-format menu contained no third-party entries and retained the device's
existing Wi-Fi utility command for the System Settings tile. Native apps and App
Center were discovered normally, without `--demo`. Sample documents lived in the
same temporary workspace; their displayed paths contain no personal files.

The running windows used `--size 480x272` and fullscreen (the shell selected
`--linux-handheld`). Each capture used Pillow's X11 `ImageGrab.grab()` against the
physical device's entire 480×272 display. The frames were saved as lossless PNG
with compression optimization and transferred unchanged. No image was cropped,
resized, composited, annotated, retouched or AI-generated. Temporary capture
processes were closed and the original session regained focus.

## Repeating captures and evidence limits

Use an isolated profile and reviewed matching binaries; preserve the installed
session and user data. Wait for the shell's `event=ready` and live status refresh
before capturing the launcher. Open Settings from its tile, then close it and open
App Center without refreshing its empty profile. For native utilities, open only
sample documents/directories and run harmless commands through Terminal's
`--command` option. Capture each active fullscreen window on the device, verifying
that the resulting image is 480×272.

For desktop-only documentation work, the existing SDL `--screenshot NEW.bmp`
exports and [Linux simulator](../../tests/simulator/README.md) remain useful.
Label those environments explicitly; Terminal's static export is not evidence
of a live PTY. Lossless BMP-to-PNG conversion should preserve identical RGB pixels.

This bounded hardware session establishes the displayed states on this device.
It is **not full 1.0.4 hardware certification** and does not retest public
installation, cold boot, physical keyboard/touch, audio, power-loss durability or
long-running behavior. Earlier release-qualified results remain in the
[device guide](../devices/pocketchip.md) and [validation records](../validation.md).

The earlier [desktop launcher image](shell-480x272.png) is retained at its original
path for existing references. Historical physical-device screenshots under
`docs/devices/pocketchip/evidence/` are unchanged.
Binary file added docs/images/app-center-480x272.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/files-480x272.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/launcher-480x272.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/notepad-480x272.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/settings-480x272.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/terminal-480x272.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions docs/shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ SDL_VIDEODRIVER=dummy cargo run --locked -- --smoke-test
`--app-config` accepts an explicit PocketHome JSON path, and `--assets` an explicit
asset root. `--list-apps` prints normalized discovery diagnostics without a window.
`--screenshot NEW.bmp` captures the initial frame and exits, refusing to overwrite
an existing file. The README screenshot was captured from the current desktop
release build at 480×272 through this real renderer, with no device connection.
an existing file. The README screenshot tour instead uses live 480×272 display
captures on a physical PocketCHIP running 1.0.4.
[Capture provenance](images/README.md) describes the build, method and limits.

The handheld backend reads stock PocketHome's `/usr/share/pocket-home/config.json`.
`--app-config` overrides that path explicitly; no per-user launcher config is
Expand Down
9 changes: 6 additions & 3 deletions docs/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,12 @@ is recorded.

## Visual and device scope

The README image is a genuine current desktop-build SDL render at 480×272,
exported through `--screenshot`. Preview its Markdown at desktop and narrow/mobile
widths; the image must scale and command blocks must remain copyable.
The README screenshot tour contains genuine 480×272 display captures from a
physical PocketCHIP running 1.0.4. [Capture provenance](images/README.md) records
the environment and method for each image. The bounded screenshot session does
not extend earlier release certifications to 1.0.4. Preview the Markdown at
desktop and narrow/mobile widths; images must scale and command blocks must
remain copyable.

The [USB hardware follow-up](devices/pocketchip/validation-usb-session.md) records
fresh ARM installation, native app, session, control and removal tests on the device.
Expand Down
21 changes: 17 additions & 4 deletions src/app_center/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2074,7 +2074,9 @@ impl GateFetch {
};
drop(first_request);
self.entered.send(()).map_err(|error| error.to_string())?;
receiver.recv().map_err(|error| error.to_string())
receiver
.recv_timeout(std::time::Duration::from_secs(30))
.map_err(|error| format!("download gate was not released: {error}"))
}
}
impl network::Fetch for GateFetch {
Expand Down Expand Up @@ -2221,7 +2223,9 @@ fn check_commit_fails_cleanly_if_lock_taken_mid_fetch() -> Result<(), String> {
let (release, locked) = mpsc::channel();
let state = loc.state.clone();
let holder = std::thread::spawn(move || -> Result<storage::Lock, String> {
started.recv().map_err(|e| e.to_string())?;
started
.recv_timeout(std::time::Duration::from_secs(30))
.map_err(|error| format!("catalog fetch did not start: {error}"))?;
let lock = storage::Lock::take(&state)?;
release.send(()).map_err(|e| e.to_string())?;
Ok(lock)
Expand All @@ -2232,6 +2236,7 @@ fn check_commit_fails_cleanly_if_lock_taken_mid_fetch() -> Result<(), String> {
release: std::sync::Mutex::new(Some(locked)),
};
let commit_messages = run_service(&loc, &next_fetch, vec![Command::Check]);
drop(next_fetch);
match commit_messages.as_slice() {
[Ok(_), Err(error)] => assert!(
error.starts_with("Another Vitrallis storage operation is active"),
Expand Down Expand Up @@ -2292,7 +2297,9 @@ fn concurrent_install_between_download_and_commit_is_detected() -> Result<(), St
let install_package = p.clone();
let install_files = files.clone();
std::thread::spawn(move || -> Result<(), String> {
started.recv().map_err(|e| e.to_string())?;
started
.recv_timeout(std::time::Duration::from_secs(30))
.map_err(|error| format!("download did not start: {error}"))?;
let prepared =
install::prepare(&install_locations, install_package.clone(), install_files)?;
install::install(&install_locations, &prepared)?;
Expand All @@ -2305,9 +2312,15 @@ fn concurrent_install_between_download_and_commit_is_detected() -> Result<(), St
release: std::sync::Mutex::new(Some(unblock)),
};
let next_messages = run_service(&loc, &fetch, vec![Command::Install(vec![p.key()])]);
// An early service error can skip fetching. Disconnect the start signal
// before joining so that error cannot leave the installer waiting forever.
drop(fetch);
installer
.join()
.map_err(|payload| format!("installer panicked: {payload:?}"))??;
.map_err(|payload| format!("installer panicked: {payload:?}"))?
.map_err(|error| {
format!("concurrent installer: {error}; service updates: {next_messages:?}")
})?;
match next_messages.as_slice() {
[Ok(_), Err(error)] => assert!(error.contains("No available update"), "{error}"),
other => return Err(format!("unexpected updates: {other:?}")),
Expand Down