gitoriaLog in with ident

notes

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit3f8cb3833f8cb383notes mission 002 (2/4): code order — topics, map, thin wrappers: lib/util.hl, lib/notes(-helpers).hl, lib/users.hl (+userOfLoginCode, tagOf), lib/api(-helpers).hl; project.hl = map; login routes take &req/&sessions (failed-login reason now kept); dead notes#1 functions removed; gates 50/0 + 18/0mre3f8cb383/reports/001-notes-offline-hybriel.md

5.3 KB

  1. # w051 — notes.worldapi.org#4: offline in pure Hybriel — STOPPED at Hybriel gaps
  2. **Nothing moved.** notes-offline.js and sw.js stay, and no app file changed (only a new top entry in STATUS.md on Loreana).
  3. I measured hl:web's offline realm (`offline = [...]`: generated worker, kept state, durable queue) on a minimal repro with
  4. the vendored ff51cf46 in real Chrome. Two things notes needs are missing, so switching now would ship a half-offline notes,
  5. which the mission forbids.
  6. Repro: `loreana:/media/STORAGE/projects/notes.worldapi.org/.scratch/w051/repro/`. It has one page component at `/` and
  7. `/p/:id`, a server-fed `count` that a push updates, and a queued `emit server`. Run it with `node run.mjs` (port 8762,
  8. Chrome 8767-8769). Output:
  9. ```
  10. online /p/1 after 3 bumps: count = 3
  11. offline /p/1 reload: count = 3 (kept for this URL)
  12. offline / reload: count = 0 (server said 3 before the outage)
  13. offline /p/7 (never visited): "Unavailable offline"
  14. offline bump, then reload: queued = 1 count = 3
  15. server back (fresh, count 0): count = 1 queued = 0 (the queued bump replayed)
  16. link with on click { e.preventDefault() }: location = / handler ran: yes
  17. ```
  18. What works: the queue survives a reload and replays, `__hlQueue` can show offline edits again after a reload, and an inline
  19. handler can claim a link.
  20. ## Hybriel ticket texts
  21. **H1 — hl:web offline: a kept page's state is stored per URL, so a page with several URLs shows stale data offline.**
  22. `client.hl stateKey()` = `<component>|<pathname+search>`. `remember()` writes each push under the URL the tab is on at
  23. that moment. Repro above: 3 pushes on `/p/1`, then go offline and load `/`: it shows 0, while `/p/1` shows 3. For notes, the
  24. list (`notes = notesFor(session)`) is the same at `/` and every `/note/:id`. A user who edits at `/note/A` and then reloads
  25. `/` offline sees an old list, and an edit to a stale note becomes a conflict copy.
  26. Proposal: keep a need that does not read a route parameter per component, and one that does read it per URL. The graph
  27. already knows what each need reads.
  28. **H2 — hl:web offline: a param page of a kept component that was never loaded as a document is "Unavailable offline".**
  29. `worker.hl navigation()` only answers from `caches.match(path)`. Repro: `/p/7` offline gives 503 "Unavailable offline",
  30. although `Page` is kept. For notes: a note created offline (`/note/l…`), or a note opened in-app, cannot be reloaded
  31. offline. Today's sw.js serves one kept shell for every `/note/*`.
  32. Proposal: for a kept route with parameters, fall back to any kept document of the same route component, and boot it with
  33. the URL's parameters and the kept state (H1).
  34. **H3 — hl:web offline: kept state, kept documents and the queue belong to no user and cannot be forgotten.**
  35. The IndexedDB `hl-offline` store (`state`, `queue`) has no user or session in its keys. The `hl-shell-*` cache keeps SSR
  36. documents, and their seed carries the page state, which for notes is the notes themselves. The package exports only
  37. `navigate` and `notify*`, so there is no way to drop any of this. Effects for notes:
  38. - After a log-out, the notes stay readable in this browser. Today notes-offline.js deletes its copy at log-out, and the
  39. gate checks that.
  40. - A queue left by user A replays under whichever session opens the next socket.
  41. Proposal: key the state and the queue by the session's user, drop that user's state, queue and documents at log-out, or
  42. export `forgetOffline()` from hl:web.
  43. ## App design for when H1–H3 land (worked out, not built)
  44. - `components/notes.hl` (kept):
  45. - Server-fed `notes = notesFor(session)`, with the full text of every note.
  46. - `__hlQueue`, and `mine` (this tab's edits the server has not acknowledged yet).
  47. - `rows` is derived on the client from notes, then `__hlQueue`, then `mine`. The helper functions must live in the
  48. component file, because a braced import makes the derivation `srv` and it is never re-derived.
  49. - The editor is a `for` over a one-row list keyed by a stable id (the local id if the note has one). It is rebuilt only
  50. when a different note is selected.
  51. - Statement-form faces `notesPut({owner,id,local,text,base,when})` and `notesDrop(...)` are queued offline. The push
  52. `notesChanged(user, list, done)` has an audience of the same user. Use `when`, not `at`: `__hlQueue` rows overwrite `at`.
  53. - Catch-up after a reconnect: a value-form `notesPull` (never queued). A null answer means offline, which drives the
  54. status line. A CSS-animation tick runs the pull, as tickets/tracker already do.
  55. - Server, `notes.hl`: queued saves from one device all carry the same old `base`, so the second one would conflict.
  56. - Fix: store `by` (the session tag) and `from` (the base of the last accepted save). A save with the same `by` and `from`
  57. is accepted as a continuation, not a conflict.
  58. - A losing device's further saves update its existing copy (`copyOf`, `by`, `from`) instead of making a new copy each
  59. time.
  60. ## Gate
  61. The existing gate on the unchanged app:
  62. `NOTES_GATE_PORT=8760 NOTES_GATE_IDENT_PORT=8761 NOTES_GATE_CHROME=8767-8769 node tests/browser.mjs` → **50 passed, 0 failed**.
  63. No new gate checks, because nothing was built. Afterwards nothing is left running on 8760–8769 (8765/8766 are the
  64. creator's services and were already running).
  65. ## Open
  66. - notes#4 waits for H1–H3. Please file them in project hybriel; I did not post them.
  67. - The repro stays in `.scratch/w051/repro/` for the hybriel worker.

Branches

Latest commits

  • 3f8cb383notes mission 002 (2/4): code order — topics, map, thin wrappers: lib/util.hl, lib/notes(-helpers).hl, lib/users.hl (+userOfLoginCode, tagOf), lib/api(-helpers).hl; project.hl = map; login routes take &req/&sessions (failed-login reason now kept); dead notes#1 functions removed; gates 50/0 + 18/0mre
  • 8cda5412notes mission 002 (1/4): code order — files moved: lib/notes.hl, lib/users.hl, lib/jsoncheck.hl, components/styles.hl (imports only); gates 50/0 + 18/0, live-data run identicalmre
  • 47dad68bnotes: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gates 50/0 + 18/0mre
  • a4a2b2aeantcolony#40: tracker missions moved too — references to them in missions/reports/LOG.md updatedmre
  • 3dea0ef2notes: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gate 50/0mre
  • e27c7d71notes: Hybriel master 8efba065 (#126 GC by bytes, #48 lambda params copy; audit: nothing to fix; gate 50/0)mre
  • 124613b6antcolony#40: mission references point to the moved missionsmre
  • 1ca2f34dantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • 2bebebdanotes: Hybriel master ff51cf46 (re-vendor round)mre
  • 3eff126dnotes#3: installable app (manifest + own icon/favicon; notes' own sw.js kept)mre
  • 9883c540deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • eee693b8deploy.sh: never send .git or .gitignore to Byrodinmre
  • c8904061State of 2026-09-27, before the move to gitoriamre