Skip to content

fix(worker): A file that records no date is undated, never dated by its last-modified time (#406) - #411

Merged
bitbiter-dev merged 3 commits into
masterfrom
fix/406-undated
Nov 1, 2026
Merged

bitbiter-dev merged 3 commits into
masterfrom
fix/406-undated

Conversation

@bitbiter-dev

Copy link
Copy Markdown
Owner

Closes #406. Part of #383 (design: #383 (comment)).

Summary

A file with no EXIF DateTimeOriginal is undated: ingested with its proxies, never in On This Day, never in a burst. The last-modified time is never a capture date.

 ProcessedContentWriter.ContentOf
-  CaptureDate.FromExifOrLastWrite(exif.DateCaptured, fileSystem, path)   # mtime fallback
+  ContentDate.Set(exif.DateCaptured)                                      # none = undated

 AssetContent(hash, date, …)
-  LocalDateTime? DateCaptured   # null = "keep the row's date"
+  ContentDate Date              # KeepRow | Set(LocalDateTime?)

 KnownFileReconciler.TakeVideoContentAsync
-  photo row's date ?? MOV's mtime
+  Set(photo row's date)          # no photo date: undated

 BurstRepository.UncheckedPhotos           # the burst invariant's one home
+  && DateCaptured != null
 BurstDetectionHandler.CheckAsync
-  IsCandidate = ExifCapturedAt != null && make && model && thumbnail
+  IsCandidate = make && model && thumbnail   # unreadable original: similarity only

 EfAssetViewReader.Query
-  selection.Apply(visible.NotHiddenFor(u).NotPairedMovOf(visible))
+  selection.Apply(… .Where(a => a.DateCaptured != null))   # AssetView keeps non-null dates (#402 widens)

Evidence

  • Before: a file without EXIF date took its mtime (WriteAsync_WithNullDateCaptured_FallsBackToFileLastWriteTimeAsync: year 2023).
    After: WriteAsync_WithoutExifDate_StoresTheFileUndatedAsync, …LivePhotoPairWithoutExifDate_LeavesBothHalvesUndatedAsync, ReconcileAsync_VideoContentChangedWithoutAPhotoRow_MakesTheVideoUndatedAsync all seed an mtime and assert DateCaptured == null.
  • Removing either filter fails its test: UncheckedPhotos_UndatedPhoto_IsNeitherLoadedNorCountedAsync, UncheckedPhotos_StatesTheDatedPhotosInItsOwnPredicate (SQL shape), OnThisDay_UndatedBurstMember_IsLeftOut.
  • Unit and integration suites pass; scripts/mutation-changed.sh: Core 100.00 %, Worker 100.00 %; scripts/derive-openapi.sh --check: docs/openapi/v1.json is current.

Merge Danger

Door: two-way

Nothing is deployed, so the migration needs no backfill; Down restores NOT NULL.

Blast Radius: ingestion

Until #404, every video except a Live Photo's is undated (no video date source yet), as the design accepts.

🤖 Generated with Claude Code

bitbiter-dev and others added 3 commits November 1, 2026 15:49
…ts last-modified time (#406)

A photo or video without EXIF DateTimeOriginal is stored undated: DateCaptured,
Month, Day and Year are null (one migration). The last-modified fallback is
gone; with it, CaptureDate.FromExifOrLastWrite became the identity, so it is
deleted rather than kept until #404, and IFileSystem leaves the content writers,
PairedMovResolver and KnownFileReconciler.

- AssetContent carries a ContentDate: KeepRow, or Set(date or none). Undated()
  becomes KeepingRowDate(); keeping and "undated" are no longer the same null.
- A Live Photo's video edited beside no dated photo row is undated.
- Burst detection checks only dated photos (UncheckedPhotos, the invariant's one
  home) and no longer re-reads the EXIF date; an unreadable original is still a
  candidate, grouped by similarity alone, as agreed with the maintainer.
- EfAssetViewReader filters undated assets before the selection, so AssetView
  and the API contract keep non-null dates (docs/openapi/v1.json unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#403 and #410 both took 0015. The capture-date ADR merged first and is the
one #383 and its slices cite, so the later one moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts (#406)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bitbiter-dev
bitbiter-dev merged commit a0f97f2 into master Nov 1, 2026
21 checks passed
@bitbiter-dev
bitbiter-dev deleted the fix/406-undated branch November 1, 2026 04:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(worker): A file that records no date is undated, never dated by its last-modified time

1 participant