# Pi standalone foundation Stage 2 checkpoint

Date: 2026-08-02
Result: PASS; integrated and deployed fixture-only, with live admission false
Repository: `jong@srv-rpi00.local:~/dev/sd_qualification`
Mainline: clean `main` at `9327e283c90e92b3cd11775573f7d837867c418d`
Reviews: PR #5 foundation and PR #6 empty-store hotfix merged
Release identity: `standalone-foundation-stage2-9327e28`

## Scope and boundaries

This checkpoint completes the independently runnable Stage 2 foundation behind
the existing authenticated Django UI. It did not select or access a controller,
enable J-Link, use a block device, operate power, move media, import an admitted
firmware/validator bundle, or authorize destructive work. The deployed service
remains fixture-only and reports `live_admitted=false`.

## Implemented foundation

- A server-side offline importer validates a versioned manifest, bounded file
  count/total bytes, exact member set, paths, types, sizes, and SHA-256; rejects
  traversal, symbolic links, duplicates, unlisted files, and existing-store
  corruption; verifies copied bytes; and publishes by same-filesystem atomic
  rename into a canonical-manifest-SHA-256 directory.
- Web and command-worker services mount the bundle store read-only. Only a
  one-shot administrative importer receives write access, while its reviewed
  inbox is read-only.
- Imported bundle records capture importer and qualifier release identities and
  always begin `admitted=false` with explicit blockers. Bundles cannot carry
  their own approval authority.
- A protected, browser-inaccessible Controller A/B registry requires exactly
  two unique assembly/probe identities, reviewed variants, boolean enablement,
  and exact imported-bundle hashes.
- Fail-closed admission distinguishes registry validity, bundle presence,
  controller enablement, external approval, adapter state, and power/media
  state with stable blocker codes.
- The dashboard and health endpoints expose bundle, registry, storage, clock,
  worker, lease, compatibility, and admission facts without a browser upload,
  shell, target endpoint, or live adapter.
- The application base, complete Python runtime dependency closure, and
  PostgreSQL runtime are pinned. Deterministic CycloneDX inventory and atomic
  offline release packaging bind source, application image, runtime image,
  Compose/configuration/schema/operations files, manifest, and checksums.

## Verification

- Django system/migration checks and Compose configuration validation passed.
- Exact integrated image: 49/49 Django tests passed; 3/3 release-packager tests
  passed.
- Two raw installed-package inventories were byte-identical at SHA-256
  `d00a7a3c33026ce6c0c711f9915a9b23a242fbe2ec824d505f7125ad9fce7736`.
- Final 650 MiB release directory:
  `/var/tmp/sdq-release-standalone-foundation-stage2-9327e28`.
- Release-member verification passed. Key hashes are:
  - `image.tar`: `a661ce852f325b843a3b08478668a0671bf59264dae69ac01c97c984746c938b`;
  - `manifest.json`: `96e001a5a6cae06a9d7e94444044be7ad68344cd6cd63c27fbc88c0062681190`;
  - `SHA256SUMS`: `5d73f1489caebce5a18a5f6ed064ea82dabec68901da21ea3c3cd80d597dcc45`;
  - packaged `sbom.cdx.json`:
    `a9526aea9d990c1044d379a412001b1f7a04c0d3b3f04440fccf97c2b605af33`.
- The archive reloaded both the exact application image and digest-pinned
  PostgreSQL image. With `--network none`, the reloaded application inventory
  was semantically identical to the packaged inventory.
- On an isolated Docker-internal network, the clean deployed baseline advanced
  from five to seven qualification migrations. A pre-upgrade dump with SHA-256
  `2bb200fa6a8a1887128c195ea969d8aeafd225771df439c372efdc9c03954c25`
  restored to five migrations and passed the baseline application check.
- Isolated web, command-worker, and notification-worker containers reported
  database, storage, clock, and worker health while live admission remained
  false. Their disposable containers and network were removed.

## Deployment and recovery evidence

- Before deployment, the running image was retained as
  `srv-rpi00-sd-qualifier:rollback-57d9f73` with image ID
  `sha256:d746c28abaae273f2c1c7c0e96ab5b838eb207b1b664e25b4d19c9bc60b3420d`.
- Live backup `/backups/sd-qualifier-20260802T174855Z.dump` passed the isolated
  restore check before application migration.
- The first integrated deployment exposed an empty mounted-bundle-store 500.
  The three application services were immediately restored to the retained
  baseline image and returned healthy. PR #6 added the missing gate scope plus
  an exact empty-store regression; the full suite increased to 49 passing tests.
- The exact hotfixed merge was redeployed. Web and both durable workers run
  image ID
  `sha256:8a6f8424481ebf814b0cf9bfb55c262b02b130fbf9c384372bb89402cb7189d4`;
  all seven qualification migrations are applied.
- The Front Door health response is `status=ok`, database/storage/clock/workers
  true, `fixture_only=true`, and `live_admitted=false`. The empty bundle store,
  absent production registry/approval anchor, live adapter, and power/media
  operations remain explicit non-fixture blockers.

## Remaining gates

Stage 2 is complete. Stage 3 now owns the fixture-only operator frontend for
bundle selection, complete card inventory/certification, resumable progress,
results/evidence/review, and intervention flows. The HPT validator remains
unreleased. SMTP, live Controller A/B identity, read-only target access, power,
raw media, and destructive/endurance execution retain their separate gates and
authorizations.
