gabriel / musehub public
feat BREAKING feat/muse53-symlog-remote-sharing-server #1 / 1
AI Agent gabriel · 13 hours ago · Oct 22, 2026 · Diff

feat: muse#53 item 4 server side — musehub_symlog_entries

Server-side companion to the muse-repo commit implementing remote symlog sharing. Mirrors musehub_reflog_entries (musehub side of muse#54 item 4), with one structural difference: symlog is per-symbol, not per-branch, so a single push batches every changed symbol's entries, and the table's content-hash entry_id includes symbol_addr rather than branch.

- New musehub_symlog_entries table (migration 0081): entry_id is a content hash of every field (repo_id, symbol_addr, old_content_id, new_content_id, commit_id, timestamp, author, operation). Entries are immutable history, so storage is pure ON CONFLICT DO NOTHING append-only, same as musehub_reflog_entries. ix_musehub_symlog_ entries_repo_symbol composite index since every read is scoped to one symbol's journal. - musehub/services/musehub_symlog.py: store_symlog_entries() (takes entries_by_symbol: dict[symbol_addr, list[entry]] for the batched multi-symbol push, unlike reflog's single-branch call) / list_symlog_entries() (returns dict[symbol_addr, list[entry]] grouped by symbol, unlike reflog's flat per-branch list). - wire_push_symlog (POST /{owner}/{slug}/symlog) / wire_get_symlog (GET, same path) on the existing bare-path wire.py router. No branch query param (unlike reflog's routes) since symlog has no branch scope; no force flag on push for the same reason reflog doesn't have one — nothing to conflict, entries are immutable. - test_migrations.py: _ALL_REVISIONS/_HEAD bumped to include 0081.

Covered by tests/test_musehub_symlog_service.py (7 cases: round-trip, re-push dedup via content-hash entry_id, batched multi-symbol push in one call, per-symbol isolation, oldest-first ordering within a symbol, empty-batch no-op, missing-commit_id skipped silently). Full suite run: 87 tests (symlog service + reflog service + version-tags service + migrations + schema-check) all passing, confirming migration 0081 applies cleanly on top of 0080 and the ORM model matches the DB schema exactly. Containers restarted and verified clean boot (schema check passed, 75 tables; alembic current = 0081 head) before live end-to-end verification from the muse-repo side: pushed a symbol's journal with --symlog to a scratch repo on the real local hub, confirmed idempotent re-push, pulled into a fresh clone and confirmed the local journal received the correct entry.

Co-Authored-By: Claude Sonnet 5 <[email protected]>

sha256:4fba330283cca0a6bdd067f2c0ad014062fcfda7e98b19ce8cd1e20290252b08 sha
+40 ~3 symbols
6 changed · 1097 in snapshot files
sha256:a6e32ba33efa4fd86f6d82298fca9b954c30cc846090d8c12185ac409241e00c snapshot
+40
symbols added
~3
symbols modified
6
files changed
1097
files in snapshot
0
dead code introduced
Semantic Changes 43 symbols
+ branch_labels variable variable branch_labels L24–24
+ depends_on variable variable depends_on L25–25
+ down_revision variable variable down_revision L23–23
+ downgrade function function downgrade L47–50
+ Sequence import import Sequence L15–15
+ Union import import Union L15–15
+ annotations import import annotations L13–13
+ op import import op L17–17
+ sa import import sa L18–18
+ revision variable variable revision L22–22
+ upgrade function function upgrade L28–44
~ musehub/services/musehub_symlog.py .py 12 symbols added
+ compute_symlog_entry_id function function compute_symlog_entry_id L35–56
+ AsyncSession import import AsyncSession L28–28
+ MusehubSymlogEntry import import MusehubSymlogEntry L30–30
+ datetime import import datetime L24–24
+ hashlib import import hashlib L22–22
+ logging import import logging L23–23
+ pg_insert import import pg_insert L27–27
+ select import import select L26–26
+ timezone import import timezone L24–24
+ list_symlog_entries function async_function list_symlog_entries L143–173
+ logger variable variable logger L32–32
+ store_symlog_entries function async_function store_symlog_entries L59–140
~ tests/test_musehub_symlog_service.py .py 14 symbols added
+ _entry_dict function function _entry_dict L17–27
+ AsyncSession import import AsyncSession L11–11
+ annotations import import annotations L7–7
+ create_repo import import create_repo L14–14
+ datetime import import datetime L9–9
+ symlog_svc import import symlog_svc L13–13
+ timezone import import timezone L9–9
+ test_batched_multi_symbol_push function async_function test_batched_multi_symbol_push L71–85
+ test_empty_batch_is_noop function async_function test_empty_batch_is_noop L125–130
+ test_entries_isolated_per_symbol function async_function test_entries_isolated_per_symbol L88–104
+ test_entry_missing_commit_id_is_skipped_silently function async_function test_entry_missing_commit_id_is_skipped_silently L133–144
+ test_list_ordered_oldest_first_within_symbol function async_function test_list_ordered_oldest_first_within_symbol L107–122
+ test_repush_same_entry_is_skipped function async_function test_repush_same_entry_is_skipped L49–68
+ test_store_and_list_round_trip function async_function test_store_and_list_round_trip L30–46
~ musehub/api/routes/wire.py .py 2 symbols added
+ wire_get_symlog function async_function wire_get_symlog L1253–1276
+ wire_push_symlog function async_function wire_push_symlog L1187–1244
~ musehub/db/musehub_repo_models.py .py 1 symbol added, 1 symbol modified
+ MusehubSymlogEntry class class MusehubSymlogEntry L608–642
~ tests/test_migrations.py .py 2 symbols modified
Files Changed
+3 ~3
1097 in snapshot
← Older Oldest on feat/muse53-symlog-remote-sharing-server
All commits
Newer → Latest on feat/muse53-symlog-remote-sharing-server

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:4fba330283cca0a6bdd067f2c0ad014062fcfda7e98b19ce8cd1e20290252b08 --body "your comment"