le-app/docs/le-app-database-migration/phase2b-stage2-verification
2026-07-12 23:54:05 -04:00
..
tests add Phase 2B Stage 2 verification 2026-07-12 23:54:05 -04:00
MANIFEST.sha256 add Phase 2B Stage 2 verification 2026-07-12 23:54:05 -04:00
README.md add Phase 2B Stage 2 verification 2026-07-12 23:54:05 -04:00
ROLLBACK.md add Phase 2B Stage 2 verification 2026-07-12 23:54:05 -04:00

Phase 2B Stage 2 — installed-state verification and daemon reload

Status: prepared for review only, 2026-07-12. Do not run until these uncommitted files have been reviewed and committed. Stage 2 does not authorize runtime activation.

Scope

tests/40-verify-stage2.sh is the only Stage 2 operator entry point. It runs as root and:

  1. requires a clean committed worktree and verifies this Stage 2 manifest, the unchanged Stage 1 artifact manifest, and the Stage 1 inventory checksums;
  2. proves that the Stage 1 artifact directory is unchanged since the commit recorded in the installed checksummed state;
  3. strictly resolves /var/lib/le-app-codex-phase2b/stage1/current, requires a COMPLETE state with exactly 16 files and no pending records, and binds its manifest digest to the recorded source commit;
  4. verifies all 16 destinations against the committed sources and state records by SHA-256, byte size, UID, GID, mode, regular-file type, and single-link count;
  5. confirms the runtime is inert, then runs the sole Stage 2 mutation: systemctl daemon-reload;
  6. validates the installed system units and drop-ins with systemd-analyze verify, systemctl cat, and exact or membership-checked systemctl show properties; parses the installed Docker unit with an explicit user-unit search path without starting a user manager;
  7. runs root-capable nft --check -f /etc/nftables.d/50-le-app-codex.nft and never loads the policy;
  8. explicitly rechecks the namespace helper and rootless launcher hashes; and
  9. repeats every inert-state assertion after the reload and static validation.

The inert-state gates require the approval marker, namespace, namespace path, project veth, rootless Docker socket, lingering, UID-1200 processes, and both Phase 2B nftables tables to be absent. They require le-app-codex-netns.service and user@1200.service to be inactive and the namespace service to remain static rather than enabled.

Explicit exclusions

Stage 2 does not create the approval marker; start, restart, or enable any unit; use systemctl --user; enable lingering; invoke the namespace helper; create a namespace or veth; load nftables; start Docker or Codex; authenticate Codex; copy a repository, migration source, data, or secrets; contact production services; or modify production.

The script makes no cleanup attempt after a failure. It preserves evidence and exits nonzero. See ROLLBACK.md for the Stage 2 no-op rollback and the separately approval-gated Stage 1 removal path.

Review and future execution

Before committing, run only the non-operational tests/41-stage2-static-tests.sh. It checks syntax, manifests, immutable Stage 1 bytes, prohibited command literals, exact 16-file specifications, and the existing temporary-filesystem Stage 1 state tests. It never runs the Stage 2 operator script.

After review, commit the Stage 2 package. From a clean checkout of that commit, the future operator invocation is:

sudo /bin/bash docs/le-app-database-migration/phase2b-stage2-verification/tests/40-verify-stage2.sh

Require the final summary to report daemon_reload=1, installed_files=16, and failures=0. Any other result is a hard stop; do not continue to namespace or runtime activation.