GHSA-cg2g-xh64-v4q7 (critical, a SAFE_FOR_TEMPLATES bypass) and GHSA-mrv2-3pqr-j5jr (high, an IN_PLACE hook that removes a parent) cover dompurify up to 3.4.16, which jspdf and jspdf-autotable pull in. jspdf 4.2.1 is the newest release and still allows that range, so the existing override moves its floor. The doctor-report PDF renders as before (unit and integration suites).
sourcesContaining in the guard helpers and the DeferredRows type re-export from full-backup-payload had no importer, which the dead-code gate reports. isDeferredRows stays re-exported; the backup job's test mocks it there.
Opening the conversations beside a docked day dropped focus to the page
in most runs. A day opened on load asks for its heading to take focus;
when the heading had not mounted yet the request waited, and it was
honoured whenever the heading appeared, even after the person had moved
on and opened the conversations. Focus jumped into the folding day and
fell to the page as it went inert. A waiting request is now honoured
only while focus is still where it was asked from, never inside an inert
panel, and it ends with the day.
The timeline range check measured the popover during its slide-in and
read it up to 8 px too high; it now waits for the entry animation.
The label-in-name fixes move their name composition into small helpers
(score ring link, IP copy button, sortable header, period marker, the
day's title focus), so every function ends at or below its old length
and the size baselines only fall.
axe 4.14 checks WCAG 2.5.3 (label in name), and the app frame failed it
on every page: the account link, the user menu and the day strip carried
an aria-label that left out their visible text. The same class sat in
the coverage meter, the score ring, medication links, instrument cards,
filter pills with a value set, cycle calendar days (named by an ISO
key), admin user and login rows, a few sort and action buttons, and the
French, Spanish, Italian and Polish weekday chips, whose abbreviations
were not part of the day names.
Controls with visible text are now named by it, with any extra purpose
added as screen-reader text; the select pill and calendar days spell
out a name that contains what they show. The day and conversation
strips keep their action as the tooltip.
A disaster-recovery restore wrote these rows stamped with the restore's own
time: the medication's updatedAt (moved again by the course window), the
category assignment, custom category labels, courses and composition, the
mood day context and tag links, illness and cycle symptom links, nutrient
days, and a flare's updatedAt after the parent pointer pass. The recovery
file now carries each stamp (additive fields: linkCreatedAt on a mood
entry, symptomLinkCreatedAt on a cycle day log, createdAt on an illness
symptom, categoryUpdatedAt on a medication, updatedAt where missing) and
the restore writes it back. A file without them restores as before.
A custom category label, a course note and a marker's context rode the
recovery file as readable text and were sealed again under the active key.
They now ride as ciphertext (labelEncrypted, noteEncrypted,
contextEncrypted) and come back as the same bytes; a file that carries the
readable value instead is sealed on the way in, as before. The portable
file keeps the readable values. The rotation script and the
encrypted-column writer guard already cover all three columns.
The parity golden copies change for exactly this: the new fields in the
recovery file, the restored stamps in place of <now>, and the three values
coming back under the key they were sealed with. The legacy readable coach
title and custom metric note are still sealed on restore, since the row
holds no ciphertext to carry. The round-trip column audit no longer
excludes the four link stamps.
Every instrument now runs the same wizard: the form's own question stem
above each item (the PHQ-9 and GAD-7 stems were missing), the item's
exact answer options as one radio group in source order, progress, Back,
number-key answers, and a review step that lists every answer, lets any
of them be changed and is the only step that submits. The result leads
with the crisis card when it applies, then shows the score on the
instrument's scale, the band, what the band means, the change since the
last check-in against the meaningful-change threshold, and the next
step. Scoring is untouched.
The wording was read against the published versions. German follows the
PHQ-D and Löwe's GAD-7 verbatim, including the follow-up question in its
published formal address. Spanish, Italian and Polish carry the official
PHQ-9 and GAD-7 translations, French the official GAD-7, and the GAD-7
gets its own answer set because the Italian forms word it differently.
Where a version could not be matched to a published form (the French
PHQ-9, every Korean form) the language now shows the English original.
Each locale list names its sources, and spot checks pin the corrected
wording.
The windowed reader is async, so a Promise.all that listed it first held a rejected promise of it whenever a later element threw while the array was built, and nobody handled that rejection. It now comes last in each of those arrays.
The backup payload schema the admin restore routes publish now declares notesEncrypted and deletedAt on a family-history entry, which the disaster-recovery file carries since the previous change.
The ratchet on tests that read a source file by path matched readFileSync
and readFile only, so a guard that called readSourceText with a fixed path,
directly or through a local read(rel) wrapper, read the file without the
anchors readSource demands and was never counted. The classifier now counts
both shapes, and leaves alone a readSource call with anchors and a sweep that
feeds the reader from a walk.
The twelve guards that read a fixed file that way now go through readSource
with an anchor each read asserts on, so the baseline stays where it was.
The floors step piped scripts/mutation-floors.mjs into tee without
pipefail, so it took tee's exit status and a module under its floor could
never fail the run. It now runs under bash with pipefail, as do the other
steps that pipe into tee, and a guard reads every workflow and fails on a
tee pipe without it. The other piped steps were checked: each either
validates what the pipe produced or pipes a command that cannot fail.
The nightly runs set FC_NUM_RUNS, an absolute count that replaced every
property's own: the database backup round trip, which states twelve cases
for its cost, ran a hundred times against a timeout sized for twelve.
FC_RUNS_FACTOR now multiplies each property's count (five for the unit
suite, four for integration), propertyTimeout grows a test's timeout by
the same factor, and the backup round trip uses it.
The unchanged-stats shortcut skips the write when every overwritten column matches the stored row. The kit test covered a new value and a new device; it now also re-posts a step total that only names a new HealthKit source version and a sleep row that only changes its stage, and checks each is written with its syncVersion bumped and the tail started.
Every record with a tombstone (allergies, labs, conditions, vaccinations,
life events, measurements and the rest) rides the disaster-recovery file
deleted rows included, so a restore gives back the account as it was. The
family history read only live entries for both files, so a restore lost
its deletions, and it carried the note as readable text that the restore
sealed again under the active key.
The disaster-recovery slice now carries every entry with its deletion and
the note as the ciphertext at rest, and the restore writes both back
verbatim; the portable file keeps the readable live entries. The parity
golden copies change for exactly that: the deleted entry, its note's key
and the shifted ids after it.
The parity harness normalised any timestamp written during the run to
<now>, and the seeded rows' createdAt and updatedAt were themselves written
during the run, so a restore that dropped a row's createdAt still matched
its golden copy. Every run-time timestamp of the seeded account is now
pinned to a fixed historical value, and the seeded snapshot is asserted to
hold none, so a lost createdAt shows as <now> against a historical value.
The account also carries a tombstone of every model that keeps them, a
flare under its condition and a superseded profile fact, so the second
restore pass resolves real pointers. The fixture is sealed under the
legacy key and a second key is active from the export on; sealed values
render as the key id they name and the text they open to, so a value the
restore re-encrypts instead of carrying over differs.
The golden copies are written from the backup code as it stood before the
export and restore moved into per-domain modules (64fa65b46), and the
current code matches them.
A web-only process opens its queue connection without migrating and refuses a job-queue schema older than its own, so in a web/worker split the new worker has to start, and upgrade the schema from 45 to 46, before the new web containers. The v1.45.0 notes say so, name the one additive migration, state that a single-container install needs no action, and point a forked compose file at the new stop grace period.
The server reads messages/<locale>.json from disk on first use. A missing
or corrupt file threw, and since the matchers iterate every locale, one bad
non-English catalog turned every request that touched them into a 500.
That locale is now served the English catalog, and the failure is logged
once as i18n.catalog.fallback with the locale and the reason. English is
the fallback itself: a process whose messages/en.json is missing or does
not parse refuses to start, with the file and the reason in the message.
The image build checks the catalogs twice with
scripts/verify-message-catalogs.mjs: in the builder against the locale
list in the source, and in the runner stage against the copies the image
carries.
Closed analytics blocks survive a write that says it reached no further
back, so a writer that never invalidated left them on their pre-write
values. The Apple Health and Health Connect imports, the CSV and JSON
imports, the score engines, the WHOOP webhook, the Withings activity, sleep
and ECG legs, the Oura history backfill and the sleep-segment sweep wrote
measurements without touching the read caches; a long-history import
showed the old all-time figures until the entries aged out.
The shared post-mutation tail now marks the caches stale back to the
oldest row it is handed, and the Withings and Oura legs that hand-rolled
the same tail run through it. The two export imports share one tail that
refolds the rollups, marks the caches stale back to the oldest sample
written (all of them when none was) and re-warms the status assessments.
A sweep that tombstones a sleep segment drops every cached block.
The wizard drops its slug alias to stay inside its function-size
baseline, the bare-t() sweep reads the tree through readSourceFolder,
and the version-stamp baseline takes the lower counts the dose-screen
and questionnaire changes left.
With an analyte, the Coach's labs block read the newest 4,000 lab rows of
every marker and matched the asked-for one in memory afterwards. An account
with that many newer readings of other markers lost the marker's older
readings, so its count and first date named a shorter history than the
record holds, or the marker vanished. The match now runs in SQL before the
row cap, on the biomarker's name for a linked row and the row's own name
otherwise, and the history compares units with sameLabUnit, so a reading
stored as "MG/DL" beside "mg/dL" stays in the series.
The no-limit option promised every lab result, and the German detail
sentence ended on a dangling clause. The Coach keeps the latest value
of the most recently measured markers and, for a marker asked about,
its five previous results with older ones summarised per year for ten
years; years before that are only counted. Every language now says
that in complete sentences, with the two numbers pinned to the lab
history constants.
The cross-source picker anchors each duplicate cluster on its first start,
so which cluster a workout lands in depends on the workouts around it. The
detail route still read the old fixed five-minute clock slot, and every
other reader handed the picker only the rows of its own range. The detail
of a twin that straddled a slot boundary declared itself canonical while
the list hid it, and runs at 09:58, 10:02 and 10:06 were grouped one way by
a read starting at 09:55 and another by a read starting at 10:00.
Every reader that decides which workout is canonical (list, detail and its
previous session, activity calendar, day view, doctor report, Coach,
briefing and correlation blocks, strain, per-sport context, the activity
insight's history, the batch write) now reads through one windowed reader.
It reads a duplicate window past the end of the range and reaches back
until every sport in the range starts at a workout that is an anchor
whatever came before it, then picks over everything read and keeps the
rows that start inside the range. The per-sport baseline and the insight
history leave out the whole cluster of the current session instead of the
row alone, which left its twin behind as a session of its own. The batch
write clusters its rows together with the stored workouts around them and
drops only a row that loses to another row of the same batch.
A property test checks the windowed read against a whole-history read for
random dense histories and ranges; route tests pin list, detail, deep link,
previous session and the batch write against the database.
The outbound screen let a halving with no number through in every
language but Dutch ("Reduce your dose by half.", "Reduza a dose pela
metade.", "Tome meia dose."), let a bare change imperative through
("Zmniejsz dawkę.", "복용량을 늘리세요.") and did not know the Polish
spelling of the generic stems, so "Odstaw ramipryl." passed. Each
language's bank now carries a halving and fraction shape and a bare
change shape on a medication noun, and the stems include the Polish
-pryl, -statyn and -glutyd.
The continuation exemption trusted "as before" on any surface that
passed no schedule, so "Take 40 mg as before." went out to people who
had never taken 40 mg. The screen now takes the dose plan (each active
medication with its dose) instead of a bare list of numbers, and
exempts a stated amount only when it is the planned dose of the
medication the sentence names. Without a plan no stated amount is
exempt.
The support report's host-name step only knew a short list of top-level
domains, so saude.minhacasa.br:3000 or vida.example.com.br reached a
report meant for a public issue unredacted, as did hosts under .pt, .be,
.lu, .li, the Nordic codes and the English-speaking ccTLDs. The list now
covers the country codes of every app language's home regions, pinned
by a test against the shipped locales.
The app ships one Portuguese bundle, and pt-PT, pt-AO and bare pt all
fold into pt-BR, so a stored pt-BR does not mean Brazil. A positive
PHQ-9 item 9 still answered it with SAMU 192 as the emergency number
and CVV 188 as the only line, neither of which connects from Portugal,
under a comment claiming pt-PT kept the international set.
With no region signal, Portuguese now gets the DE_AT_CH treatment: 112
first, the worldwide directory, then CVV and SAMU labelled Brazil and
SNS 24 (808 24 24 24, option 4) and SOS Voz Amiga labelled Portugal.
An explicit pt-PT tag gets the Portugal set and br the Brazil set.
The PHQ-9, GAD-7 and WHO-5 are not validated in Dutch or Brazilian
Portuguese, and the SCI is validated in English only, yet the check-in
wizard and the answer breakdown read the item text, answer options and
stems through t() in the reader's language. Dutch and Brazilian
Portuguese readers saw unvalidated translations of every item,
including PHQ-9 item 9, under a note saying the questions were in
English.
Every surface now resolves instrument wording through instrumentText,
which returns the English source for a language outside the
instrument's validated set. The unvalidated wording is removed from
the bundles; the locale-parity check accepts exactly those keys as
absent and fails when a bundle carries one, and a structural test
keeps item text out of bare t() calls. The PHQ-9 follow-up question
regains its original wording. CONTRIBUTING states the rule for
translators.
The check sampled getAnimations() once after the sheet showed, which passes or fails on whether WebKit drew a frame in between. It now reads the sheet and backdrop the moment they are inserted, the worst case, and is deterministic: it fails every time against the old classes. The other measurements in the spec already wait on polls, rendered frames or layout.
data-[state=open]:animate-in carries an attribute selector and outranked motion-reduce:animate-none, so the slide stayed, only cut to 0.01 ms by the global reduced-motion rule. It still existed and ran until the next drawn frame. The none is important now, on the sheet and the backdrop.
Every raised entry is main's own number for a file this branch had not touched since the fork, so no value is above both sides; the version-stamp counts that fell are recorded lower.
next stays on main's 16.4.2 (the RCE and Server Actions advisories) with its two companions; release's newer dev tools and its own additions are kept. The lockfile is release's, re-resolved. pt-BR, which main does not have, takes the one key main added.
JavaScript's \b is an ASCII word boundary, so next to an accented
letter it never matches. That left patterns, or alternatives of them,
dead: the French "en bonne santé", "à cause de", "causé" and
"continuez à", "probabilité" and "probabilità" in the French and
Italian risk banks, the Dutch "één procent", the German "Ärzte", and
the Polish "przedział", "średnia" and the verb endings in "-ą". A \w*
stem cannot run through "ł" either, so "doprowadziło do" missed its
causal pattern, and the Polish adult pattern was spelled "dorosli"
without the "ś".
Those boundaries now use lookarounds that count the Latin letters
with diacritics as word letters (WORD_START / WORD_END in the shared
screen module, the same class inline in the regex literals), the
Polish stems take accented letters, and "dorośli" is spelled both ways.
Two guards keep the class out: every pattern of every locale's screen
and population-norm bank has to match at least one realistic sentence,
so a dead pattern fails the suite, and no pattern may put \b next to
an accented letter. Both fail on the old patterns.
WebKit in the CI container can go over a second without rendering a frame while events and timers run on; the trace shows the More sheet still sliding in after its animation reported finished. The sheet paints once per frame, so a drag released inside such a stall was never drawn. The spring-back check now holds the finger for two rendered frames, and the settle helper waits for two frames too.
The non-passive touchmove listeners on the sheet and its backdrop were bound in an effect that ran while the portaled sheet was still unmounted, and never ran again, so they were never attached. The mounted element is now state, and the listeners follow it.
Undoes the raise from the pt-BR tables commit. After the screen banks,
population-norm patterns, insights footers, settings labels and
Telegram words moved into their own modules, every file that grew is
below its old line count, outbound-screen.ts leaves the over-800 list
and buildDoseChangeClass the over-200 list.
outbound-screen.ts held every locale's dose, risk, causal, continuation
and change-stem patterns plus the shared units and slot builder, and
grew with each language. The banks now live in
outbound-screen/locales/<locale>.ts, one per language, each exporting
the same LocaleBank shape; the units, target-dose shape, object slot
and generic-name stems are in outbound-screen/shared.ts. The screen
composes them per locale and keeps the matching logic, the
continuation rule and the general rationale.
No pattern changed: compiling the old and the new module and comparing
every bank of every locale, with and without a medication-name list,
gives the same regular expressions (108 of 108 identical), and the
screen tests still block and pass the same sentences in every locale.
The screen is 457 lines instead of 1,597, and buildDoseChangeClass is
a four-line composition.
Installing Chromium's system packages from apt stalled a shard for 29
minutes, the same way WebKit's did. The shards now use the image the
WebKit job already runs in, whose tag follows the lockfile's
playwright-core, so no browser or system package is installed at run
time.
The reference-sentence exemption's patterns move out of
coach-prose-grounding.ts into population-norms/<locale>.ts, composed
into the same Record the check already reads. The patterns themselves
are unchanged. A test feeds every locale one reference sentence the
English bank cannot match, so a module that is not wired fails.
The per-locale footer table is prompt text, not prompt logic; it moves beside the generator, which drops back under its size baseline after pt-BR joined the table.
The Coach metric and cluster options and the label keys of the managed-record settings forms are data; they now sit beside the forms instead of inside the component file, which drops back under its size baseline after the locale picker started reading the locale list.
The two word lists lived inside the webhook handler and had no entry
for Dutch or Korean. They move to their own module, keyed by locale so
the compiler names a language that has none, and gain "hulp", "hoi",
"goedemorgen", "도움말", "도움", "안녕" and "안녕하세요" beside the
Portuguese words. The bot still matches the union on the folded text.
A Brazilian Portuguese reader who answered PHQ-9 item 9 saw the
international set: 112 and two directories. They now see CVV (Centro de
Valorização da Vida), 188 by phone and the chat at cvv.org.br, free and
answering around the clock, with SAMU's 192 as the emergency number.
Both were checked against cvv.org.br and the Ministry of Health's SAMU
page. A bare "pt" or "pt-PT" keeps the international set, because 188
does not connect from Portugal.
The CVV label is translated in every bundle, and a test pins that every
resource of every locale's set resolves its label in that locale.
Add an inlang project so Fink opens the repository directly:
English as the source, the nine shipped locales, and the
message-format plugin pinned to 4.5.5 reading messages/{locale}.json.
That plugin keeps the nested keys and {placeholder} tokens as they
are; the i18next and next-intl plugins Fink also supports either
rewrite numeric-keyed objects as arrays or misread quotes around a
placeholder as ICU escapes.
Checked against these files with the inlang CLI and SDK: all nine
locales load with no errors and no findings, a load and save without
edits leaves every file byte-identical, and an edit changes exactly
the edited lines. inlang's working files beside settings.json ignore
themselves in git and are kept out of the format check.
CONTRIBUTING gains the browser workflow (fork, push, pull request),
what a reviewer checks, and that machine translation is a draft a
reader of the language goes through before the pull request; the
new-locale steps now name the real locale list. A test keeps the
project's locales equal to the shipped ones and the plugin pinned.
Korean has no singular plural category, so the two ...One strings are
never chosen at run time, but they carried {count} where English writes
the number. They now read "1일 후" and "검사 수치 1개", which leaves no
placeholder that English lacks.
Since the guards read the source tree through the shared index, 35 of
them no longer read a source file by path, and the realism ratchet
failed until the lower numbers were recorded.
The outbound screen, the prose-grounding check and the insights and
Coach prompt files keep one table per locale on purpose: the dual-bank
rule (the reader's bank plus English) reads in one place, and every
other locale lives there. A ninth locale adds its rows, mostly to the
dose, risk and causal banks of outbound-screen.ts and its
buildDoseChangeClass table (+134 and +40 lines). The managed-profile
settings section and the Telegram handler grow by an import and the
Portuguese bare words.