Files

479 lines
26 KiB
Markdown

# AI Project Context
This is the short, authoritative repository map for humans and AI agents.
The long [`README.md`](README.md) is an operational history and command
reference; it is not the experiment scheduler.
## One-sentence objective
Determine whether strictly causal global Omni context plus local audiovisual
floor evidence organized around visible people can improve an invisible
egocentric wearer's **floor-entry** and **floor-yield** behavior in natural
multiparty conversation.
The paper is not simply “replace EgoSpeak's encoders with a newer backbone.”
The decisive test is whether correct person-aligned vision reduces false
wearer entries during other-to-other conversation at matched wearer-onset
recall, without hurting addressed responses or yield behavior.
## Source-of-truth order
| Need | Authoritative file |
|---|---|
| Current four-page manuscript structure and narrowed claim scope (2026-09-17) | [docs/manuscript_local_evidence_structure_2026-09-17.md](docs/manuscript_local_evidence_structure_2026-09-17.md) |
| ICASSP submission story, pruning and acceptance evidence bar | [docs/icassp2027_acceptance_storyline_2026-09-10.md](docs/icassp2027_acceptance_storyline_2026-09-10.md) |
| Current paper thesis and claim boundary | [docs/global_local_omni_paper_position_2026-09-07.md](docs/global_local_omni_paper_position_2026-09-07.md) |
| Progressive feature-fusion implementation route | [docs/progressive_feature_fusion_route_2026-09-02.md](docs/progressive_feature_fusion_route_2026-09-02.md) |
| Current hypotheses, experiment gates and stopping rules | [`docs/omni_speaker_floor_experiment_plan_2026-08-30.md`](docs/omni_speaker_floor_experiment_plan_2026-08-30.md) |
| Novelty boundary and claim restrictions | [`docs/multiparty_egocentric_floor_gap_audit_2026-08-30.md`](docs/multiparty_egocentric_floor_gap_audit_2026-08-30.md) |
| Code/data map and current blocker | This file |
| Runnable commands and implementation history | [`README.md`](README.md) and [`docs/README.md`](docs/README.md) |
| Archived measured results | [`results/README.md`](results/README.md) |
| Literature summaries | [`literature_notes/README.md`](literature_notes/README.md) |
When files disagree, prefer the newest scoped document and verify behavior in
code/tests. A planned experiment is not an implemented feature or a measured
result.
The 2026-09-17 manuscript restructure uses causal local evidence adaptation
as its umbrella: model architecture (actual G + recent 1-s Qwen audio,
optional pooled vision) and streaming inference architecture (8-s maximum
presentation budget, completed blocks, temporary observed tail). Semantic
opportunity evaluation is deferred while annotation quality is audited;
dense forecasting and raw endpoint onset/offset AP remain the current paper
scope. This does not complete P0 or establish a visual/tail-performance gain.
Earlier person-grounded/semantic strategy documents remain conditional
extensions and historical records, not descriptions of the default draft.
The scoped [analysis experiment log](docs/local_evidence_analysis_experiment_log_2026-09-17.md)
queues local-evidence controls, tail-refresh x local-audio comparisons, context
caps, persistence, activity-error cases, full-pipeline profiling, and paired
Qwen Omni 3B/7B--MiniCPM-o 4.5 backbone-transfer controls. Transfer fixes the
local audio sidecar and audits readout, refresh, and actual history; the
MiniCPM entry point is not GPU-verified or an established matched 8-s cap.
All are pending in that log; Omni LoRA is deferred and no semantic gate is completed.
## Current state
### Verified assets
- Strictly causal 5 Hz Qwen2.5-Omni global state extraction on EasyCom.
- Prepared-media and direct-original-media extraction paths.
- Frozen head training/inference with EgoSpeak-compatible 10-horizon AP/mAP.
- EasyCom local Qwen visual crops and recent mixed-audio sidecars.
- Encoder-only A/V/AV and `G`, `G+A`, `G+V`, `G+A+V` baselines.
- Ego4D batch and 8-GPU local-feature launchers, with strict frame audits.
- Timing-derived wearer floor-state and event diagnostics.
- Human-annotated fixed-horizon entry/INT evaluator with point positives,
region-level hard negatives and training-side threshold calibration.
- A separate EgoSpeak-compatible activity-derived event-proxy evaluator that
pools the 0.6--2.0-s wearer trajectory over equal-width 0.6-s causal decision
regions. It preserves one score per annotated opportunity and never treats
the longer annotation evidence interval as a scoring window.
- An opportunity-conditioned dense-output evaluator aligns ENTRY positives and
core negatives at non-wearer releases, aligns INT positives and hold-floor
negatives at competing non-wearer onsets, and applies strict competing-risk
censoring. ENTRY positives whose selected release is followed by a competing
other onset or intervening wearer activity are excluded, and ambiguous
multi-competitor INT cases are not assigned an arbitrary onset. It uses only
the original wearer probabilities, excludes wearer backchannels from
headline ENTRY FPR, and records unanchored/ambiguous cases.
### Implemented but not GPU-verified
- `extract_easycom_multiwindow.py` exports 2/4/6/8/10/12-second native-block
context banks in one shared AV preparation pass, with independent Thinker KV
per cap. CPU tests match separate single-window replay and both head/local
loaders; real-GPU replay and media smoke remain required. See
[`docs/easycom_multiwindow_features.md`](docs/easycom_multiwindow_features.md).
- Two no-box full-frame Qwen AV screens are implemented on top of the matched
global-Omni + recent-Qwen-audio inputs. F2 queries the latest strictly
pre-cutoff post-merger spatial-token grid; F3 first applies a shared causal
GRU to the latest same-grid-position histories. They use no face boxes or
person IDs and therefore test full-frame visual context, not speaker
grounding. See [`docs/easycom_fullframe_qwen_av.md`](docs/easycom_fullframe_qwen_av.md).
- Qwen2.5-Omni now has two matched chunk-handling controls around the existing
5 Hz partial-tail path: native completed 2-second blocks held between
boundaries, and non-native 200-ms payload commits. Both preserve dense causal
alignment and save state-age/processing diagnostics, but still require the
documented real-GPU smoke test before a full run. See
[`docs/qwen_tail_chunk_ablation.md`](docs/qwen_tail_chunk_ablation.md).
- MiniCPM-o 4.5 input-only feature extraction has two unmeasured modes: a
native 1 Hz state causally held on the 5 Hz grid, and a 5 Hz completed-cache
plus temporary-partial-tail branch. The latter refuses the normal
head-loading contract unless boundary, replay, snapshot-restoration, and
future-audio checks pass. A real-GPU smoke run is still required; see
[`docs/minicpmo_streaming_features.md`](docs/minicpmo_streaming_features.md).
### Measured screening result
- The released EgoSpeak MiniROADA A+V checkpoint on Sessions 1--3 obtains
`74.124` 0.2-second mAP and `61.155` ten-horizon mean mAP. Raw wearer
onset/offset anticipation is harder (`24.491/38.024` mean AP). On provisional
LLM-assisted matched opportunities, the original v1 evaluator reported
ENTRY/INT AP of `52.487/20.135`; those opportunity values are superseded
because v1 could cross a competing ENTRY onset and arbitrarily selected among
multiple INT competitors. Figure-7-style continuous wearer curves support
dense forecasting, not semantic floor-control claims. The unaffected dense
curves and historical v1 opportunity record are in
[`results/easycom_egospeak_three_level_eval_2026-09-16/`](results/easycom_egospeak_three_level_eval_2026-09-16/README.md).
- F1 Frame-set Attention uses fused G+A as one query over the latest half-open
causal person set. On the existing three-seed, four-epoch test screen it
obtained `81.77745 +/- 0.15638` 0.2-second mAP and
`64.95099 +/- 0.09451` ten-horizon mean mAP. This is stable, but is not a
matched improvement over the historical no-dev pooled result. Its three-seed
boundary response/yield AP is `28.423 +/- 0.480` / `48.641 +/- 0.391`; on
the superseded v1 opportunity evaluator reported ENTRY/INT AP of
`41.561 +/- 0.581` / `22.991 +/- 0.202`; these values require a v2 rerun and
cannot currently support a semantic-selectivity conclusion. A same-split
three-seed pooled control and correct/wrong visual controls remain missing.
See the historical
[`screening record`](results/easycom_omni_floor_screen_2026-09-16/README.md).
### Measured local-fusion screens
- Two current-sidecar screening controls: `audio-person-frame-set` adds a
zero-gated audio-to-person read before the F1 query, and
`gated-person-state-adapter` adds a zero-gated person residual after the
frozen Thinker readout. They are explicitly recorded as F5/F6 screening,
not full history-conditioned F5 or an internal Thinker F6 adapter.
- `track-gru-residual` builds strictly causal histories from anonymous provided
track IDs, applies one shared GRU per track, lets G+A query current track
states, and adds a zero-gated correction to an exactly initialized pooled
`G+A+V` checkpoint. The matched Session-12 three-seed screen was null
(`+0.01306 +/- 0.02368` percentage points in ten-horizon mean mAP). A seed-0
Sessions-1--3 audit reproduced pooled predictions to within `-0.00030`
points. Saved residual gates were only `-0.0015` to `+0.0108`; because a zero
scalar gate also blocks the initial branch gradient, this is recorded as an
optimization failure rather than evidence that track continuity is useless.
See the immutable
[`result record`](results/easycom_track_gru_residual_4ep_2026-09-07/README.md)
and [`implementation note`](docs/easycom_track_gru_residual.md).
- `causal-person-adapter-v2` is now implemented but unmeasured. It preserves an
exact matched pooled `G+A+V` base, reuses the same causal anonymous track
histories, replaces the blocking zero scalar with a sample-dependent vector
gate, and zero-initializes only the final residual-logit projection. Its
isolated runbook is
[`docs/easycom_causal_person_adapter_v2.md`](docs/easycom_causal_person_adapter_v2.md).
This is an optimization-repair/incremental screen, not evidence that track
identity matters.
- `causal-person-adapter-egospeak-gru` is an unmeasured controlled ablation of
V2. It changes only the per-track visual temporal stem to EgoSpeak
MiniROADA's `Linear-LN-ReLU-Dropout-GRU-ReLU` ordering while retaining V2's
pooled base, G+A person query, set attention, dynamic gate and zero-output
residual. It is an EgoSpeak-style per-track adaptation, not a reproduction of
EgoSpeak's global 5 Hz RGB+audio GRU. See its
[`runbook`](docs/easycom_egospeak_style_person_gru.md).
- `highrate-person-av-attention` is now implemented but unmeasured. A separate
extractor runs the published, frozen MuVAP frontend at 25 Hz: mixed audio is
encoded once as a shared CPC stream, while each face track contributes a
causal visual state plus one AV active-speaker score. At each 5 Hz right edge
`G+U` queries that variable person set. The first screen loads and freezes
the matched trained global-Omni + traditional-audio base and learns a
zero-initialized person-logit residual. This is global Omni context plus
traditional high-rate audio/person perception;
it is not the full MuVAP forecasting model and does not yet include online
detection. See the
[`runbook`](docs/easycom_highrate_person_av.md).
- A MuVAP-only Stage-3 baseline is now implemented as a clean-room paper
reconstruction. It freezes the released 25 Hz mono role-VAP and causal ASD
backbones, lets `Z_VAP` query the variable `Z_ASD` person set at each frame,
applies a one-layer causal temporal Transformer, and trains only that fusion
plus the EgoSpeak 10-horizon head. It deliberately excludes Omni and is not
described as upstream Stage-3 code because that entry point remains
unpublished. See the
[`runbook`](docs/easycom_muvap_stage3_baseline.md).
- `egospeak-omni-zero-residual` has a user-reported seed-0 screen. Causal-valid
dense mean mAP increases from `61.193` to `62.714`; raw onset/offset mean
AP increases from `24.491/38.024` to `25.091/40.604`. Offset improves at all
ten horizons; onset gains are modest. Actual checkpoint provenance and
counts were not supplied for independent verification. Opportunity-based
semantic evaluation is temporarily paused because the user suspects
provisional annotation issues; historical metrics remain archived and P0
remains unfinished. See the
[`result record`](results/easycom_egospeak_omni_residual_2026-09-16/README.md).
The implementation strictly
loads and freezes the released MiniROADA A+V/GRU/anticipation path, reuses
existing causal 5 Hz Omni readouts, adds an explicit ten-bin partial-tail
phase embedding, and learns only a zero-output residual over the original
logits. The launcher verifies exact initial recovery and exports fused and
matched frozen-EgoSpeak predictions together. Existing Omni states already
combine completed history with the current partial tail; separating those
states remains a future extraction ablation. See the
[`runbook`](docs/easycom_egospeak_omni_residual.md).
### Unmeasured dual-stream continuation
Stage-two `egospeak-omni-selective-finetune` is implemented but unmeasured.
It strictly continues the trained stage-one checkpoint, selectively adapts
MiniROADA's GRU/forecast heads with a lower learning rate, and leaves original
feature extractors, input projection and Omni frozen. It exports immutable
stage-one/original references and automatic threshold-free raw endpoint AP.
The user requested this exploratory continuation while semantic annotations
are audited; P0 remains unfinished. Four additional epochs means eight total
adaptation epochs, requiring the fusion-only continuation control for a
budget-matched unfreezing comparison. See the
[`stage-two runbook`](docs/easycom_egospeak_omni_finetune.md).
### Measured oracle-information screen
- A seed-0, four-epoch Session-12 development screen supplied transcript-derived
current/past speech activity only through the explicit analysis-only oracle
contract. `aggregate-visible` improved over the matched local-audio baseline
by `+6.96649` points at 0.2 seconds and `+2.91047` points in ten-horizon mean
mAP. Visibility-only and 1-second-delayed controls stayed near audio.
- Person identity continuity is not supported: `aggregate-visible` exceeded
`per-person`, while `per-person` and `shuffled-id` were effectively tied.
The result motivates a learned causal local active-speaker/floor-evidence
branch, but does not justify a track-continuity claim. See the immutable
[`result record`](results/easycom_oracle_information_4ep_2026-09-07/README.md).
### Current blocker
`evaluate_floor_state_changes.py` now implements and unit-tests the required
competing-risk attribution: an other-offset response window ends before the
next other onset, and an other-onset yield window ends before the next other
offset. It also supports disjoint training-side threshold calibration, rejects
official-test calibration on Sessions 1--3, reports false-entry/false-stop
counts, and prints auditable timelines.
P0 is still not frozen. The remaining blocker is a real-data audit: run the v2
evaluator on saved calibration/test predictions, inspect event timelines and
censoring/invalid counts, independently review sampled events, and freeze the
scenario definitions. These values remain timing-derived behavior proxies,
not semantic EOT/INT or addressee ground truth. See the
[`runbook`](docs/easycom_competing_risk_floor_evaluation.md).
Three explanation-oriented diagnostics are implemented but unmeasured: a
separately identified post-boundary reactive evaluator, a direct
competing-risk response/yield probe over the same frozen Omni/audio/oracle
inputs, and a privileged direct-to-wearer `Target_of_Speech` oracle with an
equal-width zero-channel control. They diagnose timing, objective and
addressee-information limitations respectively; none changes or freezes P0.
See [`docs/easycom_floor_event_diagnostics.md`](docs/easycom_floor_event_diagnostics.md).
### Next experiment gates
The user has temporarily chosen to run the dense-AP model screen while P0 is
unfinished. This does not relax the paper claim: event numbers remain proxy
diagnostics until the v2 attribution, calibration and scenario audit is frozen
on real data.
1. **P0:** run and audit competing-risk v2, then freeze event attribution,
calibration, splits and scenario definitions.
2. **P1:** confirm the aggregate/per-person oracle screen over seeds 1--2,
then train a causal visible-person active-speaker/floor-evidence estimator;
compare correct, wrong-person and time-shifted visual inputs.
3. **P2:** re-evaluate `G`, `G+A`, `G+A+pooled-V` under the frozen event protocol.
4. **P3:** only if learned person-organized evidence beats aggregate/no-ID
controls, repair the matched anonymous-track-GRU residual optimization by
replacing the scalar zero gate with a zero-initialized residual output
layer, and verify nonzero branch gradients on Session 12. Add broken-ID,
wrong-time and remove-person controls only if that corrected screen improves.
5. **P4:** attempt person-memory adapter/Thinker LoRA only if P1/P3 pass.
The detailed architecture, controls and stopping gates for P3 are frozen in
[the progressive feature-fusion route](docs/progressive_feature_fusion_route_2026-09-02.md).
## Stable data contracts
### Time and labels
- Sampling rate: 5 Hz (`STEP_SECONDS = 0.2`).
- State row `j` uses media no later than `(j+1)/5` seconds.
- Future horizons: 10, corresponding to 0.2, 0.4, ..., 2.0 seconds.
- Class order: `background`, `camera_wearer_speaking`,
`other_people_speaking`.
- EasyCom targets are dense `[T, 3]`; predictions are
`[target_time, class, future_horizon]`.
- EasyCom split: train Session 4--12; test Session 1--3.
- Locked final head protocol: exactly 4 epochs on all Sessions 4--12, no
development checkpoint selection, followed by one evaluation on Sessions
1--3. Historical Session-12 screens remain development diagnostics and are
not the final training protocol.
### Global feature sidecar
Produced by `streaming_turn_model.py` or
`streaming_turn_model_directly.py` as a `.pt` dictionary. Required training
fields include:
- `states`: `[T, 2048]` or multi-layer `[T, L, 2048]`;
- `times`: `[0.2, 0.4, ...]`;
- `label_indices`: dense zero-origin indices into the target timeline;
- extraction `mode`, configuration and source metadata.
Never silently accept non-zero origins, missing label indices or mismatched
time grids.
### Local feature sidecar
Produced by `extract_turn_local_features.py`, validated by
`local_turn_features.py`, and stored as `qwen-local-pooled-v1`. It contains
native 2048D visual/audio vectors, validity masks, times, label indices,
source metadata and the exact extraction config. A partial smoke sidecar must
not be reused as a full-session feature file.
### Inference result
`infer_turn_head.py` writes:
```text
RESULT_DIR/
metrics.json
pred/<sample>.npy # [T, 3, 10]
valid/<sample>.npy # [T, 10]
transition_pred/ # only when the auxiliary head exists
```
`evaluate_floor_state_changes.py` consumes `pred/` and `valid/`; it does not
run a GPU model.
## Canonical pipelines
### Direct global features (preferred semantic path)
```text
original EasyCom/Ego4D media
-> streaming_turn_model_directly.py
-> global feature .pt
-> train_turn_head.py
-> checkpoint .pth + .json
-> infer_turn_head.py
-> pred/ + valid/ + metrics.json
-> evaluate_floor_state_changes.py
```
This path samples source video causally in Python and avoids requiring a
re-encoded fixed-FPS MP4.
### Prepared-media compatibility path
```text
prepare_easycom_session.py / prepare_ego4d_2fps.py
-> strict 2 FPS media + metadata
-> streaming_turn_model.py
-> the same training/inference pipeline
```
Keep it for reproducibility, cache-replay verification and comparisons with
already extracted features.
### Local evidence path
```text
raw media + person boxes + existing global reference .pt
-> extract_turn_local_features.py
(or extract_ego4d_local_multigpu.py supervisor)
-> local .pt sidecar
-> train_turn_head.py --local-feature-root ...
-> infer_turn_head.py --local-feature-root ...
```
The current local visual feature is pooled. It does not by itself prove
speaker identity, active-speaker alignment or addressee understanding.
## Root-code map
Root scripts remain flat because remote L20 commands call them directly.
Do not reorganize them physically without compatibility wrappers.
### Shared libraries
| File | Responsibility |
|---|---|
| `easycom_head.py` | Dataset constants, feature loading, heads, losses and AP metrics |
| `local_turn_features.py` | Local-sidecar contract, alignment, crops and pooling |
| `omni_cached_av.py` | Cached AV structures used by Omni extraction/LoRA paths |
### Global feature extraction
| File | Responsibility |
|---|---|
| `streaming_turn_model_directly.py` | Preferred direct raw-media causal extraction |
| `streaming_turn_model.py` | Prepared-media causal-prefix/open-stream extraction and replay checks |
| `qwen_omni_features.py` | Single-window feature inspection |
| `cache_omni_av_pieces.py` | Build reusable AV pieces for LoRA experiments |
| `extract_ego4d_batch.py` | Persistent-model Ego4D global batch extraction |
| `analyze_block_phase.py` | Streaming block-phase diagnostics |
| `profile_streaming_bottleneck_gpu7.py` | GPU extraction profiling |
### Data preparation and audit
| File | Responsibility |
|---|---|
| `easycom_manifest.py` | EasyCom clip inventory and dense-timeline validation |
| `prepare_easycom_session.py` | Optional strict-grid EasyCom media preparation |
| `prepare_ego4d_2fps.py` | Optional strict-grid Ego4D media preparation |
| `prepare_encoder_only_features.py` | Convert local sidecars into encoder-only references |
| `audit_egospeak_features.py` | Audit released EgoSpeak feature layout |
| `derive_easycom_interaction_events.py` | Deterministic transcript/timing proxies |
| `adjudicate_easycom_interaction_events.py` | Optional ambiguous-event adjudication |
| `audit_easycom_floor_events.py` | Count EOT/takeover/backchannel candidates |
| `prepare_easycom_eot_int_review.py` | Retain every raw wearer onset and offset as an unresolved semantic candidate; attach nearby participant boundaries, activity relations, transcripts, and target-of-speech facts without entry/takeover gating; also scan independent hard-negative regions; semantic labels remain AI/human review outputs |
| `adjudicate_easycom_eot_int_candidates.py` | Add optional DeepSeek transcript/timing suggestions; never human ground truth |
| `prepare_ego4d_av_eot_int_review.py` | Build the equivalent unresolved wearer onset/offset and hard-negative review queue from official Ego4D AV voice, transcription and annotation-only `is_at_me` fields without changing source boundaries |
| `adjudicate_ego4d_av_eot_int_candidates.py` | Apply the checkpointed strict LLM pre-label client with an Ego4D AV-specific evidence policy; every suggestion still requires human review |
| `review_floor_events.py` / `review_ego4d_av_events.py` | Serve the local EasyCom or Ego4D AV video/transcript workspace and atomically persist human event reviews |
| `prepare_easycom_oracle_features.py` | Build explicit past-only oracle person-activity sidecars and analysis-only addressee controls |
### Local features
| File | Responsibility |
|---|---|
| `extract_turn_local_features.py` | Qwen person-crop visual and recent-audio sidecars |
| `extract_ego4d_local_multigpu.py` | Foreground supervisor for disjoint multi-GPU workers |
### Training, inference and evaluation
| File | Responsibility |
|---|---|
| `train_turn_head.py` | Frozen-backbone head training, optional local fusion/auxiliary losses |
| `infer_turn_head.py` | Save EgoSpeak-compatible predictions and AP/mAP |
| `evaluate_floor_state_changes.py` | CPU floor-state/event proxy evaluation |
| `train_easycom_floor_event_probe.py` | Analysis-only direct response/yield probe with shared competing-risk labels |
| `summarize_easycom_floor_event_probes.py` | Compare direct event-probe conditions under one evaluation contract |
| `summarize_local_baselines.py` | Aggregate local baseline `metrics.json` files |
| `run_easycom_track_gru_residual.sh` | Matched pooled and anonymous track-GRU training/inference launcher |
| `run_easycom_oracle_information.sh` | Prepare and screen aggregate/per-person/shuffled/delayed oracle conditions on Session 12 |
| `run_easycom_floor_event_probes.sh` | Prepare, train and compare aggregate/addressee direct event probes |
| `run_easycom_post_boundary_eval.sh` | Compare existing dense runs after the aggregate-other boundary is observed |
| `train_omni_turn_lora.py` | Thinker LoRA training from cached AV |
| `eval_omni_turn_lora.py` | Thinker LoRA evaluation |
### Launchers and tests
- `run_*.sh` and `run_*.py` are reproducible launchers, not libraries.
- `smoke_test.py` is the model/environment smoke check.
- `test_*.py` are CPU-oriented regression tests colocated at root for current
import and command compatibility.
## Terminology restrictions
- **Global (`G`)**: a Thinker readout that may mix audio, video and language
context; do not describe it as audio-free.
- **Local audio (`A`)**: recent mixed audio encoded outside the Thinker.
- **Local visual (`V`)**: person-box visual features; current pooling discards
stable person structure.
- **Encoder-only**: Qwen modality encoder output without Thinker states.
- **Speaker grounding**: evidence that the correct visible person is linked to
the correct speech/time. AV improvement alone is insufficient.
- **Addressee grounding**: evidence about whom speech targets; aggregate
`others` labels cannot supervise it.
- **Floor entry/yield**: observed wearer OFF→ON / ON→OFF behavior under a
defined causal event window; not automatically semantic response or
interruption intent.
## Environment boundaries
- The open-stream replay implementation is pinned to
`transformers==4.52.3` because it uses version-specific internals.
- The local encoder path has been tested with 4.52.3 and 4.57.6; sidecars from
one experiment must share the same recorded contract/version.
- Remote paths differ (`/data/...` and `/data1/...`). Commands should expose
dataset/model roots as variables rather than hard-code one machine.
- Large models, datasets, checkpoints and extracted features live outside Git.
The explicitly audited 72 MiB MuVAP ASD-CPC release and 7 MiB CPC frontend
are a deliberate exception: they are vendored with their exact source and
provenance under `third_party/MuVAP` for reproducible extraction.