OverviewFilesCommitsIssuesDocsReleasesPeers

favela-surface commits / cb161ac

0.0.53: settings names the running VERSION, not just two tree hashes
You cannot tell whether an update landed by looking at two 12-character hashes. The update section showed exactly that, so "is my update installed?" was unanswerable from the device — which is the one question the section exists to answer, and the reason a phone sat on an old build with no way to know. The version now leads the section, with its build time beside it. ⚠ A build that predates the stamp SAYS SO rather than rendering a blank or, worse, a plausible-looking wrong version. Asserted, along with the absence of "Running undefined" — a fabricated version is worse than an admitted gap, because it is the kind of wrong a person acts on. 183/183 tests, 593/593 asserts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cb161acec5e72407729f85be41be93cc93c15c63
by plankatron <fleeky@gmail.com> · 2026-09-10T15:30:25.000Z

Changed files (3) +24 -3

lib/pages.js +8 -1
……@@ -1496,6 +1496,13 @@ export function updateSettings (u) {
14961496 if (!u) return ''
14971497 const short = (h) => (h ? esc(String(h).slice(0, 12)) + '…' : '<span class="who">unknown</span>')
14981498 const checked = u.checkedAt ? '<p class="who">Last checked ' + esc(u.checkedAt) + '</p>' : ''
1499 // ⛔ THE VERSION FIRST, because it is the only line here a person can compare against a release
1500 // page. Two tree hashes say something differs; they do not answer "is my update installed?", which
1501 // is the question anyone opening this section actually has.
1502 const built = u.builtAt ? ' <span class="who">built ' + esc(new Date(Number(u.builtAt)).toISOString()) + '</span>' : ''
1503 const version = u.version
1504 ? '<p><strong>Running ' + esc(u.version) + '</strong>' + built + '</p>'
1505 : '<p class="who">This build does not report its version — it predates the stamp.</p>'
14991506
15001507 // ⛔ STAGED AND DIFFERENT is the only state that means "an update is waiting". Staged and IDENTICAL
15011508 // is the ordinary case — the node re-verified the bundle it is already running — and calling that
……@@ -1507,7 +1514,7 @@ export function updateSettings (u) {
15071514 (u.ota ? '<p><button id="app-restart">restart now</button></p>' : '<p class="who">This app cannot restart itself; close and reopen it.</p>')
15081515 : '<p>Up to date.</p><p class="who">running ' + short(u.running) + '</p>'
15091516
1510 return '<h2>App updates</h2>' + body + checked +
1517 return '<h2>App updates</h2>' + version + body + checked +
15111518 (u.ota ? '<p><button id="app-check">check now</button> <span id="app-msg" class="who"></span></p>' : '') +
15121519 (u.ota ? '<script type="module">\n' + UPDATE_JS + '\n</script>' : '')
15131520}
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "favela-surface",
3 "version": "0.0.52",
3 "version": "0.0.53",
44 "description": "The favela renderer \u2014 server-side HTML over the daemon's socket. No storage, no keys, no swarm.",
55 "type": "module",
66 "main": "index.js",
test/pages.test.js +15 -1
……@@ -2,7 +2,7 @@
22
33import test from 'brittle'
44import fs from '#node:fs'
5import { timeline, search, following, knocks, post, esc, layout, who, person, settings, handleSettings, namespaceSettings, composer, mentions, notifications, thread as pages_thread, withUpdateBanner } from '../lib/pages.js'
5import { timeline, search, following, knocks, post, esc, layout, who, person, settings, handleSettings, namespaceSettings, composer, mentions, notifications, thread as pages_thread, withUpdateBanner, updateSettings } from '../lib/pages.js'
66
77const row = (over = {}) => ({
88 author: 'ab'.repeat(32), collection: 'post', name: 'p1',
……@@ -601,3 +601,17 @@ test('the banner sits at the top of the document, not wherever a page happens to
601601 const out = withUpdateBanner('<html lang="en"><body><h1>x</h1></body></html>', { running: 'a', staged: 'b' })
602602 t.ok(out.indexOf('update-bar') < out.indexOf('<h1>'), 'it cannot be pushed below the fold')
603603})
604
605// ⛔ The version is the only line in the update section a person can compare against a release page.
606// Without it, settings showed two tree hashes and "is my update installed?" was unanswerable from
607// the device — which is exactly the state this section exists to prevent.
608test('★ the update section names the running VERSION, not just tree hashes', (t) => {
609 const html = updateSettings({ ota: true, running: 'aaaa', staged: 'aaaa', version: '0.0.4', builtAt: 1789054155366 })
610 t.ok(html.includes('Running 0.0.4'), 'the version a release page would show')
611
612 // ⚠ An older build predates the stamp. It must say so rather than render a blank or, worse, a
613 // plausible-looking wrong version.
614 const old = updateSettings({ ota: true, running: 'aaaa', staged: 'aaaa' })
615 t.ok(old.includes('predates the stamp'), 'and an unstamped build says why it cannot tell you')
616 t.absent(old.includes('Running undefined'), 'never a fabricated version')
617})