| Comment: | feat(hub): persist .commit publish watermark across hub restarts (#164)
publishedRID was in-memory only. initPublishWatermark snapshotted the current max ci rid on every start, so any commit that landed in the repo but didn't get its .commit notification out before the hub died (crash mid-publish, HTTP push that completed the SQLite write but crashed before the wrapper ran publishNewCommits, or an offline tool writing to the repo while the hub was down) became silently un-propagated. Peers caught up incidentally on the next commit or not at all. Real gap. Persist the watermark to the repo's config table under "edgesync:hub-published-rid" after each successful publish. On startup, initPublishWatermark reads the persisted value first and falls back to the max-rid snapshot only when nothing is persisted (cold start). Schedule a one-shot publishNewCommits call publishCatchupDelay (3s) after NewHub so it runs after NATS leaf clients have had time to reconnect on the same leaf port — an immediate catchup fires into a void because nats-core has no per-subscriber queuing. Stop cancels the pending catchup via catchupStop so the goroutine doesn't outlive the hub. Regression test TestCrossLeaf_HubRestart_RepublishesUnannouncedCommits: two hubs leaf-linked on a stable leaf port; stop A, open the repo file directly via libfossil and commit (no hub, no NATS, no publish hook), restart A; assert B reads the offline file via the delayed startup catchup. The "open repo file directly" trick is morally equivalent to "hub crashed after libfossil's write but before the wrapper's publish" — both produce a commit that's in the repo but unannounced. Known limitations (deferred): - If no peer reconnects within publishCatchupDelay, the catchup publish is still lost. JetStream-durable .commit subject would close the gap; tracked as a follow-up. - The .commit subscriber on the pull side doesn't advance the receiving hub's watermark, so a pulled-in commit will be republished on the receiver's next restart. Idempotent on the far side (the subscriber's pull converges to zero blobs) but wasteful chatter; can be tightened in a follow-up. - v1→v2 upgrade with un-announced backlog at the time of upgrade treats those commits as "already known" — same silent-loss behavior as v1; not made worse. Refs #161 review notes. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
|---|---|
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
00bc87892ba320b6932e442c53fe0877 |
| User & Date: | [email protected] on 2026-05-12 23:50:36.000 |
| Other Links: | manifest | tags |
|
2026-05-13
| ||
| 00:13 |
refactor(agent): drop mergeWithParent now that libfossil v0.6.2 ships the fix (#165)
libfossil#30 / EdgeSync#163 shipped consumer-side and upstream respectively. With libfossil v0.6.2 (PR #32, commit e6d34fb), Repo.Commit merges parent F-cards with supplied Files internally when ParentID > 0 — the exact behavior agent.mergeWithParent was emulating. Drop the duplicate. Repo policy (CLAUDE.md "Upstream-fix policy") is unambiguous: when a fix lands in a repo we own, the workaround in the consuming repo should be removed, not left as defense-in-depth. libfossil is ours; a regression there gets fixed there. Bumps: - root go.mod: v0.6.1 → v0.6.2 - leaf/go.mod: v0.5.0 → v0.6.2 (was lagging; auto-bump workflow has been failing per #146/#158) - bridge/go.mod: v0.5.0 → v0.6.2 (same) Keeps the regression tests TestAgent_Commit_PreservesParentFiles and TestAgent_Commit_SuppliedFileOverridesParent — they assert behavior, not implementation, and now serve as integration coverage so any future libfossil regression on full-tree manifest emission surfaces in EdgeSync CI too. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: fabb4e3453 user: [email protected] tags: trunk | |
|
2026-05-12
| ||
| 23:50 |
feat(hub): persist .commit publish watermark across hub restarts (#164)
publishedRID was in-memory only. initPublishWatermark snapshotted the current max ci rid on every start, so any commit that landed in the repo but didn't get its .commit notification out before the hub died (crash mid-publish, HTTP push that completed the SQLite write but crashed before the wrapper ran publishNewCommits, or an offline tool writing to the repo while the hub was down) became silently un-propagated. Peers caught up incidentally on the next commit or not at all. Real gap. Persist the watermark to the repo's config table under "edgesync:hub-published-rid" after each successful publish. On startup, initPublishWatermark reads the persisted value first and falls back to the max-rid snapshot only when nothing is persisted (cold start). Schedule a one-shot publishNewCommits call publishCatchupDelay (3s) after NewHub so it runs after NATS leaf clients have had time to reconnect on the same leaf port — an immediate catchup fires into a void because nats-core has no per-subscriber queuing. Stop cancels the pending catchup via catchupStop so the goroutine doesn't outlive the hub. Regression test TestCrossLeaf_HubRestart_RepublishesUnannouncedCommits: two hubs leaf-linked on a stable leaf port; stop A, open the repo file directly via libfossil and commit (no hub, no NATS, no publish hook), restart A; assert B reads the offline file via the delayed startup catchup. The "open repo file directly" trick is morally equivalent to "hub crashed after libfossil's write but before the wrapper's publish" — both produce a commit that's in the repo but unannounced. Known limitations (deferred): - If no peer reconnects within publishCatchupDelay, the catchup publish is still lost. JetStream-durable .commit subject would close the gap; tracked as a follow-up. - The .commit subscriber on the pull side doesn't advance the receiving hub's watermark, so a pulled-in commit will be republished on the receiver's next restart. Idempotent on the far side (the subscriber's pull converges to zero blobs) but wasteful chatter; can be tightened in a follow-up. - v1→v2 upgrade with un-announced backlog at the time of upgrade treats those commits as "already known" — same silent-loss behavior as v1; not made worse. Refs #161 review notes. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 00bc87892b user: [email protected] tags: trunk | |
| 23:38 |
fix(agent): preserve parent's tracked files in agent.Commit (#152) (#163)
agent.Commit passed opts.Files straight through to libfossil.Commit, which emits a partial manifest containing only the supplied subset. Parent files not in opts.Files were silently dropped — bones' multi-slot workflow (each slot commits only its own paths on a linear trunk chain) materialised only the last slot's files into git, every prior slot's work lost. Bug chains to libfossil#30, but EdgeSync can shield consumers regardless of upstream's resolution. opts.Files now reads with `fossil ci` semantics — "files this commit changes." A new mergeWithParent helper enumerates the parent's F-cards, overlays supplied files (supplied wins by name), and hands the full set to libfossil.Commit. Inherited files preserve the parent's Perm; supplied files get libfossil's default (agent.FileToCommit has no Perm field today). parentID==0 stays an orphan root. Two regression tests: - TestAgent_Commit_PreservesParentFiles — disjoint paths (alpha, bravo) on a linear chain. Both must surface at trunk's tip. - TestAgent_Commit_SuppliedFileOverridesParent — same-name supplied content must win over parent's. Tests verify against BranchTip + ReadFile directly. a.Read can't be used because libfossil ResolveVersion("trunk") goes through the sym-trunk singleton tag, which libfossil.Commit doesn't propagate forward; only the first commit on trunk is reachable that way. Orthogonal to #152; flagging separately. Closes #152 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 7f0fbfa6ef user: [email protected] tags: trunk | |