# Incident Reports

## 2026-05-15 - GoPro Chapter Import Overwrite

### Summary

An import from `/Volumes/NO NAME` into `~/Autofs/xdev/autonas/ext01/@Camera/GoPro`
processed 9 GoPro MP4 files and reported success for all of them, but the import
collapsed multiple chapter files onto one destination filename. Because the script
continued after detecting the collision, each later copy overwrote the previous
destination file and the source file was then deleted after verification.

The surviving destination file appears to be the last chapter (`GX091621.MP4`),
stored as:

```text
/Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro/2026-05-15/2026-05-15_22-20-09.mp4
```

### User Command

```bash
media-importer.sh -s "/Volumes/NO NAME" -d ~/Autofs/xdev/autonas/ext01/@Camera/GoPro
```

Effective settings from the log:

```text
Organization pattern: ymd
Destination:         /Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro
Keep originals:      No
Verify mode:         size
Dry run:             No
Keep empty dirs:     No
Source patterns:
  - /Volumes/NO NAME
```

### Impact

- Total files found: 9
- Total size found: 30GB
- Script reported: 9 successfully processed, 0 errors
- Actual observed result: only one full-resolution MP4 remained in destination
- Source MP4 files were removed from the card
- Remaining card artifacts included `.LRV` proxy files and `.THM` thumbnails

The original full-resolution data for the first 8 chapters was not recoverable
from the mounted filesystem at the time of inspection. No recovery tool was run.

### Evidence

The import log repeatedly mapped each source chapter to the same destination:

```text
GX011621.MP4 -> 2026-05-15_22-20-09.mp4
GX021621.MP4 -> 2026-05-15_22-20-09.mp4
GX031621.MP4 -> 2026-05-15_22-20-09.mp4
GX041621.MP4 -> 2026-05-15_22-20-09.mp4
GX051621.MP4 -> 2026-05-15_22-20-09.mp4
GX061621.MP4 -> 2026-05-15_22-20-09.mp4
GX071621.MP4 -> 2026-05-15_22-20-09.mp4
GX081621.MP4 -> 2026-05-15_22-20-09.mp4
GX091621.MP4 -> 2026-05-15_22-20-09.mp4
```

The script emitted warnings like:

```text
Destination already exists: .../2026-05-15_22-20-09.mp4 - proceeding to move/copy and letting external tools handle conflicts
```

That warning was the bug signal. The script should have chosen a unique destination
or failed closed. Instead it proceeded.

Post-incident inspection found:

```text
/Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro/2026-05-15/2026-05-15_22-20-09.mp4
size: 586M
duration: 0:01:49
QuickTime CreateDate: 2026:05:15 19:20:09
```

The duration matched the surviving low-resolution proxy:

```text
GL091621.LRV duration: 108.58s
```

This strongly indicates that the surviving full-resolution file was the last
chapter (`GX091621.MP4`).

### Timeline Recovered From Card Artifacts

The full-resolution MP4 files were gone from `/Volumes/NO NAME/DCIM/100GOPRO`,
but GoPro sidecar/proxy files remained. The `.THM` timestamps preserved a useful
chapter timeline:

```text
GX011621.THM  2026-05-15 19:20:10
GX021621.THM  2026-05-15 19:31:58
GX031621.THM  2026-05-15 19:43:44
GX041621.THM  2026-05-15 19:55:32
GX051621.THM  2026-05-15 20:07:20
GX061621.THM  2026-05-15 20:19:08
GX071621.THM  2026-05-15 20:30:54
GX081621.THM  2026-05-15 20:42:42
GX091621.THM  2026-05-15 20:54:30
```

The `.LRV` proxy files also remained and can preserve low-resolution visual
content when full-resolution MP4 recovery is not possible.

### Root Cause

There were two separate issues:

1. Filename collision handling was unsafe.
   - Multiple GoPro chapters had the same metadata timestamp.
   - `generate_destination_path` generated the same destination filename for all
     files.
   - `process_file` detected the existing destination but continued.
   - The copy operation overwrote the destination.
   - Verification then validated the newly overwritten destination.
   - The source file was deleted.

2. Timestamp interpretation was easy to misunderstand.
   - The script converts QuickTime `CreateDate` values from UTC to local time.
   - The accident log used `22:20:09`, while surviving `.THM/.LRV` filesystem
     timestamps showed `19:20`.
   - This looks like QuickTime UTC conversion rather than a GoPro firmware
     regression.
   - This was not the data-loss mechanism, but it affected the visible names.

### Corrective Actions

Implemented in `media-importer.sh`:

- Added explicit no-overwrite checks in `safe_cp` and `safe_mv`.
- Changed copy/move flow to copy into a temporary file, verify the temporary
  file, then move it into place only if the final destination does not exist.
- Added explicit destination conflict handling. Unattended runs append numeric
  suffixes; interactive runs ask the user and can apply the decision to all
  similar conflicts during long imports.

```text
2026-05-15_19-20-09.mp4
2026-05-15_19-20-09_1.mp4
2026-05-15_19-20-09_2.mp4
```

- Sorted discovered files before processing to make chapter order stable.
- Added `--date-source filesystem` for imports where filesystem timestamps are
  the source of truth.
- GoPro `GX/GH/GP*.MP4` files now prefer filesystem timestamps in default
  `auto` mode, using matching `THM`, matching `LRV`, then MP4 mtime as fallback.
- GoPro imports that use filesystem dates automatically write the corrected clip
  start time into destination metadata before deleting the source.
- Added `--sync-metadata` to force the same metadata write for non-GoPro cases.
- Prevented accidental execution when the script is sourced rather than run.

Implemented in `test_runner.sh`:

- Added `timestamp-collision` regression test.
- The test creates multiple MP4 files with identical `CreateDate` values.
- It runs move mode and verifies that all files arrive at unique destination
  paths, with no overwrite collapse.

Verification command:

```bash
./test_runner.sh timestamp-collision
```

### Operational Guidance

For the next GoPro import, start with dry-run:

```bash
./media-importer.sh -s "/Volumes/GOPRO" -d "$HOME/Autofs/xdev/autonas/ext01/@Camera/GoPro" --dry-run -v
```

Review the dry-run output before removing `--dry-run`. Expected healthy output:

- each `GX*.MP4` maps to a distinct destination path;
- date source says `Filesystem:GX*.THM`, `Filesystem:GX*.LRV`, or the MP4
  basename depending on the best available GoPro filesystem source;
- each GoPro line says metadata date would be synced;
- no line says that an existing destination will be overwritten;
- any unavoidable conflict is either a deliberate numeric suffix or an explicit
  user choice.

After dry-run review, run the same command without `--dry-run`.

### Regression Rule

Any future change to date extraction, filename generation, verification, copy,
move, conflict handling, or GoPro/Garmin/Varia media behavior must run:

```bash
./test_runner.sh timestamp-collision
```

The test must fail if two source media files can collapse into one destination
file while the script still reports success.
