Releases
wardrowbe-v1.11.0
Compare
Sorry, something went wrong.
No results found
1.11.0 (2026-10-11)
✨ Features
auth: add support for forward auth (#242 ) (ef44c96 )
auth: let a verified sign-in reclaim an email held by an unverified account (42f59a6 )
🐛 Bug Fixes
address review feedbacks and follow ups (#244 ) (52d2d7c )
ai: treat an empty vision completion as a failure (#257 ) (8dd1b67 )
analytics: show a 0% acceptance rate and 0 rating instead of no data (4d81f67 )
api: treat a zero coordinate or rate as a value instead of missing (c3dce51 )
auth: accept the string "true" for email_verified, as Apple sends it (b6896e4 )
auth: fit IdP names and avatar URLs to their columns and fill a blank name from the token email (6183d21 )
auth: flatten control characters in the IdP display name instead of refusing sign-in (d431c96 )
auth: lock both rows in id order so two accounts swapping emails cannot deadlock (656823f )
auth: make a concurrent first sign-in idempotent (700aa4c )
auth: make a concurrent first sign-in idempotent (3eecd8b )
auth: never adopt an account whose email the identity source did not verify (2cd630a )
auth: only trust email_verified when the id_token carries the same email (f98226f )
auth: refuse email adoption for unverified sign-ins inside the user service (2723716 )
auth: retry lost sign-in races against committed rows instead of returning 500 (cc8f966 )
auth: retry when a twin sync already moved this account to the new email (7737075 )
auth: return 409 instead of 500 when a concurrent sync claims the same email (e3f9276 )
auth: Support Identity per provider (#255 ) (5f37c3b )
auth: treat a deactivated account as anonymous on optional-auth routes (e48f8ca )
auth: treat a deactivated account as anonymous on optional-auth routes (07812e0 )
auth: treat a row with the caller's external_id as its own during a first sign-in race (c36c14c )
auth: validate the OIDC email claim with the shared email rule (b8d9d38 )
colors: collapse every Unicode whitespace run the same way in SQL, Python and the frontend (2bf4822 )
colors: derive every colour list and swatch from the vocabulary (b831459 )
colors: drop zero-width spaces, word joiners and BOMs so an invisible name reads as blank (e2f6154 )
colors: lowercase, trim and hyphenate colour names in the alias migrations (21e2b76 )
colors: normalise colour aliases in item filters and outfit palettes (90c23fa )
colors: normalise colour aliases on item and preference writes (bf99cf0 )
colors: read spaced colour names as their hyphenated vocabulary colour (d906d29 )
colors: remap colour names stored in learned profiles (0972b76 )
colors: remap stored frontend-only colour names (252b3ee )
compose: pass the same backend env to every worker (1d7f8fb )
db: commit the request session before the response is sent (4be2264 )
docs: update repository clone URL in README (626ae50 )
email: accept any RFC 5322 atext bare login as the sender (f1d34f0 )
email: collapse line breaks in the subject and sender name so legacy family names still send (64bf966 )
email: encode a non-ASCII sender name apart from the address in From (bb3c873 )
email: fail once on an unusable sender or a permanent SMTP refusal instead of retrying (1823e9e )
email: log an unvalidated sender once with the validator's reason (9a432f0 )
email: send a quoted sender local part without quoting it twice (ff9daef )
email: send from a sender the header parser cannot read instead of failing every retry (b0a340b )
email: send from the IDNA form of the sender and name the address that needs SMTPUTF8 (54a6007 )
email: send to the IDNA form of a domain and name the SMTPUTF8 gap for non-ASCII local parts (15d5a39 )
email: stop retrying a send the mail server refuses for lack of SMTPUTF8 (23da5f9 )
email: warn when SMTP_FROM_EMAIL looks like an address but is not one (5d49927 )
families: check invite addresses with the sign-in email rule before saving the invite (03fd037 )
families: escape the inviter and family names in the invite email (5911dfb )
families: require a verified email to accept an email-bound invite (6e06243 )
families: tell the inviter when the invite email did not go out (dd18c38 )
family: find the current member by user id so a detached admin keeps admin controls (213e142 )
family: wait for the current member before listing others or deciding admin controls (9971871 )
frontend: read today and weather shapes from one place (f2edcad )
history: show skipped outfits and filter every status (e5eb35d )
i18n: capitalise the Italian accepted status label (5855882 )
items: don't mark an item manually tagged when a PATCH sets only user-owned tag keys (3daf66f )
items: refresh outfit caches when an item's images change (75f304e )
learning: drop non-numeric learned colour scores at runtime and in the remap migration (603b026 )
learning: keep the readable fields of a learned pattern instead of dropping the entry (7944222 )
learning: read learned scores and patterns through accessors that skip malformed entries (cac6fda )
learning: report a 0% acceptance rate as the suggestion confidence instead of none (d774df5 )
learning: skip learned scores too large to read as a float (b6f309a )
lock tag merges and protect unmirrored keys (cd347ee )
mattermost: escape list markers after a bare carriage return (63cd3dd )
mattermost: send a linked attachment title unescaped since Mattermost shows it as plain text (1ffcb5b )
migrations: apply the colour rule in Python so unknown names lowercase as runtime writes do (5b6cd7c )
notifications: bind the sent status through the outfit status enum type (43ccd5e )
notifications: build links and SMTP config from Settings (970788e )
notifications: check scheduled users for channels through the dispatcher (1130f65 )
notifications: clear the earlier error when a retried notification is sent (bd18027 )
notifications: don't pre-fill an undeliverable .invalid address into an email channel (c06cf1c )
notifications: escape line-start markdown and the attachment title sent to Mattermost (abfdedc )
notifications: escape markdown and mentions in user text sent to Mattermost (758e429 )
notifications: flag a stored channel config that is not an object and keep it out of test errors (6a5cba6 )
notifications: flag stored channel settings this version rejects instead of failing silently (87585fb )
notifications: keep a verdict the user gives while the outfit notification is sending (88321d1 )
notifications: keep the outfit greeting, weather line and short push body (bbade4b )
notifications: keep the user's verdict when a retried outfit notification lands (2ba0159 )
notifications: label retried outfits Today or Tomorrow from the outfit date (1a15a11 )
notifications: mark the outfit sent when a retried notification goes out (88c2c1e )
notifications: name a past outfit's weekday instead of calling it today (387a486 )
notifications: record each failed laundry channel with its own error and retry on the next run (5ab5f80 )
notifications: record every outfit channel attempt through the laundry recorder (2612955 )
notifications: record the laundry channels that failed before a fallback succeeded (44f9a00 )
notifications: restore the structured outfit email and Mattermost formatting (0a78107 )
notifications: retry the first channel that failed transiently, not a rejected config (f9c4ec5 )
notifications: send laundry reminders through the same channel registry as the dispatcher (0bf2b4b )
notifications: show channels registered by the mobile app (1c0677d )
notifications: skip stored channels this version has no provider for (1de3965 )
notifications: stop retrying a notification whose retry fails for good (04ef598 )
notifications: title outfits by occasion and show whole degrees with the degree sign on every channel (0e23e90 )
notifications: treat a stored channel this version cannot send to as not found (4b9b09e )
notifications: use the singular for a one-item laundry reminder (f2cce86 )
occasions: give every occasion a formality range (5d8f0d9 )
occasions: validate suggestions and schedules against one occasion list (f4b3749 )
occasions: validate the default and wear-log occasions and read unlisted stored defaults as casual (e5f581c )
outfits: default wear dates to the user's local day (41ac74f )
outfits: one Outfit type shaped like the API response (b8f7d4a )
outfits: record mark-worn on the user's local day (cf9c2ab )
preserve user-set tags through updates and AI re-analysis (eeaba6b )
preserve user-set tags through updates and AI re-analysis (453d7ab )
rembg model cache (#258 ) (2202f56 )
services: rule drifts and bug fixes (1f6f6f4 )
settings: cap the location name at its column width and show one translated save error (55ed807 )
studio: record an undated mark-worn outfit on the user's today (dede24e )
tagging: replace logprobs_confidence on re-analysis instead of keeping the first run's score (a1c2bca )
timezone: fall back to UTC in the browser as the backend does (e84bb99 )
timezone: resolve every user timezone through one helper and validate new ones (fab5c59 )
uploads: enforce MAX_UPLOAD_SIZE_MB per file (14f8aaf )
uploads: name failed files and only offer retry for retryable ones (f093659 )
users: accept the same email addresses everywhere sign-in does (19dd7c3 )
users: bound profile updates to the column limits instead of failing the flush (3321158 )
users: count a name of combining marks alone as blank (66ce72c )
users: count interlinear annotation and Egyptian format controls as blank (d338d23 )
users: keep rejecting .invalid and .onion email domains (1b0e469 )
users: match blank names on Unicode's default ignorable ranges instead of every format character (0f5bcd7 )
users: refuse a body measurement too large to read as a float (4e79c23 )
users: refuse line breaks in the location name (1882226 )
users: refuse names that are only whitespace or zero-width characters (62f1c01 )
users: treat names made only of invisible characters as blank, also after the IdP cut (b18bed8 )
validation: refuse line breaks and control characters in family and display names (7ab28e3 )
wardrobe: count worn-ago days across daylight-saving changes (374c4cb )
wardrobe: paint item colour dots through the shared colour lookup (7763da4 )
workers: charge a retry attempt only for a failed send, not for a lock error (f99c8c0 )
workers: keep a failed rollback and a lock release error out of the wrong log line (49aa50d )
workers: roll back a failed notification retry so the rest of the batch still runs (8fad78a )
♻️ Refactoring
email: flatten header values with the shared control-character rule (84fb5e0 )
items: share the tag-mirrored column list between update and the tagging worker (e481fdd )
notifications: build the laundry reminder message in one function (3ec0c4e )
single source rules and remove duplicates (#243 ) (a19750a )
users: drop the unused UserService.create/update and their schemas (3f5ad4b )
📝 Documentation
auth: note how commit order settles a reclaim racing the holder and when retries run out (a76b83e )
notifications: state the retryable rule instead of listing its current cases (3287868 )
oidc: replace the dead OIDC_SKIP_SSL_VERIFY with OIDC_CA_BUNDLE (51f0f59 )
🔧 Maintenance
remove empty and never-imported files (ee26163 )
remove empty and never-imported files (e0884a3 )
🧪 Tests
auth: cover case and padding variants of a detached address at /auth/sync (4ac6026 )
auth: merge the two-session race helpers into one (24a7126 )
auth: parametrize the adopt/refuse race and read the race outcome after every commit (a7a67b3 )
auth: parametrize the OIDC sync tests behind one fixture and cover persisted email_verified (2d8671d )
auth: read the session and family of a detached account (2bd803b )
auth: split the adoption and changed-email tests into adopt, reclaim and refuse cases (66d85b2 )
auth: table the OIDC email claim cases and cover a punycode claim against a unicode body (b85df9f )
auth: wait for the second sync to block on the first before committing (b32e458 )
calendar: seed the profile so the today test cannot race a timeout (7b3dce3 )
colors: check each colour alias against the stored colours instead of a tautology (5a3fc03 )
db: assert auth and endpoint share the request session on a route that uses both (9b5151f )
families: assert the invite 403 details and that the email match is checked first (3230c83 )
history: assert literal status labels instead of reading the catalog (d45e4cd )
invite: cover the unverified-email and wrong-email join errors (8bc1874 )
logging: check each entrypoint's real app logger instead of a recorded call (444cd1c )
make the observer stubs constructible under vitest 4 (a217e0d )
notifications: check invite and notification email escaping in one table (16072a1 )
notifications: check trailing-slash links once across outfit, laundry and invite emails (be4e0bd )
notifications: compare outfit renders with literal expected output (7d1bd67 )
notifications: cover the first send in the verdict race test (bf8777d )
notifications: parametrize the single-channel laundry reminder tests (d05962b )
notifications: split the retry status cases out of the day label test (f8bed8a )
occasions: drop the schedule occasion tests the shared vocabulary tests already cover (6ad004f )
preferences: drop the unlisted default occasion test the shared occasion table covers (8b721f5 )
share one session_maker fixture and restore dependency overrides after real_get_db (092a7d4 )
timezone: cover UTC+13, Kathmandu and daylight-saving days in the user clock (3a33ceb )
timezone: parametrize the bad stored timezone reads and saves (c5e455e )
uploads: table the failure codes with their retryable flag (12ce25d )
workers: run the retry test through the real Redis lock and cover a missing user (cac432c )
You can’t perform that action at this time.