Files
fruix/docs/reports/phase20-native-build-store-promotion-freebsd.md

190 lines
6.1 KiB
Markdown

# Post-Phase 20: native build result promotion into first-class Fruix store objects
Date: 2026-04-05
## Goal
Make native FreeBSD base-build results feel like real Fruix objects instead of stopping at mutable staged files under:
- `/var/lib/fruix/native-builds/...`
The desired model is:
- `/var/lib/fruix/native-builds/...` remains a staging/result area
- `/frx/store/...` remains the real immutable identity
Validated artifact identities:
- `world`
- `kernel`
- `headers`
- `bootloader`
## What changed
### Promotion metadata in guest result roots
The guest self-hosted helper now emits a promotion description file at:
- `/var/lib/fruix/native-builds/<run-id>/promotion.scm`
That metadata records at least:
- executor / executor-version
- run-id / guest-host-name
- closure path
- development profile path
- declared FreeBSD base metadata
- source store provenance
- build policy
- artifact entries for:
- `world`
- `kernel`
- `headers`
- `bootloader`
The helper also stages a promotable `world` artifact tree in addition to the already validated narrower artifacts.
### Host-side promotion API
Fruix now exports:
- `promote-native-build-result`
and the CLI now exposes:
- `fruix native-build promote RESULT_ROOT [--store DIR]`
### Promoted store object layout
Promotion now creates immutable store objects for:
- `/frx/store/...-fruix-native-world-...`
- `/frx/store/...-fruix-native-kernel-...`
- `/frx/store/...-fruix-native-headers-...`
- `/frx/store/...-fruix-native-bootloader-...`
Each promoted artifact store records:
- `.fruix-native-build-object.scm`
- `.references`
Promotion also creates a result-bundle store object:
- `/frx/store/...-fruix-native-build-result-...`
That bundle records:
- `.fruix-native-build-result.scm`
- `artifacts/world`
- `artifacts/kernel`
- `artifacts/headers`
- `artifacts/bootloader`
where the `artifacts/*` entries are symlinks to the promoted artifact stores.
### Identity policy
Artifact identity is now based on Fruix metadata plus a tree-content signature of the staged artifact tree.
That means promotion identity depends on both:
- the explicit Fruix-native build metadata
- the actual content of the promoted artifact tree
## Validation harness
Added:
- `tests/system/run-phase20-native-build-store-promotion-xcpng.sh`
This harness:
1. boots the approved real XCP-ng guest path
2. runs the validated in-guest self-hosted native build helper
3. imports the guest result root back to the host
4. runs `fruix native-build promote`
5. verifies promoted store paths, metadata, symlink structure, and representative hashes
## Validation
Passing run:
- `PASS phase20-native-build-store-promotion-xcpng`
- workdir: `/tmp/current-phase20-native-build-store-promotion-xcpng`
Approved real XCP-ng path:
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
Representative metadata:
```text
run_id=20260405T213444Z
source_store=/frx/store/12d7704362e95afc2697db63f168b878e082b372-freebsd-source-default
guest_result_root=/var/lib/fruix/native-builds/20260405T213444Z
result_store=/frx/store/c6329a0053720b05aff3274b8b1d522c909f475d-fruix-native-build-result-15.0-STABLE-guest-self-hosted
world_store=/frx/store/dfe37b36f6537a95ceea16ea62001b2ca5617eb7-fruix-native-world-15.0-STABLE-guest-self-hosted
kernel_store=/frx/store/0ab7cbceca240ab2c3b91e83e059844ea792e49e-fruix-native-kernel-15.0-STABLE-guest-self-hosted
headers_store=/frx/store/5bbeae9266687a229f1c6d176a08886c35243ff0-fruix-native-headers-15.0-STABLE-guest-self-hosted
bootloader_store=/frx/store/bd49a508bd7a3b94a2535d6774f31c993c406552-fruix-native-bootloader-15.0-STABLE-guest-self-hosted
artifact_store_count=4
sha_kernel=16950f116a52134b98e2f8e0dacc556e18fe254e4a0ac2c1741422dde281a341
sha_loader=ea417846167ece270ada611624dca622ca38bd30125b9a125cd8ebb8b3600313
sha_param=9eb140ca7d9666f3d484a4174c9acd94b45427db6292b4e17de19af2c6aa5219
promoted_kernel_sha=16950f116a52134b98e2f8e0dacc556e18fe254e4a0ac2c1741422dde281a341
promoted_loader_sha=ea417846167ece270ada611624dca622ca38bd30125b9a125cd8ebb8b3600313
promoted_param_sha=9eb140ca7d9666f3d484a4174c9acd94b45427db6292b4e17de19af2c6aa5219
native_build_store_promotion=ok
```
Validated facts:
- guest self-hosted native-build results remain available under `/var/lib/fruix/native-builds/...` as mutable staging/results
- those staged results are now sufficient to promote first-class Fruix identities on the host
- promotion creates four immutable artifact store objects plus one immutable result-bundle store object
- promoted metadata retains executor/source/build-policy/provenance information explicitly
- promoted kernel, bootloader, and headers hashes match the already validated staged artifacts
- the promoted `world` artifact is now also preserved as a first-class Fruix store object instead of remaining only as an unpromoted staged tree
## Important implementation note
The first full promotion attempt exposed an incorrect assumption in the validation surface:
- the promoted/staged `world` artifact should be checked at:
- `bin/sh`
- not at:
- `usr/bin/sh`
That path expectation was corrected in:
- helper validation
- emitted promotion metadata
- the end-to-end promotion harness
## Result
Fruix now has a validated native-build object model with a clear split:
- mutable native-build result roots under `/var/lib/fruix/native-builds/...`
- immutable promoted identities under `/frx/store/...`
That makes native FreeBSD base builds feel substantially more Fruix-native:
- build outputs have explicit immutable identities
- metadata is Fruix-native rather than implied only by ad hoc directory layout
- executor/source/provenance/build-policy remain attached to the promoted result
- the staged result area and the real store identity are now intentionally distinct
## Next direction
This suggests the next product step is not merely “more self-hosting”.
It is to generalize this result model so different execution modes can converge on the same promoted object story, for example:
- host-initiated in-guest builds
- guest self-hosted builds
- future executor variants
That would move Fruix closer to a shared executor model rather than treating each validation path as a one-off harness.