-
v1.0.0-Beta.16
Pre-releasereleased this
2026-09-02 17:51:38 +02:00 | 1 commits to main since this releaseSomewhere to set the space, and everyone moved to it
Added
- An administrator can change an account's space from its page. The endpoint
for it shipped in Beta.14 without its control, so the limit could be set over
the API and nowhere else — which is the same fault as code with no caller.
Changed
- The default space is a gigabyte, and accounts that already exist move with
it.DefaultQuotaBytesis only consulted when an account has no quota row,
and every account gets a row the first time anything is stored — so raising
the number in Beta.14 applied to nobody who already existed, and every
account went on reporting the 512 MB that was in force when its row appeared.
Migration 019 moves rows still sitting on the old default; only those, and it
is safe to assume they were never chosen because until now there was no
control anywhere for choosing one.
Fixed
- The administration screen showed a ceiling of zero for an account that had
stored nothing. It read the quota row directly, and an account gets that row
the first time something is stored — so a fresh account was shown as
unlimited while the person themselves was correctly told a gigabyte. Two
screens of the same server disagreeing, and an administrator would have acted
on the wrong one.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- An administrator can change an account's space from its page. The endpoint
-
v1.0.0-Beta.15
Pre-releasereleased this
2026-09-02 17:32:53 +02:00 | 4 commits to main since this releaseThe words your browser was never asked for again
Changed
-
The plates are a grid of weights rather than a row of pills. A chip is for
choosing one of a few things with names; this is a dozen numbers, most of them
on, and equal cells put them under one another where a rack can be checked
against them. -
The server's version tile says the version once. The line under it repeated
the whole string, so on a tagged build — which is every release — it said
v1.0.0-Beta.14 twice, once large and once small. -
The disk tile breaks its total down on two lines with an icon each, instead of
a sentence of small text under the figure. And photographs have their own icon
now: they were being drawn with the body figure, which is a person standing
next to a number of kilobytes.
Fixed
-
Strings added in any release appeared in English for anybody who had used the
application before it. The service worker caches assets first-hit and keys
them by URL, which is safe only because every asset carries the build stamp —
and four fetches did not: the interface locale, both catalogue locales and the
body figures. So a browser that had loadedes.jsononce kept that copy
through every update, andtfalls back to the bundled English master for a
key the cached file has not got.Nothing could see it. The audit reads the files on disk and they were
complete; the scan reads the source and every key existed; a fresh browser
profile has nothing cached and shows the right words. A test now refuses a
fetch of a static file without?v=. -
A plate typed by hand vanished when it was tapped. The grid shows the
catalogue plus whatever is owned, so a plate that was in neither list any more
had nowhere to be drawn, and the only way back was to type it again — a
catalogue plate taps off and on, a typed one had one life.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
-
v1.0.0-Beta.14
Pre-releasereleased this
2026-09-02 16:59:24 +02:00 | 8 commits to main since this releaseYour account, your space, and screens that say what they are
Added
-
A finished session opens with what it was: how long, how many sets, how much
was moved and how hard it felt. Every one of those numbers was already
recorded and none of them was shown — the screen was a drawing of food and
then a list. Under each exercise it now also says what the same exercise
looked like the time before, which is the difference between a record of what
you did and something worth reading. -
Weekly volume is no longer hidden from the interface a new account starts in.
The argument for hiding it was that a tonnage means nothing without a scale to
put it against, which is true of a figure and not of a line: whether it is
going up is exactly the question somebody new has. By movement pattern and Not
moving stay behind the switch, because those are coaching vocabulary. -
An administrator can set how much room each account has, and each account can
see what it is using: a bar with a part for photographs and a part for the
activity files a watch writes. Two parts because two things charge that
counter, and an architecture test now refuses a third without one being added
to the bar in the same change.Every account starts with two gigabytes rather than no ceiling, because a bar
with nothing to fill teaches nobody anything — and the reason to show it at
all is that finding the limit at the moment of hitting it is finding it too
late. Zero still means no ceiling, for an administrator who wants one person
unbounded. -
A plate the catalogue does not stock can be added by hand. Ticking from the
list covered almost every gym and bars could already be typed in, so plates
not being addable was an asymmetry with nothing behind it — and the
calculator and the warm-up ramp are only worth anything if they work from the
plates actually on the rack. -
A destination for notices can be corrected. Adding and deleting were the only
two operations, so fixing one character in an address meant rebuilding the
destination — which throws away the record of when it last worked, and that
record is how anybody diagnoses one that has quietly stopped. An empty secret
means "keep the one you have", since it is never sent back to the browser. -
A screen called You, holding the four things that are about the person rather
than the application: what you are called, how you prove it is you, who else
may look, and how you take it all with you. They were scattered — a row that
led nowhere, a section called Security three rows down, a whole navigation
entry holding one control, and a backup filed next to the bin. -
The local PIN can be switched on.
setPinandclearPinhave been complete
since the phase that wrote them — hashing, an attempt limit, an idle window —
and nothing had ever called either, so the lock screen worked perfectly and
could not be reached. Verified by driving it: set a PIN, push the idle stamp
back an hour, come back, and the application is behind the curtain. -
PATCH /api/me: somebody may now change their own display name and their own
password. Neither was possible without an administrator, which quietly worked
against the promise this project makes in writing — that an administrator
manages accounts and the server and never reads anybody's training. A design
where the only route to your own password runs through another person makes
that promise harder to keep than it needs to be.The two halves are not held to the same standard. A display name is cosmetic
and reversible, so a live session is enough. A password is the credential, and
changing one is exactly what somebody does with a stolen session, so it asks
for the current password first — the way turning off the second factor already
does. The username is deliberately not changeable: it is the handle the audit
log records, and a record of who did what is worth nothing if the name in it
can be edited afterwards.
Changed
-
Comparing two photographs is done by looking at them. The two were picked
from a segmented control with one pill per photograph, each carrying a date:
fine with four and a wall of identical grey dates with forty, and the wrong
question anyway — nobody remembers a physique by its date. It is two rows of
thumbnails that scroll sideways now, so forty take the same vertical room as
two. -
The body screen no longer says "Derived" over two numbers. The word reads as
something out of a maths lesson, and what the row is for is the figures
themselves; the sheet behind them still explains where each came from. -
Disk usage has left the body screen for the account screen. It is a fact
about an account rather than about a body — it counts photographs and the
files a watch writes — and it now sits beside the limit that gives it
meaning. -
Train answers the same question Today does. Today said "Push · 5 exercises"
with a button on it while Train, one tab across, said nothing was on the go
and offered a blank session — so tapping the training tab looked like being
told there was nothing planned. The plan leads there now, and recording
without one stays underneath, because somebody who does not follow a routine
is a whole user rather than a degraded case. -
Routines names both of its halves. It holds two different things — the
routines that belong to you, and the programme shapes anybody can take a copy
of — and only the shapes had a heading, so the routines read as the screen
itself and the shapes read as five more of them. Reported as "one running and
five more below", which is exactly what an unlabelled list above a labelled
one says. -
Notices no longer keep a log on the settings screen. Nobody can act on
"eleven were delivered"; they can act on some having been given up on, and on
why. That is one line when it happens and nothing when it does not. -
The green "notifications are on" banner is a row. A banner at the top of a
screen reads as the most important thing on it, and this was the least: it
told somebody what they had just done. -
Destinations and push devices are rows that lead somewhere, with the
destructive action inside rather than a red button pressed against the name. -
"Devices" and "On this device" were two screens saying much the same thing,
and are now one. What the first had that was worth keeping is a single line —
whether anything is still waiting to reach the server — and it showed the
sequence number and the sync epoch instead, which are for debugging the
protocol. -
The server screen's facts are tiles with their own icons rather than a list,
and the schema number is gone from it: it matters while a migration runs and
is otherwise a number with no action attached. The audit log is called
Records. -
Privacy is no longer a place in the navigation. It held one control, which
most people use once and many never use at all, and it now sits on the account
screen beside the question it belongs with.
Fixed
-
i18n:scancould not see any key that takes a placeholder. Its pattern
required a closing bracket straight after the string, sot("key")was
checked andt("key", { count })was not — seventy call sites, and the ones
most worth checking: a missing label is visible, a missing sentence with a
{count} in it needs somebody standing on the right screen. -
lastgym doctorwas quietly deleting activity files from everybody's usage.
The recompute summed progress photographs alone and had done since before
cardio existed, while cardio uploads charge the same counter — so rebuilding
somebody's usage discarded every file their watch had ever contributed. It
wrote the answer back, and it did that without--fix, so a command whose
whole job is to look was corrupting the number it was looking at.Activity files now carry their size the way photographs always have
(migration 018), the recompute sums both, anddoctorfills in the sizes of
files stored before there was a column to hold them. -
Your date of birth came back empty every time. It saved correctly and could
not be drawn:born_onis a moment, so it arrives as
"1990-05-12T00:00:00Z", and an<input type="date">takes exactly
yyyy-mm-ddand ignores anything else without saying so. Anybody who set it
reopened the screen, saw a blank field, and had every reason to think it had
not been saved. -
On a desktop the navigation showed itself twice. Past 60rem the bar becomes a
sidebar with every destination on it, which is the point of having the room —
but the More button stayed, sitting under the full list and offering a sheet
containing the same list again. -
A role noun stood in for a person the server has never met. The line that was
drawn in Beta.13 — a role noun stays because it is a language's unmarked word
for the job — turns out to have a second half. "Give a coach access" names the
job. "Has my coach been looking?" names somebody, and eight languages put a
gender on them: mi entrenador, mein Trainer, ο προπονητής μου, мой тренер.Where the language has a natural way to say it, the person is now named by
what they do: quien me entrena, chi mi allena, quem me treina, όποιος με
προπονεί, la personne qui m'entraîne. German, Polish and Russian have no short
neutral for it, so those ask what the audit log actually answers — whether
anybody read it — and their notification title names the session instead.The role labels themselves are unchanged, and so are the generic mentions.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
-
v1.0.0-Beta.13
Pre-releasereleased this
2026-09-02 12:18:15 +02:00 | 23 commits to main since this releaseTwenty languages that do not guess who you are
Added
-
The plural forms for the fourteen languages that were still missing them —
616 strings, written by hand. Every language is now at 100%: Arabic with all
five of its categories including the dual, Polish and Russian with three
each, and eleven more with their singular.This is the thing the base keys could not do. A count of one rendered the
plural — "1 series", "1 sesji", "1 подходов" — on the screen of anybody who
trained once in a month, and no test catches a sentence that is merely wrong
in a language nobody on the team reads. -
bun i18n:stale, the check the other three could not make: a translation of
an English string that has since been rewritten. It is a complete, correct
sentence in the right language, so nothing missing, nothing left in English
and nothing empty all pass — and what is on screen answers a question that was
asked a version ago. It reads git: if English moved after a language did, that
language is behind. An acknowledgement inscripts/i18n-stale-ok.jsonnames
the English commit it was made against, so a cosmetic rewrite can be signed
off without silencing the next real one.
Changed
-
Five strings are back to their natural wording, and on record in
scripts/i18n-identical.jsonas legitimately identical to English: the Dutch
and Turkish singular of a set, and the Italian singular of a file. All three
languages take the English loanword there, so the honest translation is the
English word — and padding it out to get past the audit is answering the tool
rather than the reader. That file exists for exactly this. -
Every empty screen now speaks in the other eighteen languages. The bin, the
photographs, the month with nothing in it, the session that recorded no sets,
the progress screen before the first workout — all of them had been rewritten
in English and Spanish and left as captions everywhere else, so a character
drawn on the screen stood over a sentence describing a table. 321 strings
across eighteen files.
Fixed
-
Ten languages assumed the reader is a man. English says "you trained" and asks
nothing about who you are; Arabic, Bengali, French, Greek, Hindi, Italian,
Polish, Portuguese, Russian and Urdu cannot say it without agreement, and each
had picked masculine and left it there. Every one is rewritten so the sentence
no longer needs the agreement — never with a slash, which is a typographic
apology rather than a fix. -
The first screen of a new server greeted her as a man.
Bem-vindoand
Benvenutoon the account-creation screen, before there is an account with a
gender to assume. Spanish had solved this long ago with "Te damos la
bienvenida"; the other two now do the same, with the agreement on the noun. -
lastgym benchmeasured every query on the progress screen except the one
with a promise attached. The load path — the band the screen draws above
everything else, and the only thing on it whose closing criterion names a
number — was not in the list, so the command reported a comfortable total for
a screen it had not finished timing. -
The photographs screen blamed a photograph when the connection was what had
failed. With no network the list never arrives, and the screen said "that
photograph could not be loaded" — about a picture nobody had asked for yet. It
now says what is actually true, which is a decision and not an accident:
photographs live on the server and are never copied to the device, because
they are fetched with credentials and drawn from a blob URL revoked as soon as
they are on screen, so nothing of anybody's body lands in a cache on a phone.
Seeing them needs a connection, and the screen says so. -
The audit log read backwards in Arabic. Not the words — the direction. The
screen substituted the two account names into the translation itself instead
of lettingtdo it, which skipped the bidi isolatestputs around every
value, and a Latin username inside an Arabic sentence then dragged the
punctuation to the wrong side. Rows starting with a name anchored left while
rows starting with an Arabic word anchored right, in the same list. Six other
screens were substituting a date, a number orlastgym userthe same way; all
of them go throughtnow, and a test refuses the second interpolator rather
than any particular string. -
Persian and Urdu addressed one reader in two registers. Five strings in
Persian and nine in Urdu used the formal شما and آپ while the fifty-odd around
them used تو and تم — the same handful of keys in both files, and the same
ones Bengali had, so one translation pass carried the register in. On the
privacy screen the heading and its own first line disagreed. -
The audit log named a gender for everyone in it. "{actor} opened the account
{subject}" has no gender in English, and none in Spanish, Portuguese, German,
Dutch, Hindi or Urdu either — their past tenses do not mark it. Arabic, Polish
and Russian conjugate it, and Greek was writing "Ο/Η {actor}", the slash this
work exists to avoid. Those four logs now read as events rather than sentences
about people: "{actor}: opening of account {subject}". The register was already
in each file — a refused sign-in has no actor to conjugate, so that one row had
been written in the passive from the start. -
Thirty-nine other strings assumed the gender of somebody who is not the
reader: the coach, the administrator, the account being deleted. German said
"Er hat keine Möglichkeit" of an administrator it has never met, French asked
"mon coach a-t-il regardé ?", Urdu had the coach putting a routine in your
account as a man. English uses singular "they" throughout and the translations
had not followed.Role nouns are deliberately left as they are — Trainer, προπονητής, منتظم. A
role noun is a language's unmarked word for the job, and the only ways to
neutralise every one of them are doublets, which is the slash, or coinages
nobody uses. That is a different question and it is not this one. -
Arabic put plurals after a hundred. In Arabic 11 and above take the singular,
so "{days} أيام" was wrong at every number the plural form exists to cover —
twenty-two strings said "100 days" the way no Arabic speaker writes it.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
-
v1.0.0-Beta.12
Pre-releasereleased this
2026-09-01 21:15:19 +02:00 | 35 commits to main since this releaseThe diagnostic comes out, and the rule it disproved with it
Removed
/insets.htmland its manifest. They answered the question they were added
for: on an iPhone the tab bar is 54 points with its label four off the bottom
edge, both safe-area insets report zero, and the viewport is 47 points shorter
than the screen — the status bar, which iOS keeps rather than hands over. The
bar that was reported as too tall and detached was an older build; it had been
fixed for two releases and could not be seen, because until Beta.07 a new
build only reached the installed application on the second launch.- The rule that made the shell full height when installed. It was written on a
theory — that100dvbreports the safe height and leaves the bar floating —
that the phone then disproved:100dvb,100%and100vhall report the
same number there. A rule that does nothing, with a comment explaining why it
was needed, is worse than no rule.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v1.0.0-Beta.11
Pre-releasereleased this
2026-09-01 20:41:45 +02:00 | 38 commits to main since this releaseThe diagnostic measures the real navigation
Fixed
/insets.htmlrenders the application's own navigation, in its own markup
and drawn by its own stylesheet, instead of a hand-made lookalike. The
lookalike had a label where the real one has an icon above a label, so the
two were never the same height and comparing them by eye proved nothing. The
height it reports is now the application's height, on the phone that is
reporting it — along with how far the label sits from the bottom edge, and
what the bar's bottom padding actually resolved to.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v1.0.0-Beta.10
Pre-releasereleased this
2026-09-01 20:20:52 +02:00 | 41 commits to main since this releaseThe diagnostic opens the way the app does
Fixed
/insets.htmlcarries a manifest of its own, so adding it to the home screen
actually opens it standalone. Without one iOS ignores the old Apple meta tag
and opens it in Safari — toolbars, no safe-area insets, and a viewport 47
points short of the screen, which is exactly what the first run reported. It
also says out loud which mode it is in, so a screenshot of it cannot be
ambiguous.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v1.0.0-Beta.09
Pre-releasereleased this
2026-09-01 20:05:24 +02:00 | 45 commits to main since this releaseA page that asks the phone what it reports
Added
/insets.html, temporarily. A page that reports what a phone actually says
about itself — whether it is running standalone, the viewport height by three
different measures, the safe-area insets, and the geometry of a tab bar built
the same way the application's is. Added because the tab bar looks wrong on
an installed iPhone and nothing available here reproduces it: Chrome does not
emulatedisplay-mode: standalone, and WebKit on Linux has no safe areas at
all. It goes away once it has answered the question.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v1.0.0-Beta.08
Pre-releasereleased this
2026-09-01 19:40:35 +02:00 | 48 commits to main since this releaseThe tab bar stops spending the safe area twice
Fixed
- The tab bar no longer spends the safe area twice on an installed iPhone.
100dvbis the right height in a browser tab, where the address bar comes
and goes and a fixed bar must follow it; installed there is no address bar
and it reports the safe height instead, stopping short of the home
indicator. The bar was fixed to the bottom of that — already 34 points above
the real edge — and then added its own 34 points of inset, because nothing
told it the first 34 were gone. Measured from the label to the bottom edge:
about 45 points on Android against about 98 on an iPhone, where the two home
indicators account for roughly ten. Full height only when installed, so the
browser-tab behaviour is untouched. - The tab bar is the height a tab bar is. Its band of icons and labels was 62
points where iOS gives one 49; eight points of padding became four and it is
54 now. The 48-point target is untouched —min-block-sizeis on the border
box — and every visible tab measures 53 after the change.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- The tab bar no longer spends the safe area twice on an installed iPhone.
-
v1.0.0-Beta.07
Pre-releasereleased this
2026-09-01 19:14:59 +02:00 | 53 commits to main since this releaseA new build lands on the first launch, and the door is centred
Fixed
- The installed application picks up a new build on the first launch after it,
instead of the second. Assets are stamped with the build precisely so a new
one cannot be served from an old cache, and the service worker matched them
withignoreSearch— which throws that stamp away, so/app.css?v=new
matched the cached/app.css?v=old. A navigation is network-first, so the
fresh HTML arrived and asked for the new stylesheet, and the previous build's
one came back. Every fix landed one launch late, which from the outside is
"I updated and it still looks the same". - The sign-in card sits in the middle of the screen. It went straight into the
scroll container and therefore straight to the top of it, with the mascot
tucked under the status bar and two thirds of the display empty below the
button. Centred with safe alignment, so a short screen or a keyboard over
half of it pushes the card down rather than putting the username field out of
reach above the top.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- The installed application picks up a new build on the first launch after it,