web: rename a database from its settings page A database's name lives in two places — the metadata row and the on-disk store directory its path names — so a rename has to move both. The order is creation's: the row first, where a name already taken is refused by the unique index before anything on disk changes, then the store, then the handle the remotesapi memoized under the old path. A store that will not move rolls the row back, so the two halves never disagree about where a database lives; only a failed rollback is escalated to a human, because it is the one outcome no later request can repair. storage.MoveStore is one os.Rename behind the containment guard DeleteStore already used, extracted here as containedPath. It refuses an occupied destination outright: os.Rename over an empty directory would succeed and swallow it. No redirect is left behind — the old address simply stops resolving, as on git.sr.ht — so an existing clone needs its remote replaced, and a companion of a git repository is re-provisioned under its old name by the next push to that repository. The README's quickstart now says both.
storage: create databases empty so the first push needs no --force Every automatic creation path wrote an "Initialize data repository" commit through WriteEmptyRepo, and that commit is history. dolt decides fast-forward on the client (actions.CanFastForward over the remotesapi), so the server cannot forgive the collision: pushing a database that has a root commit of its own — a beads tracker, anything grown locally — was rejected as a non-fast-forward and could only land with --force. That is the whole reason the companion-database recipe starts with a forced push. push-to-create already provisioned an empty store for this exact reason. Give the other two paths the same default: /internal/repos, whose caller is git.sr.ht's post-update hook and therefore fires before its user has ever pushed, now always provisions empty, and the web form does unless its new "initialize with an empty commit" checkbox is ticked. The checkbox buys what an empty store cannot offer — a database that can be cloned before anything is pushed to it, since dolt refuses a store with no commits as "contains no Dolt data". Which is also why the overview of a database with no branches now teaches push rather than clone: the clone box there quoted a command that could not work. A store that fails to open is deliberately not treated as empty — an unreadable database must not be advertised as a fresh one.
web: answer what is ready across every tracker
web: give bd memories their own view
web: draw the chrome from sr-ht-ecore The brand, the service switcher, the login block, the environment banner and the database listing were a local port of core.sr.ht's nav — one of five such ports on this instance, and they had already drifted. They are now sourcecraft.dev/bigbes/sr-ht-ecore/chrome, the one copy every custom service draws from. Deleted: web/chrome.go entire (navEntry, networkOrder, networkExcluded, buildNetwork, basePage, loginURL, logoutURL), templates/nav.html, templates/icons/circle.svg (ecore inlines the identical SVG), the repoList partial, and the local dict/shortHash duplicates. Added: one chrome.Service built in newApp from our config section with the hashed stylesheet href set on it, a chrome.Page per request through app.page, chrome.Attach on every template set, and chrome.Funcs as the base of the funcmap. Handlers embed chrome.Page in their view structs instead of copying its fields; the row browser sets ContainerClass to container-fluid, since its column count is the table's and not ours. Three behaviour changes come with ecore's policy, all deliberate: the profile link now prefers hub's ~username page when hub.sr.ht is configured (it was always meta's /profile), the brand carries a fixed 15rem min-width so the switcher starts at the same x on every service, and a binary built without a stylesheet renders bare rather than linking an empty href. The nav test went with the code it tested — ordering, exclusions and login URLs are ecore's to cover — and what replaced it asserts only what is ours: that pages are drawn through the chrome at all, and that the row browser is full-bleed. The auth path is untouched: a foreign bearer token is still accepted as a meta.sr.ht PAT.
web: mirror the git twin's description onto companion databases The internal create endpoint accepts a description, but its only caller — dolt-git-hook — never sends one: git.sr.ht's push context does not carry it. Companion databases therefore all sat descriptionless on the dashboard while their git twins had perfectly good descriptions. Resolve the description server-side instead: a GitDescriber dependency (internal GraphQL query to git.sr.ht in the owner's name, the same network-key trust the hook uses to reach us, pointed the other way) is consulted on every /internal/repos call. A fresh companion is created with the twin's description; for an existing one the push doubles as the sync point — a changed, non-empty git description overwrites the stored one. An empty git description never clobbers one set in dolt's own settings, and every failure mode (no twin, git.sr.ht down, no resolver wired) degrades to no mirroring. The lookup is capped at 3s so the hook's own 5s POST timeout is never exceeded. Adds testify as a direct dependency for the new tests.
rename module to sourcecraft.dev/bigbes/sr-ht-dolt; depend on sourcecraft sr-ht-core
web: router, handlers, and sourcehut chrome Chi route table per plan, SourceHut chrome port (nav/layout from core.sr.ht Bootstrap structure), dual-flow clone box, dolt-key association page with hash-fragment prefill, same-origin CSRF checks. Handlers depend on small injected interfaces (StoreManager, RepoStore, BrowseOpener, UserResolver) with production adapters included.