Decisions - barocss/barocss-hub GitHub Wiki

Decisions

Format: D-NNN title / context / decision / evidence / date.

D-001 Move vs Delete: move revives

  • Context: A node is deleted on one replica while it is concurrently moved on another (#4, Repository-Semantics §2.5).
  • Decision: Option A. The concurrent move revives the node out of TRASH, and a conflict is recorded so the delete's intent stays visible. A delete that causally follows the move still wins, as usual.
  • Evidence: The Kleppmann et al. move-op convergence proof carries over (Discussion #6). Option B (delete wins) loses that proof. A also fits "never silently discard concurrent intent".
  • Decided by: human (2026-09-26), on the Planner's recommendation.

D-002 Suffix instability: user-chosen names win

  • Context: Projection disambiguates name collisions as name~shortid (Repository-Semantics §1.5, #8/#9). A later sibling may take a name equal to an existing suffixed form.
  • Decision: Option A. User-chosen names always win. The suffixed node falls back to the long form. Names matching the suffix pattern are not rejected.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-003 Name comparison: byte-exact

  • Context: Whether projection should treat names that differ only in case or Unicode normalization as colliding.
  • Decision: Option A. Projection compares names byte-exact. Case and Unicode-normalization clashes are handled at checkout time, per OS, in a future checkout Issue.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-004 User-facing commits: a commit is a named checkpoint

  • Context: Version model v0 (#11, Repository-Semantics §3.4 and the §3.2 "merge commit" wording). Does barocss-hub have a user-facing commit concept, and what does it mean?
  • Decision: Option B. A commit is a checkpoint with a message: a named label {name/message, frontier, author, note} on a version. Ops keep replicating live, and a commit does not make op groups atomic.
  • Rejected: A (checkpoints only, no commit concept). C (commit = atomic transaction), which would require redesigning the op model.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-005 Version ids: frontier-based short ids

  • Context: How versions are identified to users (#11, §3.4).
  • Decision: Option A. Frontier-based version ids are shown to users as short ids, like git short SHAs, alongside checkpoint names.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-006 Checkpoint visibility: private by default

  • Context: Sync model (#14, escalation E-1). Do checkpoints replicate to peers automatically?
  • Decision: Option b. Checkpoints are private by default. Sharing needs an explicit publish step.
  • Decided by: human (2026-09-26), in the Hub Compute session.

D-007 Checkpoint immutability; movable refs are separate

  • Context: Sync model (#14, escalation E-2). Can checkpoints be removed or renamed?
  • Decision: Option c. Checkpoints themselves are never removed or renamed. Movable named refs (like branches) are added as a separate concept.
  • Decided by: human (2026-09-26), in the Hub Compute session.

D-008 Rejected ops are surfaced, naming the peer

  • Context: Sync model (#14, escalation E-3). What happens to ops rejected on receipt and their causally stuck descendants?
  • Decision: Option b. Rejected ops and their stuck descendants show a visible warning naming the peer.
  • Decided by: human (2026-09-26), in the Hub Compute session.

D-009 Refs can be deleted (replicated record)

  • Context: Ref model (#15, E-4).
  • Decision: Refs can be deleted. Deletion is a replicated ref record, so it converges like moves.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-010 New refs are local by default

  • Context: Ref model (#15, E-5).
  • Decision: New refs are local by default. Publishing is explicit, consistent with D-006.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-011 Ref-move conflicts are surfaced

  • Context: Ref model (#15, E-6).
  • Decision: A ref-move conflict is shown as a warning in hub status, like D-008's surfaced warnings.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-012 Published refs are fast-forward only

  • Context: Ref model (#15, E-7).
  • Decision: Published refs allow fast-forward moves only. A move to a version that isn't a descendant of the current target is rejected locally, and history already published isn't dropped.
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-013 Name clash: local ref wins, published as published/<name>

  • Context: Ref model (#15, E-8).
  • Decision: On a name clash the local ref wins for the bare name, and the published ref is addressed as published/<name> (like git's origin/main).
  • Decided by: human (2026-09-26), in the Hub Planner session.

D-014 Edit vs Delete: edit revives

  • Context: File content v0 (#18). A file is deleted on one replica while its content is concurrently edited on another.
  • Decision: Option B. The concurrent content edit revives the trashed file, consistent with D-001. A conflict is recorded so the delete's intent stays visible. The exact behavior of an edit that causally follows the delete is left to the spec.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-015 Recreating a deleted published ref is fast-forward only

  • Context: Ref model (E-9).
  • Decision: A published ref name that was deleted can only be recreated fast-forward from its last value before the delete, so delete-then-recreate can't bypass D-012.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-016 Publishing onto an existing published ref

  • Context: Ref model (E-10).
  • Decision: Publishing local ref n when published/n exists is a fast-forward move of published/n, and a non-FF publish is rejected. The local ref is kept after publishing.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-017 Reserved published/ prefix

  • Context: Ref model (E-11).
  • Decision: Local ref names starting with published/ are invalid.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-018 Fast-forward against all heads during a ref conflict

  • Context: D-012 while a published ref has several concurrent heads.
  • Decision: A move must be fast-forward against ALL heads (its target ⊇ the merge of all head targets), so no published target is silently dropped.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-019 Case clash at checkout: skip only the clashing files

  • Context: Checkout (#19), refining D-003. Names that differ only by case on a case-insensitive filesystem.
  • Decision: Checkout skips only the clashing file(s), marks them "unmaterialized" (shown in status), and materializes the rest. It does not refuse the whole checkout.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-020 Recording requires the working directory at head

  • Context: Checkout (#19). Recording changes from an older checked-out version.
  • Decision: Stays refused. record and mutating commands require the working directory to be at head. Recording from an older version waits for the refs/branch model and is designed together with sync.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-021 CorruptBlob and DuplicateReplica are warning reasons

  • Context: #21.
  • Decision: CorruptBlob and DuplicateReplica are D-008 warning reasons, shown in the same warning surface.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-022 DuplicateReplica stops sync with that replica

  • Context: #21. Two replicas share an id (for example a copied .hub/).
  • Decision: Stop syncing with that replica and tell the user to give it a new replica id. Don't keep accepting its ops.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-023 Reviving a file inside a deleted folder revives the whole folder

  • Context: E-12, following D-014. A concurrent edit revives a file whose parent folder was deleted.
  • Decision: The whole folder is revived (its other children return too), and a conflict is recorded. This matches the current behavior.
  • Decided by: human (2026-09-27), in the Hub Planner session.

D-024 hub reid scope: before recording only

  • Context: #22. Fixing a copied repo that shares a replica id.
  • Decision: hub reid fixes a copied repo only before it has recorded under the shared id. The docs say "reid before recording". There is no re-author tool in v0.
  • Decided by: human (2026-09-27), in the Hub Planner session.
  • Refined by D-049 (#49): ops already recorded under the shared id can now be recovered with barocss-hub reid --rescue (Repository-Semantics 4.13). Plain reid is still "before recording".

D-025 Successful reid clears DuplicateReplica warnings

  • Context: #22, D-022.
  • Decision: A successful reid automatically clears that repo's saved DuplicateReplica warnings. It's implemented behind sync::REID_CLEARS_DUPLICATE_WARNINGS = true.
  • Decided by: human (2026-09-27), in the Hub Planner session.

Roadmap note (2026-09-27, human-approved)

After #24 (.hubignore) comes a local automatic mode, hub watch: detect file changes → auto-record → auto-sync with configured local peers. It comes before network sync and refs.

D-026 Performance targets

  • Context: Scale work (#27, Performance).
  • Decision: Per repo: 100k files, 10 GB total, files up to 1 GB, 1M ops. 100 replicas, 20 concurrent editors. hub status / hub record ≤ 1 s. Sync after 1 week offline ≤ 1 min.
  • Decided by: human (2026-09-28), in the Hub Compute session.

D-027 Spec-side functions may be reimplemented for performance

  • Context: #28. evaluate and causal_past in hub-core are O(n²).
  • Decision: They may be reimplemented for performance, provided observable semantics are unchanged. The old implementation is kept as a test-only oracle with an equivalence proptest.
  • Decided by: human (2026-09-28), in the Hub Compute session.

D-028 develop is not backed up between releases

  • Context: develop exists only locally. Should it be pushed as a backup before core changes?
  • Decision: No. develop is not backed up or pushed between releases; the user accepts the local-loss risk. Pushes happen only at release.
  • Decided by: human (2026-09-28), in the Hub Planner session.

D-029 On-disk format v1: state snapshots + per-batch commit markers

  • Context: 1M-op open/status is still ~5.9 s after #31, above D-026.
  • Decision: One format bump introduces both:
    • State snapshots: open reads the latest valid snapshot plus the ops after it.
    • Per-batch commit markers: an op batch counts only once its commit marker is durable; an incomplete trailing batch is ignored on open.
  • Migration: v0 repos are migrated automatically and crash-safely on first open by a v1 binary. A v0 binary refuses v1 repos with a clear message.
  • Decided by: human (2026-09-29), in the Hub Planner session, as recommended.

D-030 Re-measure 100-replica scale after snapshots

  • Context: The dense version-vector memory question from #28/#30.
  • Decision: After the snapshot work (D-029), re-run the 100-replica scale measurement.
  • Decided by: human (2026-09-29), in the Hub Planner session, as recommended.

D-031 License: MIT

  • Context: Preparing for an alpha release.
  • Decision: MIT. Set license = "MIT" workspace-wide in Cargo.toml, and add a LICENSE file at the repo root.
  • Decided by: human (2026-09-29), in the Hub Planner session.

D-032 Installation as easy as git's

  • Context: Distribution for the alpha.
  • Decision: One standard package-manager command per OS (brew on macOS, apt/deb on Linux, winget on Windows), plus prebuilt binaries and a one-line install script. Users don't need a Rust toolchain. The exact channel list and the timing per stage go in the alpha plan.
  • Open: the binary name (hub collides with GitHub's legacy hub CLI).
  • Decided by: human (2026-09-29), in the Hub Planner session.

D-033 Name: barocss-hub

  • Context: hub collides with GitHub's legacy hub CLI (open item from D-032).
  • Decision: The product, package and binary name is barocss-hub. That applies to the crate, Homebrew, winget, apt and the CLI command, replacing hub.
  • Decided by: human (2026-09-29), in the Hub Planner session.

D-034 Alpha includes a VS Code extension MVP

  • Context: Alpha scope.
  • Decision: The alpha includes a VS Code extension MVP:
    • a status bar (synced, behind, conflicts)
    • a conflict list with a diff of the losing side
    • checkpoints in the Timeline
    • watch start/stop
    • a notice when an incoming change hits a file with unsaved edits
  • Prerequisites (editor core, before the extension): built-in ignore defaults for editor temp files; save-pattern tests for vim, VS Code and JetBrains; --json output.
  • Decided by: human (2026-09-29), in the Hub Planner session.

D-035 Blob pack format deferred

  • Context: #34 (sync blob transfer) and the D-030 100-replica run. Storing one file per blob costs ~140 µs per file create, ~10M files per topology.
  • Decision: A blob pack format (one pack plus index per sync; a format change) is DEFERRED, to be revisited later. #34 keeps one file per blob.
  • Interim (197d86a): only lacking blobs are sent, one barrier per side. Per Repository-Semantics A.2.8, blobs named by a committed op are trusted via one readdir, and only unreferenced files are hash-verified. The R=50 star pass went from 551 s to 296 s.
  • Known gap: the receiver no longer detects bit rot in its own committed blobs at sync time (a decision-proposal in A.2.8).
  • Consequence: D-030's full 100 × 100k star+ring run can't fit in 60 min, so D-030 stays partial until packs exist.
  • Decided by: human (2026-09-29), in the Hub Compute session.

D-036 .hub/ and .hubignore keep their names

  • Context: After the rename to barocss-hub (D-033), should the repository directory and the ignore file be renamed to match?
  • Decision: No. The repository directory stays .hub/ and the ignore file stays .hubignore. There is no format migration. This closes the open item under D-033.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-037 Remote transport: SSH first, HTTP later

  • Context: Moving from local-only sync to remote sync.
  • Decision: The spec defines the §4 message exchange independently of transport and covers both SSH and HTTP. SSH is implemented first, git-like: barocss-hub remote add origin ssh://host/path, then barocss-hub sync origin, with the remote side running barocss-hub serve --stdio. HTTP comes later.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-038 Topology: central replica

  • Context: Remote sync topology.
  • Decision: An always-on server holds a regular barocss-hub repo, and everyone syncs with it (like a git server). P2P is deferred.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-039 Background service ships with remote sync

  • Context: Automatic operation beyond watch.
  • Decision: A background service that starts at login (launchd/systemd) runs watch plus remote sync for registered repos. It ships together with remote sync.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-040 VS Code autoStartWatch defaults to true

  • Context: VS Code extension (D-034), after the real-install test (#41).
  • Decision: The extension's autoStartWatch setting defaults to true.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-041 Peers merge into remotes

  • Context: Remote sync spec (#42, E-R1). Local peers and remotes overlapped.
  • Decision: Peers are merged into remotes. The peer command is removed, and same-machine folders are added with remote add <name> file://…. Existing .hub/peers is migrated automatically.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-042 Bare repo layout is <dir>/.hub/

  • Context: #42, E-R2. Layout of a server-side repo with no working directory.
  • Decision: A bare repo is <dir>/.hub/, the same as a normal repo but without a working directory.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-043 serve --read-only is in the alpha

  • Context: #42, E-R4.
  • Decision: serve --read-only ships in the alpha. It refuses all pushes with forbidden.
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-044 Remote sync is automatic once a remote exists

  • Context: #42, E-R5. Relation to D-039 and D-040.
  • Decision: Remote sync runs automatically whenever a repo has a remote, for both the background service and the VS Code auto-start. Adding a remote is the opt-in.
  • Open: E-R3 (HTTP auth).
  • Decided by: human (2026-09-30), in the Hub Planner session.

D-045 sync exits non-zero when the server rejects ops

  • Context: #45. Rejected ops (D-008) during remote sync.
  • Decision: sync exits with a dedicated non-zero code when the server rejects ops. watch, the background service and VS Code show a warning.
  • Decided by: human (2026-10), see #45.

D-046 Trust store location

  • Context: #45. Where the remote trust store lives.
  • Decision: ~/Library/Application Support/barocss-hub/ on macOS, $XDG_CONFIG_HOME/barocss-hub/ on Linux.
  • Decided by: human (2026-10), see #45.

D-047 file:// remotes are trust-gated

  • Context: #45. Review reproduced an attack through untrusted file:// remotes, reversing the earlier proposal.
  • Decision: file:// remotes are trust-gated. The trust key is the canonical local repo plus the target. Peers migrated by D-041 start untrusted.
  • Decided by: human (2026-10), see #45.

D-048 Product direction B: online-first + CRDT ops + thin clients

  • Context: Product direction review after Anthropic's Scion post (2026-10-13). See #47.
  • Decision: Direction B. The server is the authoritative always-on replica. Clients hold only what they need and send ops in real time. The CRDT/convergence model stays: no merge, no merge queue. Short disconnects are buffered.
  • Rejected: A (offline-first full replicas). C (server-side merge, like Scion).
  • Spec: Online-First (Repository-Semantics §6, #47).
  • Follow-up: spec Issues #47 (architecture) and #48 (text content CRDT, so concurrent edits to the same file merge). #44 (background service) is held for re-scoping under this decision.
  • Decided by: human (2026-10), see #47.

D-049 reid --rescue recovers a copied repo's rejected edits

  • Context: #46 detects a copied repo whose ops under the shared replica id were rejected. Until now it could only exit 3 repeatedly. Refines D-024 ("reid only before recording").
  • Decision: Add barocss-hub reid --rescue. The copied repo takes a fresh replica id and re-authors its lost edits as NEW ops under that id, on top of the server's version. Nothing is lost and the repeated exit 3 stops.
  • Issue: #49 (after #46, before #47/#48).
  • Decided by: human (2026-10-14), in the Hub Planner session.
  • Reconciled: the user first answered differently in the Hub Compute session ("suppress repeat reports only"). After reconciling, they chose both; see D-050. The Compute-session answer is superseded.
  • Spec (#49, decision-proposals for Review): re-author by effect (not op replay); intended tree = the working directory including unrecorded edits, at head; rescue set from saved equivocation warnings and reported rejections, confirmed by one sync when online; counters floored above the removed ops; staged and crash-safe with a backup in .hub/rescue/<ts>/. See Repository-Semantics 4.13.

D-050 Already-reported rejections are not re-reported

  • Context: Under D-045, the same rejected ops would warn and exit 3 on every sync. Decided in addition to D-049.
  • Decision: Local .hub keeps a per-peer memory of already-reported rejected OpIds. A rejection reported once isn't re-warned and doesn't exit 3 again; only NEW rejections are reported.
    • The match is on the exact (OpId, rejected-payload hash, reason), so a new equivocation is never hidden.
    • warnings lists suppressed rejections, and warnings --clear-reported resets them.
    • reid --rescue clears the entries it resolves.
  • Reconciled: this supersedes the earlier Hub Compute session answer ("suppress repeat reports only, no re-author"). The user chose both D-049 and D-050.
  • Issue: #49.
  • Decided by: human (2026-10-14), reconciled in the Hub Planner session.

D-051 Divergence digest: per-author SHA-256 sequence digest

  • Context: #46 R2. Detecting silent divergence of copied repos (D-049) needs a content digest in Have, without breaking D-026.
  • Decision: Full-replica sync uses a per-author SHA-256 sequence digest over the shared down-closure K, in < order, in 16,384-op chunks. It is collision-resistant, so it isn't exposed to Wagner's attack, and it stays within D-026.
  • Reserved: LtHash16 (BLAKE3 XOF elements, n=1024 u16) is implemented but reserved for partial replicas (#47, D_r(B)+D_r(W)). It goes on the wire later, behind its own Hello feature.
  • Not done: no D-026 exception, and no on-disk LtHash cache.
  • New dependency: blake3 =1.8.2 (pinned).
  • Decided by: human (2026-10-14), in the Hub Compute session.

D-052 Online-First escalations E-B1..E-B9

  • Context: Online-First §6.12, spec #47 (direction B, D-048). All decided as recommended.
  • E-B1 HTTP authentication: tokens + SSH-key login. Supersedes the E-R3 open question.
  • E-B2 Offline limit: 100k ops / 1 GiB / 7 days, then refuse new records.
  • E-B3 Cache and sparse defaults: cache_max = min(2 GiB, 10 % free disk); default sparse set = whole tree.
  • E-B4 Full replicas (hydrate, today's clone) remain supported for users, opt-in.
  • E-B5 Exactly one server per repo (no promotable/failover servers).
  • E-B6 No virtual checkout in the alpha; a VS Code FileSystemProvider comes first, later.
  • E-B7 Latency target: p50 ≤ 150 ms / p99 ≤ 300 ms on LAN.
  • E-B8 The HTTP API may not write without a base.
  • E-B9 API history answers show the principal.
  • Decided by: human (2026-10-15), in the Hub Planner session.

D-053 Text-CRDT escalations E-T1..E-T6, E-T8 (E-T7 held)

  • Context: Text-CRDT §7.14, spec #48. All decided as recommended.
  • E-T1 Line-level text CRDT in the alpha.
  • E-T2 concurrent-edit notice on by default; identical concurrent edits are not flagged.
  • E-T3 Overrides via .hubattributes with text / binary.
  • E-T4 Text size cap 8 MiB, longest-line cap 64 KiB; hard cap 64 MiB for forced text.
  • E-T5 Lazy one-way format v2 upgrade at the first text op; pre-#48 binaries then refuse the repo. Meets the #33 bar.
  • E-T6 A server with text ops refuses clients without text (upgrade-required), including their pushes.
  • E-T8 No line-ending normalization; eol=lf available via attributes.
  • E-T7: held here, then decided in D-056.
  • Decided by: human (2026-10-15), in the Hub Planner session.

D-054 file:// client↔client remotes stay; guidance routes through the server

  • Context: Online-First §6.8 / open question for #51, under direction B.
  • Decision: file:// remotes between clients remain supported. When a server exists, the guidance (docs, hints) routes users through the server.
  • Decided by: human (2026-10-15), in the Hub Planner session.

D-055 Repository-Semantics §1.2 amendment: per-edit text ops

  • Context: Text-CRDT proposed amending Repository-Semantics §1.2 (content granularity).
  • Decision: Accepted. Text files are edited per edit with a separate TextEdit op. Everything else stays whole-file SetContent.
  • Decided by: human (2026-10-15), in the Hub Planner session.

D-056 E-T7: dirty text files are recorded first, then merged

  • Context: Text-CRDT §7.5.3 / §7.14 E-T7, held in D-053 because answers conflicted across sessions. Reconciled by the user: as the spec proposes.
  • Decision: checkout / watch first record a dirty text file as a TextEdit against V_wd, then materialize the merged result.
    • The dirty bytes are durable ops before anything is written; the write is the usual atomic replace.
    • Flip exception: if the file's head epoch is bytes or the dirty content isn't text, nothing is auto-recorded and checkout refuses as today. Bytes keep the #19/#26 rule.
  • Acceptance: the implementation must pass kill -9 at every step with no edit loss (the #33 bar).
  • Decided by: human (2026-10-15), reconciled in the Hub Planner session.
⚠️ **GitHub.com Fallback** ⚠️