tracker
All repositories: gitoria
26.3 KB
# STATUS — tracker.worldapi.org## 2026-09-27 — ticket #6: /scheduleBuilt `/schedule` (`components/schedule.hl`): every episode of a show the signed-in user followsthat is NOT yet released (`e.release > today`), soonest first. Creator: "/schedule has a similarlist without check icons for the upcoming episodes of shows followed" — same row shape as`/unwatched` (`components/unwatched.hl`) minus the check icon.**Reuse, not copy**: imports the exact same `followedShowIds` (`follows.hl`), `showById` /`seasonsOfShow` / `episodesOfSeason` (`shows.hl`) that `/unwatched` already uses — nothing newadded to either file. `watches.hl` is not imported at all: a row here is never watched/unwatched,there is no check icon and no click, so the page needed no per-user watch state and no `on server`face — the whole component is a single read-only server-side render (`rows` computed once from`session`, same as every other reactive member here).**Order**: soonest first, ascending by `release` (a plain `'YYYY-MM-DD'` string, orderingoperators confirmed working against this app's own vendored `bin/hybriel`, same as `/unwatched`'sSTATUS entry) — the ticket named this the architect's own reading ("the creator named the orderonly for /unwatched"), so `insertByReleaseAsc` mirrors `/unwatched`'s `insertByReleaseDesc` withthe comparison flipped.**`decided`**: "upcoming" = `release` strictly after today (not `>=`) — the natural complement of`/unwatched`'s own `release <= today`, so every released-or-today episode is unambiguously on onelist, never both, never neither.**Gate** (`tests/browser.mjs`, 35 → 38 checks): reused the same fixture (`tests/seed-show.hl`,unchanged) — episode 3 already carried a far-future release (`2099-01-01`) for `/unwatched`'s own"excludes the future one" check; `/schedule` now proves the mirror image with the same fixture:signed out, no rows, the sign-in message; signed in, only that one future episode shows (the twoalready-released ones excluded), no `button.checkicon` anywhere on the page, unaffected by any ofthe watch toggles the other checks perform on the same fixture (run in any order — `/schedule`never reads watch state).```node tests/browser.mjs38 passed, 0 failedps -eo pid,args | grep [h]l-browser-tier(nothing)```Updated `README.md` ("What it does (step 6)", Test, Files) and this file.**Open**: nothing shown on the homepage still points to `/schedule` (same as `/unwatched` beforeit — the homepage stays empty per ticket #1's decision).## 2026-09-27 — ticket #5: /unwatchedBuilt `/unwatched`: every episode of a show the signed-in user follows that is already released(`release` ≤ today, a plain `'YYYY-MM-DD'` string — migrated as-is by `tools/migrate.hl`, neverrun through `dateOf`) and not yet watched, newest release first (creator: "/unwatched lists allunwatched episodes of shows followed in release date desc order").**Reuse, not copy** (the ticket's own instruction, re "the same check icon and watch logic as theshow page"): `watchClassOf` (watched → `'checkicon solid'`/`'checkicon regular'`) was a private`static` in `components/show.hl`; moved it to `watches.hl` so both pages import the SAMEdefinition instead of each declaring their own — `show.hl` now imports it too, nothing duplicated.`isWatched`/`setWatched` (already in `watches.hl`) needed no change. New `follows.hl`(`followedShowIds`) and `shows.hl` `showById` (one line) round out the read-only data access thispage and `/schedule` (ticket #6) both need.**String comparison** (`e.release <= today`, `row.release >= x.release` for the desc sort):hybriel#2 ("no ordering operators on strings") is filed as still-open in this ticket's brief, butits own history shows it was fixed and merged to master 2026-09-25 — confirmed directly againstTHIS app's own vendored `bin/hybriel` (`'2020-09-03' <= '2026-09-27'` → `true`), so no numericdate workaround was needed.**A View text node cannot hold an inline expression** (`"Ep " + row.episodeNumber + " · " +row.title` inside a tag body) — same rule as View attributes (components/show.hl's own comment:"a View attribute must be a literal, a member or a field path"), just not documented for textnodes anywhere yet; it silently rendered `<!-- binary_expression not rendered yet -->` instead oferroring. Fixed by resolving the label into a plain field (`row.episodeLabel`) once, server-side,in `unwatchedRowsOf` — the same pattern `show.hl`'s `buildRows` already uses for every otherternary/concatenation. Not filed as a new Hybriel gap since it's the exact same restrictionalready known and worked around elsewhere in this app, just not yet hit for interpolated text.**Empty state**: signed out (no user, so no follows to show) and signed in with nothing unwatchedboth render a message; used `.muted` (colorTextMuted), not `.message` (colorDanger) — the existing`.message` class is for real errors (the login-failed banner), and an empty list isn't one (a`decided`, small).**Gate** (`tests/browser.mjs`, 29 → 35 checks): `tests/seed-show.hl` now takes an optional`TRACKER_SEED_IDENTITY` and, when given, seeds a fixed user + a follow of the fixture show for it— the only way to give `/unwatched` a known followed show before the browser ever signs in, sincethe real login's `ensureUser` matches an EXISTING `users.db` row by `identity` and reuses its idrather than minting a new one. The gate learns alice's per-app identity with one throwaway`ident.exchange` call (consuming one code) before writing the seed. Episode 1/2 of the fixture nowcarry a past `release` (`2020-01-01`/`2020-06-15`), episode 3 a far-future one (`2099-01-01`) —proving both the release-date filter and the desc order. The new checks run signed out (no rows,the sign-in message), then signed in RIGHT AFTER LOGIN, BEFORE the existing show-page checks thattoggle these same fixture episodes watched (order matters — otherwise `/unwatched` would alreadybe empty by the time it's tested): both unwatched episodes list newest-first, excluding thefuture one; a click on the newest (`Second`, ep 2) removes it from the list; a reload proves thewatch is server-side, leaving only `Pilot` (ep 1).```node tests/browser.mjs35 passed, 0 failedps -eo pid,args | grep [h]l-browser-tier(nothing)```Updated `README.md` ("What it does (step 5)", Test, Files) and this file.**Open**: nothing shown on the homepage still points to `/unwatched` — no navigation exists yet(the homepage stays empty per ticket #1's decision; a later step, from the creator, presumablyadds it). `/schedule` (ticket #6) is next and reuses `follows.hl`/`shows.hl`/`watches.hl` the sameway.## 2026-09-27 — ticket #4: the show page, watch checks (took over a previous half-done session)Continued from an earlier session that had built the whole page — header, seasons/episodes,the click-handling redesign to a flat `rows` list rebuilt server-side (`components/show.hl`'sown header comment explains why: a nested `for` over seasons > episodes risks hybriel#86) — butleft with the gate red: clicking a check never turned it solid, no console error, and thesession ran out of budget before finding why.**The actual bug**: the show page's own pure helpers (`buildRows`, `genreRowsOf`, `castRowsOf`,`watchClassOf`, `initialCollapsed`) were plain instance members (`buildRows = (show, …) => {…}`,no `static`). That shape works fine as a member's own top-level initializer (`rows = found ?buildRows(showRow, watchRows, collapsed) : []` — this is how the page renders correctly on firstload) but throws when the SAME function is called from inside an `on server` face(`showToggleEpisode` etc.): the server log showed `'buildRows' is not callable — it holds null`.Found by running `.scratch/debug-click.mjs` (left by the previous session) with the server'sstdout/stderr captured — the browser console itself stayed silent (the face's error neverreaches the client at all, a separate Hybriel gap, hybriel#92 already covers that half).Confirmed the fix by grepping other worldapi apps for the same shape:`calendar.worldapi.org/components/calendar.hl` has `static soonOf = (u) => { … upcoming(u, …) }`(an mpackdb-backed helper), called from its `on server calLoad`/`calSettingsLoad` faces, andcalendar's own gate is green — so `static` is the proven-safe shape for a component's own helperthat both (a) touches an mpackdb-backed import and (b) needs calling from more than one place(its own initializer AND a later face). Declared all five helpers in `show.hl` `static`; reranthe gate: 29/29 green, including the season-bulk-toggle and the reload-proves-server-side checks.Filed **hybriel#115 was already open** (from the previous session, about the CLIENT bundleomitting a case for such a helper) but this session's finding is the SAME root cause reachingfurther than that ticket says: the plain HL interpreter on the SERVER also treats such a helperas `null` outside its own initializer, not just the JS-compiled client bundle. Left as one`issues` entry pointing at hybriel#115 rather than opening a near-duplicate ticket — therepro/observed there already generalizes (a per-instance member function wrapping anmpackdb-backed import is unreliable wherever it is called from, not just the browser).Ran `node tests/browser.mjs`: 29 passed, 0 failed (was 20 passed / 1 failed — the click timeout— before the `static` fix).**Rehearsed `tools/relink-episode-seasons.hl` against a full COPY** of the real`storage/mpackdb/` (`.scratch/relink-rehearsal/`, since removed) — the tool's own header claimedthis rehearsal already existed here; it didn't (a leftover claim from the previous session), sothis session actually ran it:```relink: episodes 231584, null season before 36194, linked 36194, still null (no matching season) 0relink: episodes.db replaced with the relinked file — re-run to confirm idempotency (0 linked the second time)```Ran a second time on the same copy straight after — idempotency confirmed:```relink: episodes 231584, null season before 0, linked 0, still null (no matching season) 0```All 36,194 pre-existing null-season episodes (ticket #2's count) now link to a real season by`(show, seasonNumber)`; none left over. Not run against the LIVE `storage/mpackdb/` — that needsthe tracker container stopped first (hl:mpackdb does not coordinate two processes on one storagedirectory, hybriel#21), the architect's job per the ticket ("the architect runs it live").Updated README.md ("What it does (step 4)", Test, Files) and this file.## 2026-09-27 — ticket #2: old data migrated (took over a previous session's rewritten tool)Continued from an earlier session that had rewritten `tools/migrate.hl` to avoid a real data-lossbug (see below) but never run it even once, and had left the real `storage/mpackdb/` holding datafrom the OLDER, buggy two-pass version of the tool (put a bare record, then `update()` it once theother side of a circular reference existed). Took over: moved that buggy data aside(`.scratch/pre-migrate-buggy-backup`, since removed), kept `storage/mpackdb/users.db` (step 1'slogin table, untouched by the migration), and ran the already-fixed tool for the first time.**The fix already on disk (kept as is):** `Show.seasons`/`Season.show` and `Show.cast`/`Person.shows` are two-way references, so the id on one side must exist before the record on theother side can be built. The old approach — `put()` a bare record, come back with `update()` oncethe far side's id exists — loses rows: reopening a table in a fresh process after an `update()` hadtouched it read back FEWER rows than were ever `put()` (seen directly: `persons.db`'s `count()`dropped 15157 → 15152 after one round of `update()` — a real `hl:mpackdb` bug, reported to Hybriel,not something an app is allowed to patch around). The fix assigns every new id itself (8 randombytes, `hl:crypto`) in one pass over each export file BEFORE building any record, so by the time arecord is actually built every reference is already a known id — one `put()` per row, `update()`never called.**Run** (`tools/migrate.hl`, `TRACKER_OLD_DATA=.../old-tracker/mongo-export`,`TRACKER_STORAGE=$PWD/storage/mpackdb`):```migrate: genres 27 new, 0 already there (old 27 -> new 27)migrate: persons 15157 new, 0 already there (old 15157 -> new 15157), show refs skipped 0migrate: shows 9453 new, 0 already there (old 9453 -> new 9453), cast refs skipped 0, genre refs skipped 0, season refs skipped 0migrate: seasons 6430 new, 0 already there (old 6430 -> new 6430), show refs skipped 0, episode refs skipped 0migrate: episodes 231584 new, 0 already there (old 231584 -> new 231584), show refs skipped 0, season refs left null (pre-existing) 1migrate: follows 128 new, 0 already there (old 128 -> new 128), show refs skipped 0migrate: watches 11692 new, 0 already there (old 11692 -> new 11692), target refs left null (pre-existing) 3migrate: 0 failed```Every count equals the old export's own document count (`old-tracker/README-RECONSTRUCTED.md`:Show 9453, Season 6430, Episode 231584, Person 15157, Genre 27, Follow 128, Watch 11692). Ran asecond time straight after (idempotency: the tool's `oldId` index finds each row again) — everytable printed `0 new`, all rows `already there`, counts unchanged, 0 failed: the id-loss bug doesnot resurface across a real close-and-reopen.**Proof beyond the tool's own counters** (`tools/verify.hl`, new): the ticket asks for a check thatdoes not just trust `migrate.hl`'s own "skipped" tallies. It runs against a plain filesystem COPYof `storage/mpackdb/` (hl:mpackdb rewrites a file's data on open, so the live store is never openeda second time) and independently re-derives, from the persisted records alone: every table's`count()` against the export's own document count, and — by building id sets for every table andwalking every reference field (`Show.genres/cast/seasons`, `Season.show/episodes`,`Episode.show/season`, `Person.shows`, `Follow.show/user`, `Watch.target/user`) — that everynon-null reference resolves to a real id. It also follows one show end to end (`Raised by Wolves`,2 seasons, 18 episodes, 1 follow, 2 season-level watches — all cross-checked back to the show).```$ cp -r storage/mpackdb .scratch/verify-copy && TRACKER_VERIFY_COPY=$PWD/.scratch/verify-copy ./bin/hybriel tools/verify.hlcount ok genres: 27 (expected 27)count ok persons: 15157 (expected 15157)count ok shows: 9453 (expected 9453)count ok seasons: 6430 (expected 6430)count ok episodes: 231584 (expected 231584)count ok follows: 128 (expected 128)count ok watches: 11692 (expected 11692)users: 1 (expected 1, the creator)dangling references: 0 (episodes with a pre-existing null season: 36194, watches with a pre-existing null target: 3 — not dangling, the export already had no target)END-TO-END show 105ad9c91b14d663 "Raised by Wolves" seasons=2season 1 (be2965a5ab7165ca) episodes=10 show-back-ref-ok=trueseason 2 (0fe4e53157ed0367) episodes=8 show-back-ref-ok=truetotal episodes across seasons: 18follows on this show: 1, season-level watches touching it: 2VERIFY PASS```(36194 episodes with no season = 36193 that never had one in the old export, plus the 1 the tool'sown count already named as a pre-existing dangling `season` reference in the source data — neitheris a reference this migration broke; `dangling references: 0` covers exactly that.)The single old user (`User.jsonl`, `[email protected]`, password not taken over) is this app'suser for ident short id `az5b2` (`storage/mpackdb/users.db`, the same table step 1's login uses) —confirmed in the copy: one record, `identity=az5b2`.**Two Hybriel bugs found while building this** (both already filed as issues on this ticket by anearlier session, not re-filed): `hl:fs readFile` silently truncates at 10 MiB (worked around herewith `split`, no shell, no JS, see `tools/migrate.hl`'s header comment) and `hl:mpackdb` loses rowsacross `update()` + reopen (the reason this tool never calls `update()`).**Open**: nothing shown yet — this ticket is data-only, on purpose (the next step, from thecreator, is showing the data in the UI).## 2026-09-27 — ticket #3: no border on the header or filled buttonsDesign notes from the first test (architect): "the application-header should have no bottomborder" and "the buttons also no border except they are inverted". Two changes in`styles.hl`:- `applicationHeader`: dropped `borderBottom`.- `ident-selector::part(button)` (the signed-out "choose ident" button): added`border = 'none'` — the element's own CSS gave it a 1px border the same colour as itsbackground (`--_accent`), invisible but still a real border in the computed style; this isthe app-side restyling hook ident's README documents (`::part(button)`), not a change toident's vendored `selector.js`.Left unchanged, already correct: the native `button` rule (filled, e.g. no other filledbuttons on this page yet) already has `border = '0'`; `button.quiet` ("Log out") and theselector's dropdown `[part="identity"]` rows keep their border (transparent background —inverted style); `a.button` ("Log in with ident") has no border rule and browsers give `<a>`none by default. The selector's signed-in "logged in with ident" status pill(`::part(status)`) is not one of the buttons the ticket names and isn't a button element(a `<span>`) — left with its border.**Gate** (`tests/browser.mjs`, now 16 checks): added computed-style checks — the header's`borderBottomWidth` is `0px`; `#loginbutton`'s border is `0px`; the selector's`[part~="button"]` (inside its shadow root) border is `0px`; `#logout`'s border is NOT `0px`(still inverted-bordered) once signed in.```node tests/browser.mjs16 passed, 0 failed```## 2026-09-27 — ticket #1 reopened: login redone to match calendar exactly (header selector, empty page)The architect rejected the first build of ticket #1: the sign-in sat centered on the page(its own "Sign in with ident" card, identity + sign-out shown in a `homeCard`) instead ofthe header identity selector calendar/gitoria use, and there was no `<ident-selector>` atall. Rebuilt the login by copying calendar.worldapi.org's own files (unchanged in shape, perthe architect's instruction "Copy calendar.worldapi.org's login unchanged"):- **`users.hl`** (new): the ident exchange + a `usersTable` (`storage/mpackdb/users.db`,`identity` → this app's user id), copied from calendar's `users.hl` with `TRACKER_KEY` /`TRACKER_SECRET` / `TRACKER_URL` / `TRACKER_STORAGE` in place of calendar's names (keptthe names already in this app's `docker-compose.yml`/README/DECISIONS rather thanswitching to calendar's generic `IDENT_API_KEY`/`IDENT_API_SECRET`).- **`login.js`** (new): calendar's bridge between `<ident-selector>`'s `ident-login` DOMevent and the hidden `#identcode` input, copied verbatim (creator: "login.js is correct,as thats for externals in general").- **`components/main.hl`**: now the header carries `userBox` (`<ident-selector>` + "Log inwith ident" / "Log out"), the `trackerLogin`/`trackerLogOut` faces and the`trackerSignedIn`/`trackerSignedOut` client push — calendar's shell renamed to this app'sevent names.- **`components/home.hl`**: now truly empty (`View { home {} }`) — no sign-in link, noidentity/sign-out duplicated on the page; that is entirely the header's job now.- **`components/loginfailed.hl`** (new) + **`project.hl`**: the login routes are now`/login/callback` (function route, the button's return AND the code exchange) and`/login/failed`, copied from calendar's `project.hl` (`safePath`, `failed()`,`loginCallback`) in place of the old `/login` + `/callback` + `/logout` routes; the old`/logout` POST route is gone (logout is a face, like calendar's).- **`styles.hl`**: added the header selector styles (`userBox`, `identSelector`,`ident-selector::part(...)`, sticky header) from calendar's `styles.hl`; dropped thenow-unused `homeCard`/`accountBar`/`userName` rules from the rejected centered sign-in.**Gate rewritten** (`tests/browser.mjs`, 12 checks): replaced the old gate (which droveident's full email-code UI through a vendored dev copy of ident at `.scratch/ident-dev`)with calendar's own pattern — `tests/identkit.mjs` (copied from calendar unchanged) starts athrowaway ident (a fresh copy of `/media/STORAGE/projects/ident.worldapi.org`'s code, no`.env`/storage copied, codes to a mail sink, never the live ident), signs in over its RESTAPI and registers this app, then the gate proves the header in a real headless Chrome:signed out → `#selector` + `#loginbutton` in the header, **`main` is empty** → sign in(`/login/callback?ident_code=`, the same exchange the selector would trigger) → `#selector`shows `class="in"`, `#logout` appears, **`main` still empty** → sign out → signed out again→ a reload stays signed out. No console errors.```node tests/browser.mjs12 passed, 0 failed````./deploy.sh --dry-run --target .scratch/deploy-preview --url http://127.0.0.1:0/`: gatepassed as step [1/4], rsync preview held none of `storage/`, `.sessions/`, `.env`,`.scratch/`, `server.*`, logs. Removed the stale `.scratch/ident-dev` (the old gate's vendoredident copy) and `.scratch/browser-gate` (the old gate's storage) — unused by the new gate.**Still open**: the app is registered in ident's dev copies only (this gate's own throwawayident). The LIVE ident registration (`TRACKER_KEY`/`TRACKER_SECRET` in `.env` next to`docker-compose.yml`) is the architect's first-deploy step (README "Deploy"), not built here.## 2026-09-27 — ticket #2: data migration — blocked twice on sandbox access, nothing doneTwo workers in a row (`s-20260927T1029-f57b0f`, then this session) could not start: themigration source the ticket names, `/media/STORAGE/projects/old-tracker/mongo-export/*.jsonl`+ its `README-RECONSTRUCTED.md`, is not reachable from this worker's sandbox. Verified freshthis session:```$ ls -la /media/STORAGE/projects/antcolony-docs hybriel ident.worldapi.org tracker.worldapi.org # no old-tracker$ mount | grep STORAGE/dev/nvme1n1p6 on /media/STORAGE/projects/tracker.worldapi.org type btrfs (rw,...)/dev/nvme1n1p6 on /media/STORAGE/projects/hybriel type btrfs (ro,...)/dev/nvme1n1p6 on /media/STORAGE/projects/ident.worldapi.org type btrfs (ro,...)/dev/nvme1n1p6 on /media/STORAGE/projects/antcolony-docs type btrfs (ro,...)# old-tracker is not among the mounted subvolumes at all```So this is not a data problem (the export is "complete" per the ticket) — it's the workersandbox for this ticket that never got `old-tracker` mounted (read-only, like `hybriel` /`ident.worldapi.org` / `antcolony-docs` above). Nothing was built or changed this session:no `storage/`, no `tools/`, no id-mapping — writing the migration tool blind, guessing theJSONL shape from the ticket text alone, risks silently wrong data and was avoided on purpose(see the project's "Build it properly" rule). Filed as an antcolony issue so the next worker'ssandbox includes `old-tracker` before another attempt.## 2026-09-27 — ticket #1: the empty shell, ident loginBuilt the app from scratch: vendored `bin/hybriel` + `plugins/` + `shared/tokens.hl` from`ident.worldapi.org` (newest local build, hybriel master 837fe120, no local patch — seeREADME "Vendored Hybriel"). `project.hl` (routes `/login`, `/callback`, `/logout`, `/`),`components/home.hl` (the empty homepage), `components/main.hl` (shell), `styles.hl`(accent green `#4ec9b0`, per `antcolony/README.md` "look and style" / `CONCEPT.md`).`docker-compose.yml` / `Dockerfile` / `deploy.sh` following ident's own house pattern(port 45008, container `tracker.worldapi.org`).**Login**: the ident login BUTTON flow (ident.worldapi.org README "How apps use ident"),server-side exchange (`POST <ident>/api/exchange`), this app's own framework session(`session.user = { identity }`, cookie `trackersid`). ident hands over only the identity'spublic **short id** — no display name yet (ident#11, properties handover, is on hold) — so"your name" on the homepage is `Signed in as <shortid>`, the same identifier every otherworldapi app would show; see `decided` in the report.**Lesson (Hybriel)**: a face that mutates `session.user` (e.g. `signOut`) does NOTautomatically re-render the page — a component's reactive members (`signedIn`, `identity`,…) are computed once at mount from the framework's read-only session snapshot; a clienthandler that calls `emit server X()` must ALSO flip the affected members itself afterwards(ident's own `components/home.hl` `on doSignOut(e)` does exactly this: `emit serversignOut()` then `signedIn = false` …). Missing that made the sign-out button silently donothing client-side (the ack came back `ok:true`, the server-side session really wascleared — proven by a reload — but the DOM never updated) until copied.**Gate** (`tests/browser.mjs`, 13 checks): a fresh copy of `ident.worldapi.org`'s code (no`.env`, no `storage/`, no `.sessions/` — verified empty of secrets/data before first use)runs as this gate's own ident, on its own storage and its own mail sink (no live ident, noreal mail). The gate registers this app in that dev ident once over the `/__hl/emit` API(the same one-time step a person does by hand in ident's `/apps` page), then drives theREAL login-button flow in a real headless Chrome: signed out → click "Sign in with ident" →ident's email form → the mail-sink code → the first-login optional-names form (Skip) → theone-identity choice (one click) → back on tracker, "Signed in as `<shortid>`" → "Sign out" →signed out again → a reload stays signed out. No console errors. Ran twice for stability, 0failures; no leftover Chrome/hybriel processes after either run.```node tests/browser.mjs13 passed, 0 failed````./deploy.sh --dry-run --target .scratch/deploy-preview --url http://127.0.0.1:0/` (a localdirectory, per ident's own convention for testing deploy.sh without touching Byrodin): gatepassed, rsync preview held none of `storage/`, `.sessions/`, `.env`, `.scratch/`, `server.*`,logs — confirmed by the same grep the script refuses on.**Open**: the app is not registered with the LIVE ident yet — `TRACKER_KEY`/`TRACKER_SECRET`are unset until the architect's first deploy does that (README "Deploy"); until then`/login` on the live site answers a plain error page instead of redirecting (`/` itselfstill renders). This is normal for a brand-new app, the same as ident's own SMTP `.env`before its first deploy — not something this ticket's worker can set (no `.env` access,and it is a LIVE ident registration).
Branches
- mainmain branch
Latest commits
- 44b7d9f9tracker#6: /schedule — upcoming episodes of followed shows, soonest firstmre
- 91c9fc8ctracker#5: /unwatched — unwatched released episodes of followed shows, newest firstmre
- a97c0295tracker#4: show page /shows/:slug (header, seasons, episodes, watch checks) + tools/relink-episode-seasons.hlmre
- 05f407c5tracker: no border on any button except inverted ones (Log out, ident status and identities too); header brand weight 100mre
- 17375427tracker#2: tools/migrate.hl + tools/verify.hl — old MongoDB data into mpackdb with new idsmre
- 31aac936tracker#3: no border on the header and on filled buttons; inverted buttons keep theirsmre
- 2ad9d29ctracker#1: login exactly like calendar (identity selector in the header, empty homepage)mre
- 3691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre