Review fixes and how to reproduce them

This change addresses the eight findings reproduced against commit dac9f1b. It keeps emergency checks active across mission phases, validates complete avoidance paths, rejects incomplete evidence and recovers a failed publication. The implementation and test record are linked from the review history and the current validation record.

What changed

Finding Implemented behavior Regression coverage
R1: navigation fallback only ran on watch Station-fix expiry, lost uplink and onboard conflict decisions run before every armed transit/approach phase. A handoff stops the current tick before publishing another offboard command Every airborne phase with an expired fix; uplink loss during transit/landing; a return-phase conflict; immediate handoff
R2: only the destination avoided other guardians The whole horizontal flight segment must clear each friendly position and stale-position uncertainty circle Crossing, tangent and clear segments; uncertainty circle crossing
R3: no route became a direct flight A failed return plan requests LAND and permanently hands control to PX4 A complete obstacle barrier cannot produce a direct corridor target
R4: carrier/clearances never expired Source-stamped carrier pose, clearance and state streams reject old, future and repeated captures. Launch/landing needs fresh inputs. Lost landing inputs or revoked clearance stop descent and hold horizontally at the vehicle's altitude layer Each input stopped independently; replay/future/nonfinite timestamps; stale vehicle state; explicit landing revocation
R5: disarm was treated as touchdown Station sequencing requires a fresh, advancing PX4 landed/contact report after flight, a disarmed vehicle near its pad, and fresh carrier/state data. The runner independently checks airborne, landed/contact and disarm observations Missing, stale, replayed or non-contact land reports; disarm above deck; valid ordered evidence
R6: incomplete scenario entities could pass The publisher requires exactly one of each configured vehicle and exactly the configured threat set Duplicated/missing/extra identities, empty truth and full publisher rejection without replacing the old sample
R7: failed promotion removed the sample path Promotion restores the old sample on failure. A later invocation recovers an interrupted retirement. Cleanup failures preserve the installed sample Injected second-rename failure, retry, interruption recovery and cleanup failure
R8: cached moves ignored new obstacles Retained keep-clear orders and received station avoidance targets must pass the current free-space check Newly blocked cached route; still-valid cached route; invalid station target

The new test cases are in integration/test_review_regressions.py, tests/test_publication.py, tests/test_guardian.py and the existing ROS contract suites. Tests use actual decision and validation functions with controlled inputs; they do not launch physical hardware.

Flight retests exposed three additional edge cases. A deck altitude estimate below the nominal deck height could leave the vehicle hovering with too little downward command; descent now continues toward a nonnegative target while PX4 owns contact detection and disarm. Also, rejecting a blocked station target when the vehicle had no local tracks produced an infinite predicted-clearance metric, which strict JSON logging correctly rejected. Orders now omit the unavailable metric, and regression tests require strict JSON serialization for both a blocked cached target and a blocked station target with no tracks.

Finally, the station's continuous obstacle geometry could admit a route that the vehicle's more conservative LiDAR grid rejected. Guardian state now includes its blocked cells, and station orders must clear both the known geometry and that reported grid. The vehicle still rechecks against its current map when the order arrives. The station regression adds a newly reported blocked cell on its previous route and requires the replacement route to avoid it. This retains the obstacle gate instead of allowing a rejected route to execute.

Runtime contracts

Station and carrier streams use the ROS clock. In SITL this is Gazebo simulation time, so recording slowdown does not make a healthy stream stale. The allowed age is one simulated second. Capture times must advance, must not be in the future and must not already be expired at receipt. Replayed messages cannot extend validity. The adapter also reports whether its underlying PX4 pose/status telemetry is fresh, so a live report timer cannot keep old vehicle data healthy.

The fleet clearance envelope is now:

{"t": 42.1, "clearance": {"px4_0": {"launch": true, "land": false}}}

Guardian uplinks already contain an envelope and now pass the same freshness gate. Station orders continue to carry their existing authority. The station independently subscribes to each PX4 instance's vehicle_land_detected topic; an advancing report is valid for two simulated seconds. Ground contact and landed must both be true. Initial landed reports before takeoff cannot prove a later touchdown.

sequenceDiagram
  participant Carrier
  participant Station
  participant Vehicle
  participant PX4
  Carrier->>Vehicle: Source-stamped pose
  Vehicle->>Station: Source-stamped state and telemetry freshness
  Vehicle->>Station: Conservative blocked cells used by onboard planning
  Station->>Vehicle: Source-stamped clearance
  Vehicle->>Vehicle: Reject replay, future capture or age over 1 second
  alt Inputs current and landing authorized
    Vehicle->>PX4: Follow pad and descend
  else Inputs stale or landing revoked
    Vehicle->>PX4: Hold horizontal position at assigned altitude layer
  end
  PX4->>Station: Advancing landed/contact report
  Vehicle->>Station: Fresh disarmed state near pad
  Station->>Station: Confirm touchdown before next landing slot
Sequence diagram — source is available below.
Mermaid source
sequenceDiagram
  participant Carrier
  participant Station
  participant Vehicle
  participant PX4
  Carrier->>Vehicle: Source-stamped pose
  Vehicle->>Station: Source-stamped state and telemetry freshness
  Vehicle->>Station: Conservative blocked cells used by onboard planning
  Station->>Vehicle: Source-stamped clearance
  Vehicle->>Vehicle: Reject replay, future capture or age over 1 second
  alt Inputs current and landing authorized
    Vehicle->>PX4: Follow pad and descend
  else Inputs stale or landing revoked
    Vehicle->>PX4: Hold horizontal position at assigned altitude layer
  end
  PX4->>Station: Advancing landed/contact report
  Vehicle->>Station: Fresh disarmed state near pad
  Station->>Station: Confirm touchdown before next landing slot

Guardian emergency priority

Emergency behavior is evaluated before normal armed transit and approach sequencing. Expired independent fixes take priority over ordinary maneuver commands when GNSS is already untrusted. Routine station recovery orders do not restart the return route on every tick. Onboard reflexes and lost-link procedures can override the route; watch continues to use the same shared guardian decision code as the fast simulator.

flowchart TD
  A[Armed mission tick] --> B{Independent station fixes expired?}
  B -->|yes| C[LAND and permanent PX4 handoff]
  B -->|no| D{Onboard conflict or lost uplink?}
  D -->|yes| E[Reflex or lost-link fallback]
  D -->|no| F[Normal phase target]
  F --> G{Return path exists?}
  G -->|no| C
  G -->|yes| H{Landing inputs current?}
  H -->|no| I[Stop descent and hold at assigned layer]
  H -->|yes| J[Validated route or pad target]
Data-flow diagram — source is available below.
Mermaid source
flowchart TD
  A[Armed mission tick] --> B{Independent station fixes expired?}
  B -->|yes| C[LAND and permanent PX4 handoff]
  B -->|no| D{Onboard conflict or lost uplink?}
  D -->|yes| E[Reflex or lost-link fallback]
  D -->|no| F[Normal phase target]
  F --> G{Return path exists?}
  G -->|no| C
  G -->|yes| H{Landing inputs current?}
  H -->|no| I[Stop descent and hold at assigned layer]
  H -->|yes| J[Validated route or pad target]

Publication recovery

The publisher validates the complete scenario matrix and exact entity sets before creating a replacement. It builds the replacement in a sibling directory. On promotion failure, the previous directory is restored. If the process stops between retirement and promotion, the next invocation restores it before validating new input.

This is rollback and restart recovery, not a claim of a filesystem transaction across the generated report and Markdown page. During the two renames a reader can still briefly observe the sample path missing. Hosted Pages deployments use the completed repository snapshot after CI, rather than serving these local directories during publication.

Reproduce the checks in WSL

Use the existing setup from the complete reproduction guide. Do not run two PX4 matrices at once: their ports and simulator resources overlap.

cd "$DRONE_REPO"   # your checkout
export SITL_WORKSPACE="$HOME/work/mission-computer-lab"
export PYTHON="$SITL_WORKSPACE/venv/bin/python"

# Recompute evidence that is bound to the guardian decision source hash.
"$PYTHON" tools/guardian_sim.py --seeds 40 --workers 8 --output artifacts/guardian

# Build, unit-test and run the accelerated fault matrix.
PYTHON="$PYTHON" bash scripts/run_all.sh
"$PYTHON" -O -m unittest discover -s tests -p 'test_*.py'
bash scripts/test_integration.sh

Current suite counts and the completed flight result are recorded in validation. Every command must exit zero. python -O proves the validation gates do not depend on removable assertions.

Run the complete flight matrix into a new directory. A nonempty output directory is rejected to preserve previous evidence:

RETEST="$SITL_WORKSPACE/retests/review-fixes-$(date +%Y%m%d-%H%M%S)"
bash scripts/run_sitl.sh --all --video --output "$RETEST"

Expected: thirteen PASS results and exit zero. The fleet and eight guardian scenarios now include one independent landed_confirmed check per vehicle. Inspect failed checks, observer logs, station logs and mission logs before retrying. Keep the failed directory intact and choose a new output directory for a retry.

For a diagnostic run that completes every case even after a failure, add --keep-going. It preserves each failed check and still exits nonzero; it does not relax publication requirements. The fast-inbound retest exposed a remaining timing limit: with the strengthened obstacle checks, all vehicles recovered and landed safely, but one run moved only 0.38 m farther from the impact point before arrival, below the required 0.50 m increase. See the current validation record before interpreting the older passing replay as evidence for the latest source.

Check the result and the runtime source fingerprints:

"$PYTHON" - "$RETEST" <<'PY'
import hashlib, json, pathlib, sys
run=pathlib.Path(sys.argv[1])
results=json.loads((run/'results.json').read_text())
provenance=json.loads((run/'provenance.json').read_text())
if len(results)!=13 or not all(r['passed'] and all(v is True for v in r['checks'].values()) for r in results):
    raise SystemExit('Incomplete or failed flight matrix')
if provenance['inputs_unchanged'] is not True:
    raise SystemExit('Runtime inputs changed during flight')
for name,want in provenance['environment']['input_sha256'].items():
    if hashlib.sha256(pathlib.Path(name).read_bytes()).hexdigest()!=want:
        raise SystemExit('Source changed after flight: '+name)
print('Complete passing matrix and matching current inputs')
PY

Publish only after those checks pass:

"$PYTHON" tools/publish_sample.py
"$PYTHON" tools/publish_sitl.py --input "$RETEST"
npm run docs
node tests/test_diagrams.mjs
node tests/test_dashboard.mjs
node tests/test_sitl_dashboard.mjs
node tests/test_fleet_dashboard.mjs
node tests/test_guardian_dashboard.mjs
node tests/test_theme.mjs
"$PYTHON" tools/check_docs.py
git diff --check

If Node is installed only on Windows, run the npm and node commands from PowerShell in the same checkout. The local HTML pages load their bundled Mermaid and replay dependencies without a CDN.

graph LR
  A[Source fixes] --> B[Regression tests]
  B --> C[Seeded guardian evaluation]
  B --> D[Fast fault matrix]
  B --> E[PX4 recorded flight matrix]
  E --> F[Hashes and independent landing evidence]
  C --> G[Validated publication]
  D --> G
  F -->|all pass| G
  F -->|failed checks| L[Preserve failed results and prior replay sample]
  L --> H
  G --> H[Rendered guides and replay tests]
  H --> I[Git commit and SSH push]
  I --> J[GitHub CI]
  J --> K[GitHub Pages deployment]
Dependency graph — source is available below.
Mermaid source
graph LR
  A[Source fixes] --> B[Regression tests]
  B --> C[Seeded guardian evaluation]
  B --> D[Fast fault matrix]
  B --> E[PX4 recorded flight matrix]
  E --> F[Hashes and independent landing evidence]
  C --> G[Validated publication]
  D --> G
  F -->|all pass| G
  F -->|failed checks| L[Preserve failed results and prior replay sample]
  L --> H
  G --> H[Rendered guides and replay tests]
  H --> I[Git commit and SSH push]
  I --> J[GitHub CI]
  J --> K[GitHub Pages deployment]

Deployment checks

After committing the fixes, regenerated evidence and rendered guides together, push main over Git SSH. The existing Pages workflow automatically deploys a successful push-triggered simulation-evidence run. Verify that both the CI run and the Pages run reference the pushed commit, then open the public replay and this guide.

From the checkout, inspect the changes before staging them. For this fix, the checkout was initially clean, so every resulting source, test, guide and evidence change belongs to the same update:

git status --short
git diff --check
git add -A
git commit -m "Fix mission safety boundaries and evidence publication"
git push origin main
git rev-parse HEAD
git ls-remote --heads origin main

The two hashes must agree. Do not force-push if another update has reached the remote. With an authenticated GitHub CLI, inspect the runs for the pushed commit:

COMMIT=$(git rev-parse HEAD)
gh run list --commit "$COMMIT" --workflow ci.yml --limit 5
# Copy the run ID shown above into the next command.
gh run watch CI_RUN_ID --exit-status

# The Pages run appears after successful push-triggered CI.
gh run list --commit "$COMMIT" --workflow pages.yml --limit 5
gh run watch PAGES_RUN_ID --exit-status
curl --fail --location https://buicongnguyen.github.io/mission-computer-lab/docs/review-fixes.html

Replace CI_RUN_ID and PAGES_RUN_ID with the numeric IDs from the corresponding listings. If a run fails, use gh run view RUN_ID --log-failed to inspect its failed step. A successful HTTP response alone does not identify the deployed revision: also check the successful Pages run's commit against COMMIT and confirm the new guide's content is present.

The hosted workflow exercises the portable unit, simulation and page checks. ROS and full PX4/Gazebo flights are performed locally in WSL and preserved through runtime provenance. Passing hosted CI is not evidence that GitHub ran the entire flight matrix.

The project remains a software simulation: procedural payload sensors, CPU ONNX, PX4 firmware in Gazebo and illustrative guardian decisions. These fixes do not establish physical flight qualification or Qualcomm hardware/NPU behavior.