Skip to content

feat(artwork): synchronous resize pipeline + variant endpoint (phase 1.h.3) - #30

Merged
InstaZDLL merged 2 commits into
mainfrom
feat/1-h-3-artwork-resize
Jun 6, 2026
Merged

feat(artwork): synchronous resize pipeline + variant endpoint (phase 1.h.3)#30
InstaZDLL merged 2 commits into
mainfrom
feat/1-h-3-artwork-resize

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Summary

Sprint 1 / Phase 1.h.3 — every upload now produces two resize variants (thumb ≤ 128px, preview ≤ 480px, JPEG q85) alongside the byte-perfect original. New public endpoint GET /api/v1/artwork/{hash}/{variant} serves the resized bytes; the bare GET /api/v1/artwork/{hash} also resolves variant hashes via a fallback lookup so a client that persisted only the variant hash can hit the same route.

Highlights

  • Long-edge clamp before resize. DynamicImage::thumbnail would happily upscale a 64×48 source to 128×96 fake pixels — the pipeline checks source.long_edge() ≤ target and passes the image through at native size when no shrink is needed.
  • Variant table (metadata_artwork_variant) with FK ON DELETE CASCADE so a future GC sweep on metadata_artwork reclaims variants in the same transaction. PK on (parent_hash, variant); secondary index on the variant's own hash for the bare-GET fallback.
  • UploadResponse.variants[] alphabetically ordered (preview, thumb) so a client never needs a second round-trip to discover what's available.
  • Idempotent re-upload still skips the resize — the cached variant set is echoed from the DB.
  • Race-safe writes (ON CONFLICT (parent_hash, variant) DO NOTHING) — two concurrent uploads of the same parent collapse to one variant row.
  • Pipeline errors map cleanly: EmptySource/ZeroDimension/Decode → 400, Encode → 500.

Wire contract

POST   /api/v1/artwork            → { hash, byte_size, mime, url, variants: [{variant, hash, mime, byte_size, width, height, url}] }
GET    /api/v1/artwork/{hash}     → original bytes (falls back to variant table if no parent match)
GET    /api/v1/artwork/{h}/thumb  → 128px JPEG q85
GET    /api/v1/artwork/{h}/preview → 480px JPEG q85

All GETs ship Cache-Control: public, max-age=31536000, immutable + ETag = "<hash>".

Sync vs async

Resize runs synchronously on the upload thread in 1.h.3 — a JPEG resize is on the order of tens of milliseconds for the 4 MiB upstream cap, well within a normal upload's latency budget. 1.i.1 will move it behind an apalis Postgres job so the POST responds immediately and the variants stream in over the sync channel.

Test plan

  • cargo fmt --all --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --lib (34/34 pass; 8 new pipeline unit tests + the existing 26 stay green)
  • cargo test integration suite — needs reachable Postgres; CI will run it (5 new integration tests: variant round-trip, unknown variant 400, missing parent 404, idempotent variant echo, bare GET fallback)

Files

  • New module: src/artwork_pipeline.rs (resize + JPEG q85 encode + BLAKE3)
  • New migration: migrations/20260607000000_metadata_artwork_variant.sql
  • Cargo: image = { default-features = false, features = ["jpeg", "png", "webp"] }
  • Handler + DB helpers updated; Config / AppState shape unchanged.

Follow-up

  • 1.i.1apalis Postgres-backed job queue, move pipeline async. Per the post-1.g sprint plan (validated 2026-06-05).

Summary by CodeRabbit

  • New Features

    • Les artworks générent automatiquement deux variantes (thumb, preview) à l'upload; réponse d'upload inclut la liste de variantes et leurs métadonnées.
    • Nouvel endpoint pour récupérer directement une variante (/artwork/{hash}/{variant}) et résolution par hash de variante.
    • Ré-upload idempotent évite régénération/insertion inutiles.
  • Database

    • Ajout d'une table pour stocker les variantes d'artwork avec contraintes et index.
  • Tests

    • Tests e2e étendus pour couvrir pipeline, idempotence et endpoints variantes.
  • Chores

    • Limites d'upload et en-têtes HTTP de cache documentés.

…1.h.3)

Every successful upload now runs through `artwork_pipeline::generate_variants`
to produce two JPEG q85 resizes (thumb ≤ 128px, preview ≤ 480px),
hashed independently and stored alongside the original in
`metadata_artwork_variant` (parent → variant via `ON DELETE CASCADE`
so a future GC of the parent reclaims both in one transaction).

The pipeline applies a long-edge clamp before invoking
`DynamicImage::thumbnail` — that helper happily upscales when the
source is smaller than the bounding box, which is the wrong
semantic for cover art. A 64 × 48 source now passes through at
native size rather than ballooning to 128 × 96 fake pixels.

A new `GET /api/v1/artwork/{hash}/{variant}` route serves the
resized bytes with the same cache + ETag posture as the parent.
The bare `GET /api/v1/artwork/{hash}` also resolves variant hashes
through a fallback lookup on `metadata_artwork_variant.hash`, so a
client that persisted only the variant hash never needs to know
the parent.

Upload response gains a `variants[]` array (alphabetically ordered:
preview, thumb) so a client never needs a second round-trip to
discover what's available. Idempotent re-uploads echo the cached
set without re-running the resize.

Pipeline failures map to 400 (decode rejected the input) or 500
(our JPEG encoder bailed on otherwise valid input). The MIME header
is still validated up-front, so the decoder only sees bytes that
claim to be JPEG / PNG / WebP.

Tests: 8 new unit tests on the pipeline (variant kind round-trip,
clamp behaviour, distinct hashes per variant, no upscale of small
sources) + 5 new integration tests (variant endpoint round-trip,
unknown variant 400, missing parent 404, idempotent variant echo,
bare GET fallback on variant hash). 34/34 lib tests pass.

Sync execution is the right move for 1.h.3 — a JPEG resize is on
the order of tens of milliseconds for the 4 MiB ceiling we enforce
upstream, well within a typical upload's latency budget. 1.i.1 will
move the pipeline behind an apalis job so the upload responds
immediately and the variants stream in over the sync channel.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: faa30fd4-5cb6-4eae-9118-eb6e279a78b4

📥 Commits

Reviewing files that changed from the base of the PR and between 33cef7b and d8534d6.

📒 Files selected for processing (2)
  • src/api/artwork.rs
  • tests/artwork.rs

📝 Walkthrough

Walkthrough

Le PR ajoute un pipeline synchrone générant deux variantes d'image (thumb, preview) à l'upload, stocke leurs métadonnées idempotemment en base, étend les endpoints GET pour servir variantes (par parent/variant ou par hash), et couvre le tout par tests unitaires et E2E.

Changes

Pipeline de génération de variantes redimensionnées

Layer / File(s) Résumé
Schéma de persistance des variantes
migrations/20260607000000_metadata_artwork_variant.sql, src/db.rs
Création de metadata_artwork_variant avec FK CASCADE vers metadata_artwork(hash), CHECK sur variant (thumb,preview), index idx_metadata_artwork_variant_hash. Ajout de VariantMeta et helpers : insert_variant_if_absent, fetch_variant, fetch_meta_by_variant_hash, fetch_variants_for_parent.
Pipeline image avec dépendance et types
Cargo.toml, src/artwork_pipeline.rs, src/lib.rs
Ajout de la dépendance image = { version = "0.25", default-features = false, features = ["jpeg","png","webp"] }. Module artwork_pipeline expose generate_variants produisant thumb (128px) et preview (480px) en JPEG q85, no-upscale, BLAKE3 hash, VariantKind, Variant, PipelineError et tests unitaires.
Types API et routing
src/api/artwork.rs (imports, constantes, types, routes)
Imports du pipeline (PipelineError, VariantKind), constante EXPECTED_VARIANT_COUNT, nouveau pub struct VariantResponse, et extension de UploadResponse avec variants. Enregistrement du handler get_artwork_variant.
Logique d'upload génération et persistence
src/api/artwork.rs (handler d'upload)
Upload exécute le pipeline (création) ou récupère variantes existantes (idempotence). Écrit bytes en stockage et insère métas via insert_variant_if_absent. repair_missing_variants régénère et insère les variants manquants ; réponse inclut la liste triée de VariantResponse.
Endpoints variant et résolution par hash
src/api/artwork.rs (endpoints, erreurs)
GET /api/v1/artwork/{hash} fait fallback vers fetch_meta_by_variant_hash si pas de parent. GET /api/v1/artwork/{hash}/{variant} valide VariantKind, récupère méta et sert bytes avec Content-Type, Content-Length, Cache-Control: immutable et ETag. ArtworkError étendu et mapping depuis PipelineError.
Couverture E2E complète
tests/artwork.rs
Ajout de synth_jpeg (JPEG décodable). Tests: upload+roundtrip avec 2 variantes, idempotence, endpoints variant (vérif content-type, bytes et BLAKE3), rejet variante inconnue (400), 404 parent manquant, self-heal après suppression partielle, GET par hash de variante. Remplacement des payloads fictifs par JPEG synthétiques.
Documentation
CLAUDE.md
Mise à jour de la section "Artwork cache" : génération idempotente de variantes (thumb/preview), endpoints /api/v1/artwork/{hash}/{variant}, comportement de cache/ETag, et persistance CASCADE.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • InstaZDLL/waveflow-server#28: Introduit la base du cache artwork (metadata_artwork, stockage/handlers POST/GET) sur laquelle ce PR étend le support de variantes et le pipeline.

Poem

🎨 Deux tailles naissent à l'upload,
Thumb et preview sans sur-éclat,
JPEG q85 et hash en lot,
Idempotence garde le pas,
Les variantes servent, tout droit.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Le titre décrit précisément la contribution principale : ajout d'un pipeline synchrone de redimensionnement et d'endpoints pour les variantes d'artwork, avec la référence à la phase du projet.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1-h-3-artwork-resize

Comment @coderabbitai help to get the list of available commands and usage tips.

@InstaZDLL InstaZDLL self-assigned this Jun 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/artwork.rs (1)

218-231: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Gérer les échecs partiels de variantes pour éviter un cache “bloqué” incomplet.

Si une écriture échoue après insertion du parent (Line 275) mais avant la fin de toutes les variantes (Line 279+), un retry tombe sur le fast-path (Line 218) et renvoie un set partiel sans réparation. Résultat: état incohérent durable pour ce hash.

Recommandation: rendre l’écriture DB atomique (transaction parent + variantes) et/ou ajouter une logique de backfill quand fetch_variants_for_parent ne retourne pas l’ensemble attendu.

As per coding guidelines: Artwork POST /api/v1/artwork doit rester idempotent et réutiliser un état stocké cohérent pour les réuploads.

Also applies to: 279-300

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/artwork.rs` around lines 218 - 231, The code can return a partial
variant set when a parent row is inserted but some variant writes fail; fix by
making the DB write atomic or by backfilling on read: wrap the parent+all
variant inserts in a single transaction (the upload handler that currently
writes parent then variants) so either all variants are persisted or none,
and/or change the read fast-path (the code calling
crate::db::artwork::fetch_meta and
crate::db::artwork::fetch_variants_for_parent) to detect a missing/incomplete
variant set and trigger a repair/backfill (re-run the failed variant inserts or
roll back the parent) before returning the stored row. Reference the upload POST
handler and the DB operations used here (fetch_meta, fetch_variants_for_parent
and the parent/variant insert routines) and ensure idempotency and consistent
stored state for reuploads.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/api/artwork.rs`:
- Around line 283-320: La réponse inclut les variantes dans l'ordre du pipeline
(non déterministe) — stabilise l'ordre en triant les variantes avant d'itérer :
trier le tableau `variants` (ou une copie) par `variant.kind.as_str()` en ordre
alphabétique puis effectuer le stockage, l'insertion DB
(`crate::db::artwork::insert_variant_if_absent`) et la construction de
`VariantResponse`; alternative acceptable : construire `variant_responses` puis
trier `variant_responses` par son champ `variant` avant de l'inclure dans
`UploadResponse` pour garantir un ordre stable (`preview` avant `thumb`).

---

Outside diff comments:
In `@src/api/artwork.rs`:
- Around line 218-231: The code can return a partial variant set when a parent
row is inserted but some variant writes fail; fix by making the DB write atomic
or by backfilling on read: wrap the parent+all variant inserts in a single
transaction (the upload handler that currently writes parent then variants) so
either all variants are persisted or none, and/or change the read fast-path (the
code calling crate::db::artwork::fetch_meta and
crate::db::artwork::fetch_variants_for_parent) to detect a missing/incomplete
variant set and trigger a repair/backfill (re-run the failed variant inserts or
roll back the parent) before returning the stored row. Reference the upload POST
handler and the DB operations used here (fetch_meta, fetch_variants_for_parent
and the parent/variant insert routines) and ensure idempotency and consistent
stored state for reuploads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3c10b994-8c77-437d-a80e-e026c110ceb7

📥 Commits

Reviewing files that changed from the base of the PR and between 592a399 and 33cef7b.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock, !*.lock
📒 Files selected for processing (8)
  • CLAUDE.md
  • Cargo.toml
  • migrations/20260607000000_metadata_artwork_variant.sql
  • src/api/artwork.rs
  • src/artwork_pipeline.rs
  • src/db.rs
  • src/lib.rs
  • tests/artwork.rs

Comment thread src/api/artwork.rs
…ache

Two issues raised by CodeRabbit + the CI failure on the alphabetical
ordering assertion:

1. The upload handler returned variants in the pipeline's iteration
   order, which happened to match the alphabetical assertion locally
   but not on the CI runner. Sort `variants_response` by
   `variant.cmp(&other.variant)` on both code paths (fresh upload
   AND idempotent re-upload) so the wire order is stable and clients
   can pin `variants[0]` without reading the source of truth.

2. The idempotent fast path returned whatever was in
   `metadata_artwork_variant`, even if a previous upload had landed
   the parent row + parent bytes but lost a variant write to a
   backend / DB hiccup. The cache would then advertise a partial
   set forever. Added a self-heal pass: when the variant count is
   below `EXPECTED_VARIANT_COUNT`, re-run the pipeline against the
   freshly received body and write only the missing variants. The
   inserts go through `ON CONFLICT (parent_hash, variant) DO NOTHING`
   so a concurrent repair collapses cleanly, and the response
   re-fetches the post-repair set so it reflects the canonical row
   state.

Test coverage: `re_upload_self_heals_a_missing_variant` deletes one
variant row out from under the cache, re-uploads the same bytes,
and asserts the response carries both variants AND the table is
back to the full set. The two CI-failing assertions
(`upload_then_public_get_round_trips`,
`second_upload_of_same_bytes_is_idempotent`) now hold because the
handler sorts before responding.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai deux corrections en un round :

1. Wire order stable (issue + CI fail) — l'assertion alphabetical sur variants[0] == "preview" passait localement (la pipeline itère thumb→preview et axum/Postgres ne réordonnent pas) mais a fail sur CI Ubuntu. Sorting déterministe ajouté sur les deux paths (fresh upload + idempotent re-upload), juste avant la réponse. fetch_variants_for_parent SQL fait déjà ORDER BY variant ASC, mais le repair pass peut ajouter des rows en cours de route — sort en aval garantit le contrat.

2. Self-heal sur idempotent path (outside-diff) — addressed. Quand fetch_variants_for_parent retourne moins que EXPECTED_VARIANT_COUNT, le handler re-run la pipeline contre le body fresh et insère uniquement les variantes manquantes (via ON CONFLICT (parent_hash, variant) DO NOTHING → safe en race avec d'autres repairs concurrents). La réponse re-fetch le set post-repair pour refléter l'état canonique. Pas de transaction atomique end-to-end (l'object_store n'est pas dans la tx PG), mais la self-heal sur read garantit qu'un cache partiel se répare au prochain upload.

Test re_upload_self_heals_a_missing_variant ajouté : DELETE manuel d'une variant row → re-upload même bytes → assert 2 variants dans response + 2 rows en DB.

Push: d8534d6.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the current state of the artwork handler and the test file on the PR branch
echo "=== src/api/artwork.rs ==="
cat src/api/artwork.rs

echo ""
echo "=== tests/artwork.rs (new test) ==="
grep -n "self_heal\|EXPECTED_VARIANT_COUNT\|sort\|repair\|re_upload\|fetch_variants_for_parent" tests/artwork.rs | head -80

echo ""
echo "=== src/artwork_pipeline.rs (EXPECTED_VARIANT_COUNT) ==="
grep -n "EXPECTED_VARIANT_COUNT\|pub const\|VariantKind\|fn generate_variants" src/artwork_pipeline.rs | head -30

Length of output: 29275



@InstaZDLL Les deux fixes sont corrects — code vérifié.

Fix 1 — Sort stable ✅
Le .sort_by(|a, b| a.variant.cmp(&b.variant)) est bien appliqué sur les deux chemins :

  • Fresh upload : après la boucle for variant in variants
  • Idempotent re-upload : après le into_iter().map(...) sur existing_variants, ce qui couvre à la fois le cas sans repair et le cas post-repair (puisque existing_variants est réassigné avant le map)

Fix 2 — Self-heal ✅
La logique dans repair_missing_variants est propre :

  • L'existing_names HashSet évite de réécrire les variantes déjà présentes
  • L'ordering bytes-first → DB-row est maintenu dans le repair path (cohérent avec le fresh upload)
  • ON CONFLICT (parent_hash, variant) DO NOTHING → deux repairs concurrents collapsent sans erreur
  • Re-fetch post-repair via fetch_variants_for_parent → la réponse reflète l'état canonique DB, pas l'état in-flight
  • Pas de fallback silencieux sur set partiel (les erreurs remontent) — le contrat UploadResponse.variants reste honnête

Un point à surveiller pour la suite :
EXPECTED_VARIANT_COUNT = 2 est une constante manuelle qui doit rester en sync avec VariantKind dans artwork_pipeline.rs. Le commentaire documente bien l'intention ("bumped in lockstep"), mais si un troisième bucket est ajouté un jour et que la constante est oubliée, le self-heal ne se déclencherait pas. Rien de bloquant maintenant — juste à garder en tête pour 1.i.


🧠 Learnings used
Learnt from: InstaZDLL
Repo: InstaZDLL/waveflow-server PR: 16
File: src/db.rs:144-146
Timestamp: 2026-05-31T00:40:18.480Z
Learning: Dans `src/db.rs` (waveflow-server, Rust/sqlx/Postgres), la fonction `find_or_provision_by_external_id` utilise un pattern "read-first, write-on-miss" plutôt qu'un CTE `INSERT ... ON CONFLICT DO NOTHING RETURNING id UNION ALL SELECT id`. Raison : sous READ COMMITTED, les deux branches d'un CTE partagent le même snapshot statement — si une tx concurrente insère la même ligne entre l'acquisition du snapshot et notre INSERT, DO NOTHING se déclenche sans RETURNING, et le SELECT du UNION ALL ne voit pas encore la row commitée → 0 lignes → erreur 500. Le pattern retenu (SELECT optionnel → return si trouvé; sinon INSERT ON CONFLICT DO UPDATE SET external_id = EXCLUDED.external_id RETURNING id) évite ce cas en garantissant que RETURNING se déclenche toujours sur le miss path, même en cas de race concurrente. Référence : Erwin Brandstetter sur la race condition du pattern CTE UPSERT.

Learnt from: CR
Repo: InstaZDLL/waveflow-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-06T18:20:35.595Z
Learning: Applies to src/db.rs : Store artwork metadata in `metadata_artwork` table using `INSERT … ON CONFLICT (hash) DO NOTHING` for race-safe concurrent uploads

Learnt from: CR
Repo: InstaZDLL/waveflow-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-06T18:20:35.595Z
Learning: Applies to src/api/**/*.rs : Artwork `POST /api/v1/artwork` must be idempotent; re-upload of same bytes must skip storage write and echo stored row's `mime` + `byte_size`

Learnt from: CR
Repo: InstaZDLL/waveflow-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-06T18:20:35.595Z
Learning: Applies to src/api/**/*.rs : Artwork upload endpoint `POST /api/v1/artwork` must enforce a 4 MiB cap in the handler plus matching `DefaultBodyLimit::max(MAX_UPLOAD_BYTES + 1024)`

Learnt from: CR
Repo: InstaZDLL/waveflow-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-06T18:20:35.595Z
Learning: Applies to src/api/**/*.rs : Artwork `GET /api/v1/artwork/{hash}` must be public and serve with `Cache-Control: public, max-age=31536000, immutable` + ETag = `"<hash>"`

@InstaZDLL
InstaZDLL merged commit f177ab1 into main Jun 6, 2026
7 of 8 checks passed
@InstaZDLL
InstaZDLL deleted the feat/1-h-3-artwork-resize branch June 6, 2026 18:52
InstaZDLL added a commit that referenced this pull request Jun 6, 2026
CodeRabbit flagged a `usize → i64` cast in run_once that could wrap
for a hostile `WAVEFLOW_ARTWORK_SCANNER_BATCH_SIZE` value above
`i64::MAX`. The hot path passes the value straight to a SQL `LIMIT`
binding (sqlx serialises as i64), so a wrap would either error
inside Postgres or skew the query semantics.

Clamp at boot via `.min(i64::MAX as usize)` so the hot loop stays a
plain `as i64` cast. Defaulted value (50) is unaffected; only a
deliberately-set astronomical env value triggers the clamp.

Skipping the second finding (rename migration to today's date,
2026-06-06): PR #30 already merged `20260607000000_metadata_artwork_variant.sql`,
so renaming this file's prefix to `20260606xxxxxx` would put it
BEFORE the previously-applied migration in sqlx's ordering. The
"future date" pattern is already established in the repo (the
20260607 itself); enforcing today's wall-clock here would only be
cosmetic and re-checksum a row that nobody's applied yet anyway.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
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.

1 participant