fix: rolling-rebase audit follow-ups — library-sync favorite leak, re-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) - #794
Merged
Conversation
…h globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses.
Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg`
(one job's cleanup unlinked the frame the other was reading -> 'Input
file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while
the previous job could still be probing it.
- serialize trims per asset id (AsyncLock, in-process)
- unique per-invocation temp frame path (covers cross-process overlap)
- trim to a temp file and atomically rename into place
…#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths.
…sing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json.
…ch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK.
Deeds67
added a commit
that referenced
this pull request
Jul 18, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 20, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 21, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 22, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 22, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 23, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 23, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 24, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
Deeds67
added a commit
that referenced
this pull request
Jul 25, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
ya4ept
pushed a commit
to ya4ept/Noodle_gallery
that referenced
this pull request
Jul 30, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (open-noodle#743) (open-noodle#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (open-noodle#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (open-noodle#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (open-noodle#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (open-noodle#743) - asset_offline_description was the open-noodle#672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (open-noodle#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (open-noodle#743)
Deeds67
added a commit
that referenced
this pull request
Aug 10, 2026
…-trim race, Dart nullable-items patch, branding/i18n, ownership-glob (#743) (#794) * fix(tools): escape route-group parens in ownership-coverage micromatch globs (#743) micromatch parses bare `(user)` as an empty regex capture group, so ownership globs like `web/src/routes/(user)/spaces/**` silently matched nothing in coverage.ts (both the ignore filter and the glob classifier). Port the literal-parens escaping the S20 manifest guard already uses. * fix(server): make rapid re-trims of the same video race-free (#743) Overlapping trim jobs shared the frame path `{id}_edited.mp4.frame.jpg` (one job's cleanup unlinked the frame the other was reading -> 'Input file is missing') and ffmpeg overwrote `{id}_edited.mp4` in place while the previous job could still be probing it. - serialize trims per asset id (AsyncLock, in-process) - unique per-invocation temp frame path (covers cross-process overlap) - trim to a temp file and atomically rename into place * fix(server): mask isFavorite in LibraryAssetSync for non-owned assets (#743) LibraryAssetSync streamed the raw asset.isFavorite to every library member. Apply the same per-row ownership mask SharedSpaceAssetSync / AlbumAssetSync use (owner keeps the flag, everyone else sees false) on both the backfill and upsert paths. * fix(branding): brand the 4 remaining Immich i18n keys + close the missing-override gap (#743) - asset_offline_description was the #672 bug shape again: overridden at the TOP LEVEL with the admin-block text, clobbering the user-facing string while admin.asset_offline_description kept leaking 'Immich'. Both nesting levels now carry their own branded text. - add overrides for import_option_skip_duplicates, my_immich_title, my_immich_description (the my.immich.app redirect service keeps its domain name). - verify-branding.sh and test-i18n-branding.sh only checked keys that HAVE overrides, so a leaking key with no override passed silently — both now scan every string value in the branded en.json. * fix(mobile): re-author the Dart nullable-items-in-arrays template patch (#743) The patch that types nullable-item arrays as List<T?> was orphaned: generate-dart-sdk.sh stopped applying it after the upstream three-state DTO template rework (immich-app#27231) rewrote the same template regions, so TimeBucketAssetResponseDto declared its 8 nullable-item array fields as non-nullable and a null city/thumbhash/duration item threw at decode time instead of surfacing in the type system. - re-author the hunks against the current (post-immich-app#27231) template, covering required, x-is-optional, and plain-optional declarations; the rebuild only kicks in when items.isNullable so no other model regenerates differently - re-insert the patch application into generate-dart-sdk.sh (after native_class.mustache.patch, which its hunks are authored against) - drop the stack hand-patch (time_bucket_asset_response_dto.dart.patch) — the template now produces the same declaration - add a preflight guard: the script must keep applying the patch, and every nullable-item array in the spec must be List<T?> in the committed generated model (this is the guard that would have caught the orphaning) The old patch's third hunk (implicit-optional number parsing) is obsolete: the x-is-optional pipeline now handles absent/null numbers. Verified: full mobile test suite (2520 tests) + dart analyze --fatal-infos green on the regenerated SDK. * chore(server): satisfy require-await in the trim-serialization test (#743)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #743.
Follow-ups surfaced during the 2026-07-02 rolling-rebase audit remediation — each verified during a fix slice but left out of scope at the time. This clears the actionable items (1–4, 6); item 5 (benign migration-timestamp collisions) is grandfathered and intentionally needs no code change.
Changes
1.
LibraryAssetSyncleaked the owner'sisFavoriteto all library members (c8dc666)Connected-libraries sync streamed the raw
asset.isFavoriteto every member. Adds asyncLibraryAssetcolumn set (syncAsset minus the favorite) and applies the same per-row ownership maskSharedSpaceAssetSync/AlbumAssetSyncuse — owner keeps the flag, everyone else seesfalse— on both the backfill and upsert paths.2.
handleVideoTrimrace on rapid re-trim (70f8942)Overlapping trims shared the frame path
{id}_edited.mp4.frame.jpg(one job's cleanup unlinked the frame another was reading → "Input file is missing") and ffmpeg overwrote{id}_edited.mp4in place while the previous job could still be probing it. Now serializes trims per-asset (in-processAsyncLock), uses a unique per-invocation temp frame path (covers cross-process overlap), and trims to a temp file that's atomically renamed into place.3. Re-author the Dart
nullable_items_in_arrayspatch for v3 templates (f52de2d,d1a177c)The patch that types nullable-item arrays as
List<T?>was orphaned —generate-dart-sdk.shstopped applying it after the upstream three-state DTO rework (immich-app#27231) rewrote the same template regions, soTimeBucketAssetResponseDtodeclared its 8 nullable-item array fields as non-nullable and a nullcity/thumbhash/durationitem threw at decode time instead of surfacing in the type system. Re-authored against the current template (required, x-is-optional, and plain-optional branches), re-inserted into the SDK build, dropped the now-redundant stack hand-patch, and added a preflight guard so re-orphaning is caught. Verified: full mobile suite (2520 tests) +dart analyze --fatal-infosgreen on the regenerated SDK.4. Four more un-branded i18n keys containing "Immich" (
fb36761)admin.asset_offline_descriptionwas the #672 bug shape again (a top-level override clobbered the user-facing string while the admin key kept leaking). Adds/fixes overrides for it plusimport_option_skip_duplicates,my_immich_title,my_immich_description(the my.immich.app redirect keeps its domain).verify-branding.sh/test-i18n-branding.shonly checked keys that had overrides, so a leaking un-overridden key passed silently — both now scan every string value in the branded en.json.6.
micromatchparens quirk in the ownership-manifest matcher (2a315e4)micromatchparses bare(user)as an empty regex capture group, so route-group globs likeweb/src/routes/(user)/spaces/**silently matched nothing incoverage.ts(both the ignore filter and the glob classifier). Ports the literal-parens escaping the S20 manifest guard already uses.Not addressed (by design)
Item 5 — the two remaining benign migration-timestamp collisions are grandfathered by the S17 guard; renaming is unsafe on a continuously-deployed fork (Kysely missing-migration hard-fail). Tracked only for visibility.
Testing
media.service.spec.ts+ mediumsync-library-asset.spec.tsaddedcoverage.spec.ts+dart-nullable-array-items.spec.tsguards addedtest-i18n-branding.shhardened to scan all string valuesdart analyze --fatal-infosgreen on regenerated SDK