Add native FreeBSD runtime slice

This commit is contained in:
2026-04-03 06:33:01 +02:00
parent 04b6ade095
commit f163a63b1f
6 changed files with 606 additions and 4 deletions

View File

@@ -3224,3 +3224,107 @@ Next recommended step:
1. introduce a clearer native runtime slice so runtime is no longer modeled by reusing the broader native world output for both boot and runtime 1. introduce a clearer native runtime slice so runtime is no longer modeled by reusing the broader native world output for both boot and runtime
2. validate that explicit native runtime slice on QEMU and XCP-ng 2. validate that explicit native runtime slice on QEMU and XCP-ng
3. then revisit headers/toolchain/development package boundaries 3. then revisit headers/toolchain/development package boundaries
## 2026-04-03 — Phase 14.2: validated an explicit native FreeBSD runtime slice
Completed work:
- wrote the Phase 14.2 report:
- `docs/reports/phase14-native-runtime-freebsd.md`
- added a new native package in `modules/fruix/packages/freebsd.scm`:
- `freebsd-native-runtime`
- this runtime slice is built from `/usr/src` through the native world path and now prunes at least:
- `boot`
- `usr/include`
- `usr/share/doc`
- `usr/share/examples`
- `usr/share/info`
- `usr/share/man`
- `usr/share/mk`
- `usr/tests`
- added a dedicated Phase 14.2 operating-system template:
- `tests/system/phase14-native-runtime-pid1-operating-system.scm.in`
- added dedicated validation wrappers:
- `tests/system/run-phase14-native-runtime-qemu.sh`
- `tests/system/run-phase14-native-runtime-xcpng.sh`
- the validated Phase 14.2 model now uses:
- `#:kernel freebsd-native-kernel`
- `#:bootloader freebsd-native-world`
- `#:base-packages (list freebsd-native-runtime)`
- this makes the system composition more explicit:
- native world provides boot assets
- native runtime provides the guest runtime slice
- host base stores remain absent from the validated path
Important finding:
- this Phase 14.2 layout still duplicates some content because boot assets still come from the broader native world output while runtime comes from the separate native runtime slice
- that made the earlier Phase 13 image sizes too small
- the working values are now:
- local QEMU:
- `DISK_CAPACITY=12g`
- `ROOT_SIZE=10g`
- real XCP-ng:
- `ROOT_SIZE=10g`
- disk capacity still matched to the fixed 30 GiB VDI
- the new Phase 14.2 wrappers now use those larger defaults
Validation:
- local QEMU/UEFI/TCG runtime wrapper passes:
- `tests/system/run-phase14-native-runtime-qemu.sh`
- workdir: `/tmp/phase14-2-qemu2-1775189802`
- result: `PASS phase14-native-runtime-qemu`
- confirmed:
- `disk_capacity=12g`
- `root_size=10g`
- `runtime_store=/frx/store/684a82aeed2c9a353e3a09d2cbf5358274d758005e0bfa9b1025d101bc166f79-freebsd-native-runtime-15.0-STABLE`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `native_runtime_ready=ok`
- real XCP-ng runtime wrapper passes:
- `tests/system/run-phase14-native-runtime-xcpng.sh`
- workdir: `/tmp/phase14-2-xcpng-1775190184`
- result: `PASS phase14-native-runtime-xcpng`
- confirmed:
- `vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289`
- `vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `guest_ip=192.168.213.62`
- `root_size=10g`
- `runtime_store=/frx/store/684a82aeed2c9a353e3a09d2cbf5358274d758005e0bfa9b1025d101bc166f79-freebsd-native-runtime-15.0-STABLE`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `native_runtime_ready=ok`
- the wrappers also assert the runtime-split boundary directly:
- runtime store contains required boot-to-ready files such as:
- `/bin/sh`
- `/sbin/init`
- `/etc/rc`
- `/usr/sbin/sshd`
- `/sbin/dhclient`
- `/usr/bin/ssh-keygen`
- `/usr/share/locale/C.UTF-8/LC_CTYPE`
- runtime store no longer contains:
- `/boot`
- `/usr/include`
Current assessment:
- Phase 14.2 is complete
- the validated Fruix guest now reaches ready state using an explicit native runtime artifact rather than reusing the broad native world output for both boot and runtime roles
- the validated path remains host-base-free:
- native kernel
- native world as the temporary boot-source artifact
- native runtime as the guest runtime artifact
Next recommended step:
1. define cleaner runtime vs. development boundaries in code/package sets
2. introduce a narrower native boot asset package so the broader native world output is no longer needed as the temporary boot-source store
3. revalidate the full host-base-free path after that split

View File

@@ -0,0 +1,139 @@
# Phase 14.2: validated an explicit native FreeBSD runtime slice
Date: 2026-04-03
## Goal
Phase 14.1 proved that Fruix could boot without host-copied `/boot` material by sourcing boot assets from the existing native world output.
Phase 14.2 removed the remaining model ambiguity on the runtime side:
- the guest should now boot and reach ready state using an explicit native runtime output
- not by reusing the broader native world artifact for both boot and runtime roles
## Changes
Added a new native package:
- `freebsd-native-runtime`
This package is built from `/usr/src` via the existing native world build path and prunes at least:
- `boot`
- `usr/include`
- `usr/share/doc`
- `usr/share/examples`
- `usr/share/info`
- `usr/share/man`
- `usr/share/mk`
- `usr/tests`
The validated Phase 14.2 operating-system template now uses:
- `#:kernel freebsd-native-kernel`
- `#:bootloader freebsd-native-world`
- `#:base-packages (list freebsd-native-runtime)`
That means the model is now explicit:
- native world provides boot assets
- native runtime provides the guest runtime slice
- host base stores are no longer part of the validated path
## Practical sizing finding
This Phase 14.2 layout still duplicates some native world/runtime content in the closure because the boot assets still come from the broader native world output.
As a result, the Phase 13 image sizes were no longer large enough.
Working values were:
- local QEMU:
- `DISK_CAPACITY=12g`
- `ROOT_SIZE=10g`
- real XCP-ng:
- `ROOT_SIZE=10g`
- disk capacity still matched to the fixed 30 GiB VDI
The wrappers were updated to use those larger defaults.
This is acceptable for Phase 14.2 because the next subphase is specifically about cleaning up the runtime/development/boot boundary further.
## New files
Added:
- `tests/system/phase14-native-runtime-pid1-operating-system.scm.in`
- `tests/system/run-phase14-native-runtime-qemu.sh`
- `tests/system/run-phase14-native-runtime-xcpng.sh`
These wrappers assert:
- `host_base_store_count=0`
- native kernel present
- native world present as the current boot-source artifact
- native runtime present
- runtime store still contains the files needed for boot-to-ready
- runtime store no longer contains `/boot`
- runtime store no longer contains `/usr/include`
## Validation
### Local QEMU / UEFI / TCG
Passing run:
- `PASS phase14-native-runtime-qemu`
- workdir: `/tmp/phase14-2-qemu2-1775189802`
Confirmed:
```text
disk_capacity=12g
root_size=10g
runtime_store=/frx/store/684a82aeed2c9a353e3a09d2cbf5358274d758005e0bfa9b1025d101bc166f79-freebsd-native-runtime-15.0-STABLE
native_base_store_count=3
host_base_store_count=0
shepherd_pid=1
sshd_status=running
native_runtime_ready=ok
```
### Real XCP-ng VM
Passing run:
- `PASS phase14-native-runtime-xcpng`
- workdir: `/tmp/phase14-2-xcpng-1775190184`
Confirmed:
```text
vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289
vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743
guest_ip=192.168.213.62
root_size=10g
runtime_store=/frx/store/684a82aeed2c9a353e3a09d2cbf5358274d758005e0bfa9b1025d101bc166f79-freebsd-native-runtime-15.0-STABLE
native_base_store_count=3
host_base_store_count=0
shepherd_pid=1
sshd_status=running
compat_prefix_shims=absent
guile_module_smoke=ok
native_runtime_ready=ok
```
## Result
Phase 14.2 is complete.
The validated Fruix guest now reaches ready state using an explicit native runtime artifact:
- native kernel
- native world for boot assets
- native runtime for the guest runtime slice
- no host-staged FreeBSD base stores in the validated path
## Next step
Phase 14.3 should clean up the remaining redundancy by defining clearer runtime vs. development boundaries and, ideally, replacing the temporary use of the broad native world artifact as the boot-source package with a narrower native boot asset package.

View File

@@ -30,6 +30,7 @@
freebsd-bash freebsd-bash
freebsd-native-kernel freebsd-native-kernel
freebsd-native-world freebsd-native-world
freebsd-native-runtime
freebsd-native-build-package? freebsd-native-build-package?
freebsd-host-staged-package? freebsd-host-staged-package?
%freebsd-host-staged-all-packages %freebsd-host-staged-all-packages
@@ -495,10 +496,10 @@ the first native replacement for the earlier host-copy kernel package."
#:home-page "https://www.freebsd.org/" #:home-page "https://www.freebsd.org/"
#:synopsis "Native Fruix-managed FreeBSD world artifact" #:synopsis "Native Fruix-managed FreeBSD world artifact"
#:description #:description
"FreeBSD-specific package definition that builds and installs a minimal "FreeBSD-specific package definition that builds and installs a broad
runtime-oriented world from /usr/src into a real Fruix store artifact. The native world from /usr/src into a real Fruix store artifact. It still keeps a
first split intentionally focuses on the world needed for the Fruix guest to large cross-section of boot and runtime files together and now serves as the
boot, network, activate, and run Shepherd." broad source artifact that later native runtime/boot splits derive from."
#:license 'bsd-2 #:license 'bsd-2
#:install-plan #:install-plan
'((source-root . "/usr/src") '((source-root . "/usr/src")
@@ -516,6 +517,39 @@ boot, network, activate, and run Shepherd."
"usr/share/man" "usr/share/man"
"usr/tests"))))) "usr/tests")))))
(define freebsd-native-runtime
(freebsd-package
#:name "freebsd-native-runtime"
#:version freebsd-release
#:build-system 'freebsd-world-build-system
#:home-page "https://www.freebsd.org/"
#:synopsis "Native Fruix-managed FreeBSD runtime slice"
#:description
"FreeBSD-specific package definition that stages a runtime-focused slice of
installworld/distribution from /usr/src. It removes the boot tree and obvious
development-oriented paths so the validated Fruix guest can use an explicit
native runtime output rather than reusing the broader native world artifact for
both boot and runtime roles."
#:license 'bsd-2
#:install-plan
'((source-root . "/usr/src")
(target . "amd64")
(target-arch . "amd64")
(kernconf . "GENERIC")
(make-flags . ("__MAKE_CONF=/dev/null"
"SRCCONF=/dev/null"
"SRC_ENV_CONF=/dev/null"
"MK_DEBUG_FILES=no"
"MK_TESTS=no"))
(prune-paths . ("boot"
"usr/include"
"usr/share/doc"
"usr/share/examples"
"usr/share/info"
"usr/share/man"
"usr/share/mk"
"usr/tests")))))
(define (freebsd-native-build-package? package) (define (freebsd-native-build-package? package)
(not (not (memq (freebsd-package-build-system package) (not (not (memq (freebsd-package-build-system package)
'(freebsd-kernel-build-system freebsd-world-build-system))))) '(freebsd-kernel-build-system freebsd-world-build-system)))))

View File

@@ -0,0 +1,71 @@
(use-modules (fruix system freebsd)
(fruix packages freebsd))
(define phase14-operating-system
(operating-system
#:host-name "fruix-freebsd"
#:kernel freebsd-native-kernel
#:bootloader freebsd-native-world
#:base-packages (list freebsd-native-runtime)
#:groups (list (user-group #:name "wheel" #:gid 0 #:system? #t)
(user-group #:name "sshd" #:gid 22 #:system? #t)
(user-group #:name "_dhcp" #:gid 65 #:system? #t)
(user-group #:name "operator" #:gid 1000 #:system? #f))
#:users (list (user-account #:name "root"
#:uid 0
#:group "wheel"
#:comment "Charlie &"
#:home "/root"
#:shell "/bin/sh"
#:system? #t)
(user-account #:name "sshd"
#:uid 22
#:group "sshd"
#:comment "Secure Shell Daemon"
#:home "/var/empty"
#:shell "/usr/sbin/nologin"
#:system? #t)
(user-account #:name "_dhcp"
#:uid 65
#:group "_dhcp"
#:comment "dhcp programs"
#:home "/var/empty"
#:shell "/usr/sbin/nologin"
#:system? #t)
(user-account #:name "operator"
#:uid 1000
#:group "operator"
#:supplementary-groups '("wheel")
#:comment "Fruix Operator"
#:home "/home/operator"
#:shell "/bin/sh"
#:system? #f))
#:file-systems (list (file-system #:device "/dev/gpt/fruix-root"
#:mount-point "/"
#:type "ufs"
#:options "rw"
#:needed-for-boot? #t)
(file-system #:device "devfs"
#:mount-point "/dev"
#:type "devfs"
#:options "rw"
#:needed-for-boot? #t)
(file-system #:device "tmpfs"
#:mount-point "/tmp"
#:type "tmpfs"
#:options "rw,size=64m"))
#:services '(shepherd ready-marker sshd)
#:loader-entries '(("autoboot_delay" . "1")
("boot_multicons" . "YES")
("boot_serial" . "YES")
("console" . "comconsole,vidconsole"))
#:rc-conf-entries '(("clear_tmp_enable" . "NO")
("hostid_enable" . "NO")
("sendmail_enable" . "NONE")
("sshd_enable" . "YES")
("ifconfig_xn0" . "SYNCDHCP")
("ifconfig_em0" . "SYNCDHCP")
("ifconfig_vtnet0" . "SYNCDHCP"))
#:init-mode 'shepherd-pid1
#:ready-marker "/var/lib/fruix/ready"
#:root-authorized-keys '("__ROOT_AUTHORIZED_KEY__")))

View File

@@ -0,0 +1,124 @@
#!/bin/sh
set -eu
repo_root=${PROJECT_ROOT:-$(pwd)}
os_template=${OS_TEMPLATE:-$repo_root/tests/system/phase14-native-runtime-pid1-operating-system.scm.in}
system_name=${SYSTEM_NAME:-phase14-operating-system}
disk_capacity=${DISK_CAPACITY:-12g}
root_size=${ROOT_SIZE:-10g}
metadata_target=${METADATA_OUT:-}
cleanup=0
if [ -n "${WORKDIR:-}" ]; then
workdir=$WORKDIR
mkdir -p "$workdir"
else
workdir=$(mktemp -d /tmp/fruix-phase14-native-runtime-qemu.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
inner_metadata=$workdir/phase14-native-runtime-qemu-inner-metadata.txt
metadata_file=$workdir/phase14-native-runtime-qemu-metadata.txt
cleanup_workdir() {
if [ "$cleanup" -eq 1 ]; then
rm -rf "$workdir" 2>/dev/null || sudo rm -rf "$workdir"
fi
}
trap cleanup_workdir EXIT INT TERM
KEEP_WORKDIR=1 WORKDIR="$workdir/inner" METADATA_OUT="$inner_metadata" \
OS_TEMPLATE="$os_template" SYSTEM_NAME="$system_name" DISK_CAPACITY="$disk_capacity" ROOT_SIZE="$root_size" \
"$repo_root/tests/system/run-phase11-shepherd-pid1-qemu.sh"
phase8_metadata=$(sed -n 's/^phase8_metadata=//p' "$inner_metadata")
closure_path=$(sed -n 's/^closure_path=//p' "$inner_metadata")
closure_base=$(sed -n 's/^closure_base=//p' "$inner_metadata")
serial_log=$(sed -n 's/^serial_log=//p' "$inner_metadata")
ssh_port=$(sed -n 's/^ssh_port=//p' "$inner_metadata")
shepherd_pid=$(sed -n 's/^shepherd_pid=//p' "$inner_metadata")
sshd_status=$(sed -n 's/^sshd_status=//p' "$inner_metadata")
activate_log=$(sed -n 's/^activate_log=//p' "$inner_metadata")
native_base_store_count=$(sed -n 's/^native_base_store_count=//p' "$phase8_metadata")
native_base_stores=$(sed -n 's/^native_base_stores=//p' "$phase8_metadata")
host_base_store_count=$(sed -n 's/^host_base_store_count=//p' "$phase8_metadata")
host_base_stores=$(sed -n 's/^host_base_stores=//p' "$phase8_metadata")
[ "$native_base_store_count" = 3 ] || { echo "expected 3 native base stores, got: $native_base_store_count" >&2; exit 1; }
[ "$host_base_store_count" = 0 ] || { echo "expected 0 host base stores, got: $host_base_store_count" >&2; exit 1; }
[ -z "$host_base_stores" ] || { echo "host base stores are not empty: $host_base_stores" >&2; exit 1; }
printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-kernel-15.0-STABLE$' >/dev/null || {
echo "native base stores do not include the native kernel" >&2
exit 1
}
printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-world-15.0-STABLE$' >/dev/null || {
echo "native base stores do not include the native boot/world source artifact" >&2
exit 1
}
runtime_store=$(printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-runtime-15.0-STABLE$' | head -n 1)
[ -n "$runtime_store" ] || {
echo "native base stores do not include the explicit native runtime slice" >&2
exit 1
}
for path in \
"$runtime_store/bin/sh" \
"$runtime_store/sbin/init" \
"$runtime_store/etc/rc" \
"$runtime_store/usr/sbin/sshd" \
"$runtime_store/sbin/dhclient" \
"$runtime_store/usr/bin/ssh-keygen" \
"$runtime_store/usr/share/locale/C.UTF-8/LC_CTYPE"
do
[ -e "$path" ] || {
echo "required native runtime path missing: $path" >&2
exit 1
}
done
[ ! -e "$runtime_store/boot" ] || { echo "native runtime still contains /boot" >&2; exit 1; }
[ ! -e "$runtime_store/usr/include" ] || { echo "native runtime still contains /usr/include" >&2; exit 1; }
[ "$shepherd_pid" = 1 ] || { echo "shepherd was not PID 1" >&2; exit 1; }
[ "$sshd_status" = running ] || { echo "sshd is not running" >&2; exit 1; }
case "$activate_log" in
*fruix-activate:done*) : ;;
*) echo "activation log does not show success" >&2; exit 1 ;;
esac
cat >"$metadata_file" <<EOF
workdir=$workdir
inner_metadata=$inner_metadata
phase8_metadata=$phase8_metadata
closure_path=$closure_path
closure_base=$closure_base
serial_log=$serial_log
ssh_port=$ssh_port
disk_capacity=$disk_capacity
root_size=$root_size
runtime_store=$runtime_store
native_base_store_count=$native_base_store_count
native_base_stores=$native_base_stores
host_base_store_count=$host_base_store_count
host_base_stores=$host_base_stores
shepherd_pid=$shepherd_pid
sshd_status=$sshd_status
boot_backend=qemu-uefi-tcg
init_mode=shepherd-pid1
native_runtime_ready=ok
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS phase14-native-runtime-qemu\n'
printf 'Work directory: %s\n' "$workdir"
printf 'Metadata file: %s\n' "$metadata_file"
if [ -n "$metadata_target" ]; then
printf 'Copied metadata to: %s\n' "$metadata_target"
fi
printf '%s\n' '--- metadata ---'
cat "$metadata_file"

View File

@@ -0,0 +1,130 @@
#!/bin/sh
set -eu
repo_root=${PROJECT_ROOT:-$(pwd)}
os_template=${OS_TEMPLATE:-$repo_root/tests/system/phase14-native-runtime-pid1-operating-system.scm.in}
system_name=${SYSTEM_NAME:-phase14-operating-system}
root_size=${ROOT_SIZE:-10g}
metadata_target=${METADATA_OUT:-}
cleanup=0
if [ -n "${WORKDIR:-}" ]; then
workdir=$WORKDIR
mkdir -p "$workdir"
else
workdir=$(mktemp -d /tmp/fruix-phase14-native-runtime-xcpng.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
inner_metadata=$workdir/phase14-native-runtime-xcpng-inner-metadata.txt
metadata_file=$workdir/phase14-native-runtime-xcpng-metadata.txt
cleanup_workdir() {
if [ "$cleanup" -eq 1 ]; then
rm -rf "$workdir"
fi
}
trap cleanup_workdir EXIT INT TERM
KEEP_WORKDIR=1 WORKDIR="$workdir/inner" METADATA_OUT="$inner_metadata" \
OS_TEMPLATE="$os_template" SYSTEM_NAME="$system_name" ROOT_SIZE="$root_size" \
"$repo_root/tests/system/run-phase11-shepherd-pid1-xcpng.sh"
phase8_metadata=$(sed -n 's/^phase8_metadata=//p' "$inner_metadata")
closure_path=$(sed -n 's/^closure_path=//p' "$inner_metadata")
closure_base=$(sed -n 's/^closure_base=//p' "$inner_metadata")
guest_ip=$(sed -n 's/^guest_ip=//p' "$inner_metadata")
vm_id=$(sed -n 's/^vm_id=//p' "$inner_metadata")
vdi_id=$(sed -n 's/^vdi_id=//p' "$inner_metadata")
shepherd_pid=$(sed -n 's/^shepherd_pid=//p' "$inner_metadata")
sshd_status=$(sed -n 's/^sshd_status=//p' "$inner_metadata")
compat_prefix_shims=$(sed -n 's/^compat_prefix_shims=//p' "$inner_metadata")
guile_module_smoke=$(sed -n 's/^guile_module_smoke=//p' "$inner_metadata")
activate_log=$(sed -n 's/^activate_log=//p' "$inner_metadata")
native_base_store_count=$(sed -n 's/^native_base_store_count=//p' "$phase8_metadata")
native_base_stores=$(sed -n 's/^native_base_stores=//p' "$phase8_metadata")
host_base_store_count=$(sed -n 's/^host_base_store_count=//p' "$phase8_metadata")
host_base_stores=$(sed -n 's/^host_base_stores=//p' "$phase8_metadata")
[ "$native_base_store_count" = 3 ] || { echo "expected 3 native base stores, got: $native_base_store_count" >&2; exit 1; }
[ "$host_base_store_count" = 0 ] || { echo "expected 0 host base stores, got: $host_base_store_count" >&2; exit 1; }
[ -z "$host_base_stores" ] || { echo "host base stores are not empty: $host_base_stores" >&2; exit 1; }
printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-kernel-15.0-STABLE$' >/dev/null || {
echo "native base stores do not include the native kernel" >&2
exit 1
}
printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-world-15.0-STABLE$' >/dev/null || {
echo "native base stores do not include the native boot/world source artifact" >&2
exit 1
}
runtime_store=$(printf '%s\n' "$native_base_stores" | tr ',' '\n' | grep 'freebsd-native-runtime-15.0-STABLE$' | head -n 1)
[ -n "$runtime_store" ] || {
echo "native base stores do not include the explicit native runtime slice" >&2
exit 1
}
for path in \
"$runtime_store/bin/sh" \
"$runtime_store/sbin/init" \
"$runtime_store/etc/rc" \
"$runtime_store/usr/sbin/sshd" \
"$runtime_store/sbin/dhclient" \
"$runtime_store/usr/bin/ssh-keygen" \
"$runtime_store/usr/share/locale/C.UTF-8/LC_CTYPE"
do
[ -e "$path" ] || {
echo "required native runtime path missing: $path" >&2
exit 1
}
done
[ ! -e "$runtime_store/boot" ] || { echo "native runtime still contains /boot" >&2; exit 1; }
[ ! -e "$runtime_store/usr/include" ] || { echo "native runtime still contains /usr/include" >&2; exit 1; }
[ "$shepherd_pid" = 1 ] || { echo "shepherd was not PID 1" >&2; exit 1; }
[ "$sshd_status" = running ] || { echo "sshd is not running" >&2; exit 1; }
[ "$compat_prefix_shims" = absent ] || { echo "compatibility prefix shims reappeared" >&2; exit 1; }
[ "$guile_module_smoke" = ok ] || { echo "guest Guile module smoke failed" >&2; exit 1; }
case "$activate_log" in
*fruix-activate:done*) : ;;
*) echo "activation log does not show success" >&2; exit 1 ;;
esac
cat >"$metadata_file" <<EOF
workdir=$workdir
inner_metadata=$inner_metadata
phase8_metadata=$phase8_metadata
closure_path=$closure_path
closure_base=$closure_base
vm_id=$vm_id
vdi_id=$vdi_id
guest_ip=$guest_ip
root_size=$root_size
runtime_store=$runtime_store
native_base_store_count=$native_base_store_count
native_base_stores=$native_base_stores
host_base_store_count=$host_base_store_count
host_base_stores=$host_base_stores
shepherd_pid=$shepherd_pid
sshd_status=$sshd_status
compat_prefix_shims=$compat_prefix_shims
guile_module_smoke=$guile_module_smoke
boot_backend=xcp-ng-xo-cli
init_mode=shepherd-pid1
native_runtime_ready=ok
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS phase14-native-runtime-xcpng\n'
printf 'Work directory: %s\n' "$workdir"
printf 'Metadata file: %s\n' "$metadata_file"
if [ -n "$metadata_target" ]; then
printf 'Copied metadata to: %s\n' "$metadata_target"
fi
printf '%s\n' '--- metadata ---'
cat "$metadata_file"