# QA — HepatoFabric3D

Date: 2026-06-01
Builder: parallel build Agent #3 (step2)
Build path: `projects/2026-06-01-3-hepatofabric3d/`

## Environment probed

| Package | Status | Used for |
|---------|--------|----------|
| numpy 2.0.2 | present (required) | arrays, RNG |
| scipy 1.13.1 | present | labeling, distance transform, gaussian |
| scikit-image | **ABSENT** | 3D skeletonize → numpy medial-axis fallback engaged |
| networkx 3.2.1 | present | graph topology |
| skan | absent | not required (networkx + manual fallback cover it) |
| tifffile | absent | only needed for `--input`; gracefully disabled |

Because scikit-image is absent in this environment, the skeleton backend runs
the pure-numpy medial-axis fallback (`numpy-medial-fallback`). This is the
intended graceful-degradation path and is labeled in every report.

## Checks performed

### 1. Syntax validation ✅
```
python3 -c "import ast; ast.parse(open('main.py').read())"
-> OK (parses cleanly, twice — initial and after edits)
```

### 2. `--help` ✅
```
python3 main.py --help
-> usage shown; all flags present: --demo --input --stage{F1..F4} --top
   --summary --seed --json -h. Epilog carries research-only disclaimer.
```

### 3. `--summary` (real execution capture) ✅
```
Backends: skel=numpy-fallback | label=scipy | graph=networkx | tiff=absent
SUMMARY (one line per stage):
  F1  area=4.82%   comps=62  connIdx=0.000  bridges=0   band=minimal   zonation=+0.14
  F2  area=7.66%   comps=89  connIdx=0.190  bridges=2   band=minimal   zonation=-0.03
  F3  area=12.05%  comps=88  connIdx=0.630  bridges=6   band=moderate  zonation=-0.00
  F4  area=17.90%  comps=94  connIdx=1.246  bridges=12  band=moderate  zonation=-0.11
```
Sanity: connectivity index increases **monotonically** F1→F4 (0.0 → 1.246),
bridging septa rise 0→2→6→12, and steatosis zonation shifts from periportal
(+0.14) toward pericentral (-0.11) across the series. Expected behavior. ✅

### 4. Default full report + comparison ✅
`python3 main.py` prints per-stage sections [1]-[4] for F1-F4 plus the
COMPARATIVE REPORT table and an auto-selected KEY INSIGHT:
```
KEY INSIGHT (smallest 2D-area gap with largest 3D-topology divergence):
  F3 vs F4 differ by only 5.85% in 2D collagen area fraction,
  yet their 3D connectivity index differs by 0.616 and bridging
  septa count by 6.
```
(Earlier scoring picked F1 vs F4; fixed to reward area-similarity so the chosen
pair genuinely demonstrates "same area%, different topology".) ✅

### 5. `--stage`, `--top`, `--json` ✅
- `--stage F3` → single-stage report, connIdx 0.630, band 'moderate' (suggests F3). ✅
- `--top 2` → table ordered F4 (1.246) then F3 (0.630) by connectivity. ✅
- `--stage F2 --json` → valid JSON, parsed back with json.load; connIdx 0.19. ✅

### 6. Pure-numpy fallback (scipy + skimage + networkx forced absent) ✅
Ran with `PYTHONPATH` shadowing scipy/skimage/networkx/tifffile to raise
ImportError:
```
Backends: skel=numpy-fallback | label=numpy-fallback | graph=manual-fallback | tiff=absent
STAGE F3 ... connIdx=0.630 ... graph: components=8 largest=7 maxDeg=3 (manual-unionfind)
```
Runs end-to-end. Ground-truth-derived connectivity index (0.630) is identical
across backends; voxel-level counts differ (cruder threshold/skeleton) but are
labeled. **`python3 main.py` succeeds with numpy only.** ✅

### 7. `--input` error handling ✅
- `--input /tmp/nope.tif` with tifffile absent → clean error message + install
  hint, exit 1, no traceback. ✅
- External-volume analysis path (`_ExternalVolume`) tested directly with a
  fabricated numpy volume: after threshold fix, reports non-zero area
  (0.99%, 37 components), portal-node inference works. ✅

### 8. Data file load ✅
```
json.load('data/fibrosis_reference.json')
-> keys: schema_version, description, disclaimer, staging_systems,
   synthetic_stage_params, connectivity_index_thresholds, volume_defaults
   stages: F1 F2 F3 F4 | bands: minimal low moderate high
```

### 9. Disclaimer presence ✅
Research-only disclaimer appears in: README (top banner + dedicated section),
CLI header, `--help` epilog, every summary/comparison footer.

### 10. Runtime / offline ✅
Full F1-F4 default run: ~1.07s wall (scipy present). No network calls anywhere
(synthetic generation only). ✅

## Issues found & fixed during QA

1. **KEY INSIGHT pair selection** — original score over-weighted the
   connectivity gap and picked the F1-vs-F4 extremes (13% area gap), which is a
   poor "same-area" illustration. Rescored to `conn_gap * (1 - area_gap/area_span)`;
   now selects F3 vs F4 (5.85% area gap). Fixed.
2. **`%%` literal leaking into output** — two print strings used `%%` outside a
   `%`-format context, showing literal `%%`. Fixed.
3. **Degenerate empty mask on sparse user volumes** — `threshold_collagen`
   percentile could threshold everything out (area=0, comps=0) on
   background-dominated inputs. Added positive-voxel-only percentile plus a
   max-relative fallback cut. Verified non-zero on test volume.

## Result

✅ PASS — all required checks green. `python3 main.py` runs unconditionally
(numpy-only path verified). Optional deps (scipy/networkx present;
scikit-image/skan/tifffile absent) degrade gracefully with labeled backends.
