Merge remote-tracking branch 'origin/master' into release-0.3.5

This commit is contained in:
igneum-labs 2026-10-04 21:35:05 +00:00
commit 7cd5f39b30
2 changed files with 31 additions and 2 deletions

View file

@ -23,6 +23,7 @@ shard run reported as exit 0, 7a7e873).
| 4 Oct 2026 | PC 2 came back on 0.3.4 (the canary) and its card's OTA state read "none" | the OTA state parsed only `OTA: …` lines, and the install lines sit in the previous run's log file; the new run only says `update check: 0.3.4 is current (manifest 0.3.4)` | 797a844 then 2nd commit: every update line the app logs (`is available: downloading`, `downloaded and verified`, `is ready; it installs at the next safe moment`, `installing`, `update to X complete`, `is marked failed`, `has no build yet`, `is current`) is parsed and the newest decides the state; the Mac's card had read "0.3.3 is current" from a 70-minute-old check while 0.3.4 was downloaded, verified and staged | test: staged beats the older check, a finished run reads current, a newer OTA line wins, failed and updated; live cards after the deploy |
| 4 Oct 2026 | the live 0.3.3 Mac bundle's `Info.plist` said 0.3.2 while the engine reported 0.3.3 (Finder and Get Info showed the wrong version) | the 0.3.3 cut was by hand and the plist was not bumped | nothing to do: 0.3.4 was cut by `tools/ship-app.mjs`, which bumps the six version files, and its plist says 0.3.4 | `PlistBuddy` on the staged 0.3.4 bundle |
| 4 Oct 2026 | 0.3.4 reports `igneumd/2.1.0-73fc2fd` on Windows and `-1b65132` on the Macs; neither is a fork commit (both are igneum-repo commits), so the suffix says nothing about the node source | the fork's `build-info/build.rs` looks for a `.git` directory; a worktree's `.git` is a file, so the walk climbs into the vendoring igneum repository and embeds its HEAD; then it reads `.git/HEAD` as a path, which a worktree lacks | fork branch `bugfix-build-info-worktree` (vendor/igneum-node-bughunt, commits 92e1b030 and its follow-up): the root is any `.git`, the HEAD file comes from `git rev-parse --absolute-git-dir`, the ref from `--git-common-dir`; for the main session to merge into devnet-v4 and finality-fixes (the fork has no remote) | `cargo build -p kaspa-build-info` in the fixed worktree embeds the fork's `92e1b030` and watches the worktree's HEAD; the unfixed devnet-v4 worktree embeds the outer repo's `ebce665` and watches the outer repo's files |
| 4 Oct 2026 | PC 1's card went from "installing 0.3.4" to "none" while the install was still stuck (the Windows helper bug, 0d123b3) | the app uploads at most the last 256 KiB of its log (262,144 chars per upload on PC 1) and a PC logs every block, so an update line leaves the parsed tail in about 30 min | 699d0d8: `console_ota_memo` keeps the last OTA state per machine and app run, written when a parse finds one and read back for the same run when the tail has none; a relaunch starts clean (0855c44 widened the SQL window first, which cannot help against the upload cap) | after the deploy all five machines carry memo rows and the cards read installing (PC 1), staged (PC 37ba0461), updated (the rest) |
## Open

View file

@ -55,10 +55,37 @@ function parseNodeTail(tail) {
n.last_warning = errs.length ? errs[errs.length - 1].replace(/^\S+ \S+ /, '').slice(0, 200) : null;
return n;
}
// The app uploads at most the last 256 KiB of its log, so an update line scrolls out of the tail in about half an hour
// on a PC that logs every block (4 October 2026: PC 1's "installing 0.3.4" left its card while the install was still
// stuck). The last OTA state seen per machine and app run is kept in console_ota_memo and read back, for the same run,
// when the tail has none; a new run (the app relaunched) starts clean.
async function otaMemo(sql, groups) {
try {
await sql(`CREATE TABLE IF NOT EXISTS console_ota_memo (id8 text PRIMARY KEY, run_id text NOT NULL, ota jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now())`);
const live = [...groups.values()].filter(g => g.app && !g.id.startsWith('legacy:'));
if (!live.length) return;
const rows = await sql(`SELECT id8, run_id, ota FROM console_ota_memo WHERE id8 = ANY($1)`, [live.map(g => g.id)]);
const memo = new Map(rows.map(r => [r.id8, r]));
for (const g of live) {
const m = memo.get(g.id);
if (g.app.ota) {
if (!m || m.run_id !== g.app.run_id || JSON.stringify(m.ota) !== JSON.stringify(g.app.ota)) {
await sql(`INSERT INTO console_ota_memo (id8, run_id, ota, updated_at) VALUES ($1, $2, $3::jsonb, now())
ON CONFLICT (id8) DO UPDATE SET run_id = EXCLUDED.run_id, ota = EXCLUDED.ota, updated_at = now()`, [g.id, g.app.run_id, JSON.stringify(g.app.ota)]);
}
} else if (m && m.run_id === g.app.run_id && m.ota) {
g.app.ota = { ...m.ota, remembered: true };
}
}
} catch (e) { /* a convenience: the cards render without it */ }
}
async function machines(sql) {
// node logs get a longer tail: the connection-manager (peers) line is rare on a busy node
// the app log's tail is wide (400 KB, about three hours on a PC that logs every block) so a stuck update's lines do
// not scroll out of the card: on 4 October 2026 PC 1's "installing 0.3.4" left the 60 KB window after 30 minutes
const rows = await sql(`SELECT DISTINCT ON (label) label, machine, run_id, received_at, left(lines, 300) AS head,
right(lines, CASE WHEN label LIKE 'nodelog-%' THEN 80000 WHEN label ~ '^(win|mac)-[0-9a-f]{8}$' THEN 60000 ELSE 20000 END) AS tail
right(lines, CASE WHEN label LIKE 'nodelog-%' THEN 80000 WHEN label ~ '^(win|mac)-[0-9a-f]{8}$' THEN 400000 ELSE 20000 END) AS tail
FROM miner_logs WHERE received_at > now() - interval '36 hours' ORDER BY label, received_at DESC`);
const groups = new Map();
for (const r of rows) {
@ -73,7 +100,7 @@ async function machines(sql) {
g.labels.push({ label: r.label, at, run_id: r.run_id });
if (p.stream === 'node') { g.node = { ...parseNodeTail(r.tail), at, run_id: r.run_id }; }
else if (p.stream === 'miner') g.cards.push({ label: r.label.replace(/^miner-/, ''), vendor: p.vendor, card: p.card, at, ...parseMinerTail(r.tail) });
else if (p.stream === 'app') g.app = { at, ...parseAppTail(r.tail) };
else if (p.stream === 'app') g.app = { at, run_id: r.run_id, ...parseAppTail(r.tail) };
groups.set(p.id, g);
}
// node version lives in the first upload of a run (the tail may have lost the banner): one cheap query for the current runs
@ -86,6 +113,7 @@ async function machines(sql) {
const hd = parseHeader(h.head); if (hd && !g.header) g.header = hd;
}
}
await otaMemo(sql, groups);
const now = Date.now();
const out = [...groups.values()].map(g => {
const id8 = g.id.startsWith('legacy:') ? null : g.id;