chore: refactor backend around domain types #26

Merged
reya merged 9 commits from audit into master 2026-10-03 10:35:50 +00:00
4 changed files with 9 additions and 296 deletions
Showing only changes of commit f12feeb196 - Show all commits
+6
View File
@@ -8,10 +8,16 @@
### Changed ### Changed
- Surface backend errors as error notifications in the workspace instead of silently dropping them
- Break ties in repository activity lists by event id, so same-second events order deterministically
- Restructure the backend around domain types: git operations behind a `Repo` type, the grasp push pipeline behind `GraspPush`, nostr connectivity behind `NostrBackend`, and shared helpers consolidated into `utils`
### Fixed ### Fixed
### Removed ### Removed
- Remove dead code: the unused `login`/`logout` family, the unwired `SyncProgress` pipeline, `merge_pull_request`, inbox mark-read/archive APIs and the wasm32-only code paths
### Deprecated ### Deprecated
## v0.2.0-alpha - 2026/09/27 ## v0.2.0-alpha - 2026/09/27
+3 -15
View File
@@ -47,19 +47,10 @@ impl RepoStatus {
} }
} }
// NIP-10 and NIP-34 use the lowercase `e` tag; NIP-22 comments, kind `1111`,
// use the uppercase `E` tag for the thread root.
pub fn references_root(event: &Event, root: &EventId) -> bool {
let root = root.to_hex();
event
.tags
.iter()
.any(|tag| matches!(tag.kind(), "e" | "E") && tag.content() == Some(root.as_str()))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::GitEvent;
const ROOT_ID_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111"; const ROOT_ID_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111";
const OTHER_ID_HEX: &str = "2222222222222222222222222222222222222222222222222222222222222222"; const OTHER_ID_HEX: &str = "2222222222222222222222222222222222222222222222222222222222222222";
@@ -90,11 +81,8 @@ mod tests {
)) ))
.expect("signed event"); .expect("signed event");
assert!(references_root(&event, &root)); assert!(event.references_root(&root));
assert!(!references_root( assert!(!event.references_root(&EventId::from_hex(OTHER_ID_HEX).expect("valid id")));
&event,
&EventId::from_hex(OTHER_ID_HEX).expect("valid id")
));
} }
#[test] #[test]
-264
View File
@@ -1,264 +0,0 @@
# Backend refactor plan
Branch: `audit` @ `6c4cc9f`. Baseline: `cargo check --workspace` clean (~10s incremental), `cargo test --workspace` 111/111 passing.
Scope: `signed_core`, `signed_git`, `signed_nostr`, `signed_state`, `utils`, `paths`, `settings` (~13,000 lines). `workspace`, `signed_ui`, `dock`, `desktop` are touched only as call sites. `.delta/` is ignored throughout.
## Goals
1. Behavior lives in `impl` blocks on a named domain type. No free functions in domain crates.
2. Free functions are allowed only in `utils` (and `paths`, which is the same kind of crate) — pure, stateless helpers.
3. Comments only where they carry non-obvious "why": protocol rules, safety invariants, races, ordering guarantees. No summaries, no field-name restatements.
4. Tests only for protocol interop and user-visible contracts. Delete tests that lock internal structure.
Everything below is behavior-preserving except the explicit deletions in Phase 0.
---
## 1. Audit summary
| Crate | Lines | Free fns (non-test) | Impl blocks | Comment lines | Tests |
|---|---|---|---|---|---|
| `signed_core` | 2,226 | 47 | 6 | ~216 | 29 |
| `signed_git` | 3,743 | 52 | 3 | ~276 | 38 |
| `signed_nostr` | 257 | 3 | 1 | ~6 | 0 |
| `signed_state` | 6,206 | 53 | ~20 | ~460 | 25 |
| `utils` | 98 | 4 | 0 | ~3 | 3 |
| `paths` | 105 | 8 | 0 | ~26 | 0 |
| `settings` | 369 | 1 | 4 | ~44 | 5 |
### Structural findings
- **`signed_git` is 52 free functions and 3 impl blocks.** `gix::open` is called 29×; nearly every entry point re-opens the repository. Eight near-duplicate `worktree_*`/`repo_*` pairs exist only to bridge `&Path` ↔ `&gix::Repository` (`worktree_all_commits`/`all_commits`, `worktree_branches`/`repo_branches`, `worktree_ref_state`/`repo_ref_state`, `worktree_current_branch`/`current_branch`, `head_commit_id`/`head_commit`, …). The `worktree_` prefix means two different things (path-based vs `&gix::Repository`-based). `GitCache` is the one type done right — it is the template for the refactor.
- **`signed_state/backend.rs` (2,020 lines) is a god object plus 24 free helpers.** `create_repository` (L370–546) and `publish_local_repo` (L549–722) duplicate ~120 lines of validation/announce/push/retract/fan-out; `push_repo_from` (L752) is already the factored third variant. The grasp push loop (`push_staged_to_grasps` + 6 helpers) and the grasp URL family float freely. `Backend::connect_repo_relays` (method) calls a free fn of the same name (L1375).
- **`signed_core` operates on foreign types.** 47 free functions take `&Event`/`&RepoAddr`-alias first args or are module-bound constructors; inherent impls are impossible without introducing types. `RepoAddr` is a bare alias for the SDK's `Coordinate`, so it can carry no methods.
- **`signed_nostr::new_backend` returns a tuple** `(Client, UniversalSigner)` whose halves are always created and consumed together.
- Duplication across crates: three middle-truncation helpers (`utils` private `truncate_middle`, `signed_ui::middle_truncate`, `workspace::truncate_label`), two hand-rolled mbox envelope parsers, three git-CLI spawn patterns, and `workspace/src/views/repo/mod.rs:1955` re-assembling by hand what `worktree_snapshot` should provide.
### Dead code (grep-verified, zero callers outside own crate)
| Item | Location | Note |
|---|---|---|
| `login`, `login_with_new_identity`, `login_with_nsec`, `login_with_bunker`, `logout`, `with_master_key` | backend.rs L917–1025, L1451 | No UI path calls these; `import_dialog::open` is an empty stub (TODO.md confirms) |
| `BackendEvent::SyncProgress` pipeline (variant, `sync_progress` field + accessor, progress task) | backend.rs L43, L64, L1104, L1191–1263 | No subscriber; TODO.md retains it for an unbuilt indicator |
| `BackendEvent::Error` | backend.rs L47 | Emitted 9×, subscribed 0× — errors currently vanish |
| `RepoStore::merge_pull_request` + `publish_applied_status` | repo.rs L1195, L1499 | Zero callers |
| `Inbox::mark_read` / `mark_archived` | inbox.rs L27, L43 | Zero callers |
| `InboxItem::kind` | signed_core/inbox.rs L45 | View reads `root_event.kind` directly |
| `filters::notification_comments`, `filters::NOTIFICATION_KINDS`, `inbox::notification_root` re-exports | signed_core | Internal-only despite `pub` + root re-export |
| `paths::{home_dir, config_dir, data_dir}` pub-ness | paths/lib.rs | Internal-only |
| `local_repo_addr` re-export | signed_state/lib.rs L19 | Internal-only |
| wasm32 code paths (`new_backend` wasm variant, `init` wasm variant, `cfg!` guards, `nostr-memory` dep) | signed_nostr/backend.rs L25, signed_state/lib.rs L65, backend.rs L174, checkouts.rs L103/136/162 | No wasm target exists in the workspace |
| Unused `signed_core/lib.rs` root re-exports | lib.rs L11–14 | Callers use `filters::`/`inbox::` module paths |
### Speculative generality
- `U: AsRef<str>` generics on `clone_repo`, `fetch_repo_refs`, `GitCache::ensure_clone` — every call site passes `&str`/`String`.
- `GraspSignals` exposes 10 bools; consumers read 4.
- `RepoStore::set_status` and `reply` are pub but only used internally (draft-PR flow / `comment`).
---
## 2. Target types
New or promoted types that absorb the free functions. Each row is a home, not a new abstraction layer — they are mostly newtypes over data the code already passes around.
| Crate | Type | Absorbs |
|---|---|---|
| `signed_core` | `RepoAddr` (newtype over `Coordinate`, replacing the alias) | `repo_addr` → `RepoAddr::new`; `identifier_from_name` → `RepoAddr::identifier_from_name` (NIP-34 identifier policy stays here, not utils); enables `addr.announcement_filter()` etc. |
| `signed_core` | `PullRequest<'a>(&'a Event)` | `pull_request_patches`, `pull_request_patch`, `latest_update`, private `forward_series`, `patch_produces_commit` |
| `signed_core` | `trait Nip34Event` (extension on `Event`) | `current_commit_of`, `merge_base_of`, `clone_urls_of`, `branch_name_of`, `activity_subject`, `references_root`, `is_git_activity` — needed because the same accessors apply to PR roots *and* updates, where `PullRequest` can't go |
| `signed_core` | `RepoState { refs, head }` | `build_state` → `RepoState::builder(id)`, `parse_state` → `RepoState::from_event`; kills the anonymous tuple return |
| `signed_core` | `ThreadResolver<'a>` (wraps the event lookup closure) | `resolve_thread_root`, `parent_id`, `nip10_root_id`, `first_e_id`, `first_uppercase_e_id`, `first_tag_id`, `e_tag_with_marker`, `notification_root` |
| `signed_core` | `Filters` (unit struct) | the 12 filter constructors (`announcement`, `state`, `activity`, `statuses_for`, `grasp_list`, `comments_for`, `notifications`, `authored_activity`, `all_announcements`, `all_states`, `deletions`, `deletions_for_repo`) — repo-scoped ones become `RepoAddr` methods |
| `signed_core` | `Announcement::forks_in` (assoc fn) | `fork_candidates` |
| `signed_nostr` | `NostrBackend { client, signer }` | `new_backend` → `NostrBackend::open(path)`; private `with_database` assoc fn; kills the tuple |
| `signed_git` | `Repo` (wrapper over `gix::Repository`) | ~46 of the 52 free functions — see Appendix A. `Repo::open`, `Repo::open_cached` (object cache for history walks), `Repo::init`, `Repo::clone`. One `gix::open` per operation instead of 29 scattered calls. The `worktree_*`/`repo_*` pairs collapse |
| `signed_git` | `GitCache` (existing) | unchanged except `open`/`ensure_clone` return `Repo` |
| `signed_state` | `GraspServer<'a>(&'a RelayUrl)` | `grasp_base_url`, `grasp_clone_url`, `grasp06_prs_url`, `pr_clone_urls`, `grasp_list_servers`, `latest_grasp_list_servers`, `user_grasp_list_servers` |
| `signed_state` | `GraspPush { client, signer, executor }` | `push_staged_to_grasps`, `sign_state_event`, `stage_event_on_relay`, `keep_newest`, `is_transient_grasp_denial`, `is_stale_advertisement_race` (→ `PushRejection::classify`), `PushOutcome` — moved to a new `push.rs` |
| `signed_state` | `BunkerCredential { uri, session_key }` | `extract_master_key`, `with_master_key` (if the bunker login path survives Phase 0 — otherwise deleted) |
| `signed_state` | `StatusIndex(HashMap<EventId, RepoStatus>)` | `resolve_statuses`, `status_of`; `RepoStore::status_of` reads through it |
| `signed_state` | `PatchSeries` | `patch_current_commit`, `split_patch_series` consumers in repo.rs |
| `signed_state` | `Mirrors` (unit struct over the `OnceLock<GitCache>`) | the 6 free fns in `git_store.rs` |
| `signed_state` | methods on existing types | `query_inbox`/`load_state`/`save_state`/`fetch_notifications`/`event_references` → `Inbox`; `pr_proposes_checkout`, `resolve_associations`, `checkout_status`, `checkout_push_status`, `compute_statuses` → `CheckoutsStore` assoc fns; `resolve_local_repos` → `LocalReposStore`; `next_update` → `impl UpdateEvent`; `retract_event`, `publish_best_effort`, `ensure_bootstrap_relays`, `subscribe_bootstrap_only`, `sync_bootstrap_only` → `Backend`; `publish_patch_series`, `comment_builder` → `RepoStore`; `url_identity`, `same_repo_url` → `utils` |
| `utils` | consolidation | one `middle_truncate` (absorbing 3 copies — `signed_ui` gains a `utils` dep), `flatten_whitespace`, `url_identity`/`same_repo_url`, `latest`, `sort_newest_first`/`sort_oldest_first` |
`settings` needs nothing: it is already plain data + one store. `default_scan_paths` folds into the `Default` impl.
---
## 3. Phases
Each phase ends with `cargo check --workspace && cargo test --workspace` green and its own commit, so review and bisection stay cheap. Call-site updates in `workspace`/`signed_ui`/`desktop` happen in the same commit as the API change.
### Phase 0 — Delete dead code and non-critical tests
The cheapest phase; shrinks every later diff.
1. Delete the `login`/`logout` family + `with_master_key` (backend.rs). Either delete the empty `import_dialog` stub and its menu entry, or leave the stub — **decision D1**.
2. Delete the `SyncProgress` pipeline (variant, field, accessor, progress task). TODO.md's planned indicator can re-add from git history when it is actually built — **decision D2, default delete**.
3. `BackendEvent::Error`: 9 emit sites, 0 subscribers. Either wire one subscriber in `workspace` (a banner/toast, ~15 lines, makes existing error emissions user-visible) or delete variant + emissions — **decision D3, default: wire the subscriber** (AGENTS.md requires errors reach the UI; the emissions already exist).
4. Delete `merge_pull_request` + `publish_applied_status` (recoverable from history if the merge UI is built) — **decision D4, default delete**.
5. Delete `Inbox::mark_read`/`mark_archived`, `InboxItem::kind`.
6. Demote to private: `RepoStore::set_status`, `RepoStore::reply`, `Backend::restore_session`, `Backend::set_signer`.
7. Privatize `filters::notification_comments`, `NOTIFICATION_KINDS`, `inbox::notification_root`, `paths::{home_dir, config_dir, data_dir}`; drop unused `signed_core`/`signed_state` root re-exports (`local_repo_addr`, `GraspSignals` path, etc.).
8. Remove wasm32 paths + `nostr-memory` target dep — **decision D5, default remove** (no wasm target exists). Revert is trivial if a web build materializes.
9. Replace `U: AsRef<str>` generics with concrete `&[String]`/`&str` signatures.
10. **Delete all NON-CRITICAL tests** (list in §5). Doing this before the structural phases means the structure changes never touch them.
### Phase 1 — `utils` consolidation
- Add `middle_truncate`; delete `signed_ui`'s private copy and `workspace::truncate_label`; `shorten_pubkey` calls it.
- Move in: `flatten_whitespace`, `url_identity`, `same_repo_url`, `latest`, `sort_newest_first`, `sort_oldest_first`.
- These are drop-in; domain crates already depend on `utils`.
### Phase 2 — `signed_core`
Dependency bottom, consumed by everything else.
1. `RepoAddr`: alias → newtype with `new`, `identifier_from_name`, `Display`/`From`/`FromStr` delegation. Mechanical fallout across all crates (~30 sites).
2. `Nip34Event` extension trait for the tag accessors; `PullRequest<'a>` newtype; `Announcement::forks_in`.
3. `RepoState` (replaces the tuple return; one consumer in `signed_state/repo.rs` refresh).
4. `ThreadResolver<'a>` absorbing the seven inbox/threading helpers.
5. `Filters` unit struct + repo-scoped filters as `RepoAddr` methods; `RepoStatus::resolve` assoc fn.
### Phase 3 — `signed_nostr`
`NostrBackend::open` / `in_memory`; `signed_state::init` constructs it. ~30 lines.
### Phase 4 — `signed_git` → `Repo`
The biggest mechanical phase.
1. Introduce `Repo` wrapping `gix::Repository` with `open`, `open_cached`, `init`, `clone`.
2. Move every function from Appendix A into methods; collapse the 8 duplicate pairs; the `worktree_` prefix disappears (`repo.dirty()`, `repo.snapshot()`, `repo.branches()` …).
3. Fold remote operations into methods (`push_ref`, `push_all`, `fetch_refs`, `origin_url`, `ensure_origin`, `set_origin`, `remote_has_refs`); one git-CLI spawn helper instead of three patterns.
4. One mbox envelope parser shared by `split_patch_series`/`patch_commits` (stays in `signed_git` as an impl on the patch parser, not utils — it is not generic).
5. `GitCache::open`/`ensure_clone` return `Repo`.
6. Extend `Repo::snapshot()` with branches/tags/current_branch so `workspace/src/views/repo/mod.rs:1955` (`load_repo_data`) collapses into it.
7. Keep pure string parsing (`sanitize_path_component`, `fork_namespace`, `split_patch_series`, `patch_diffs`, `patch_commits`) as assoc fns on the owning type (`Repo::sanitize_path_component`, `PatchParser::…`) or move to `utils` if they carry no git semantics — Appendix A marks each.
### Phase 5 — `signed_state`
1. New `push.rs`: `GraspServer` newtype + `GraspPush` + `PushOutcome`/`PushRejection`, moving ~400 lines out of `backend.rs`.
2. Factor the shared announce→authorize-push→retract-on-failure→fan-out orchestration out of `create_repository`/`publish_local_repo`, using `push_repo_from` as the template (~120 lines saved). Add `publish_one` (sign → send → `require_relay_accepted`, returning the `Event`) and collapse its 3 open-coded copies in repo.rs; merge the shadowed `connect_repo_relays` pair.
3. `StatusIndex`, `PatchSeries`, `Mirrors`; `Inbox`/`CheckoutsStore`/`LocalReposStore`/`Backend`/`RepoStore` method moves per §2.
4. Fold `push_repository`/`push_checkout` wrapper bodies into one helper.
5. Consider whether `backend.rs` (~2,020 → ~1,200 lines) still warrants a file split after the push extraction; stop there — the remaining methods are genuinely `Backend`'s job.
### Phase 6 — Comment stripping
Apply §4 across all backend crates. Mechanical; do it last so it never masks a behavioral diff.
### Phase 7 — Final sweep
- `cargo fmt`, `cargo clippy --workspace`, full test run.
- Invariant grep: `grep -rn '^\(pub \)\?\(async \)\?fn ' crates/{signed_core,signed_git,signed_nostr,signed_state}/src` returns only `lib.rs` `init` and test helpers.
- Update `docs/TODO.md` (both entries are consumed by this plan) and this document's status.
---
## 4. Comment policy
Delete by default. Keep only (one line each, unless truly needed):
- **Protocol/format rules**: NIP-34 `u`-tag parsing workaround (model.rs L311 — the SDK's `Nip34Tag` doesn't model the `u` tag); `subject`-tag fallback; `refs/heads` publication form in state events; NIP-10 `e` vs NIP-22 uppercase `E` semantics; kind→root mapping table in inbox.rs L115–124; `comments_for` returning two filters because `#E`+`#e` would AND (filters.rs L109–112); ngit URL parity notes (`grasp_base_url` scheme mapping, `/prs/` namespace, `clone` tag ordering).
- **Safety invariants**: `sanitize_path_component` untrusted-input contract; fast-forward-only branch updates; `checkout_push_status` never fetching checked-out refs.
- **Races / retry taxonomy**: grasp denial classification (backend.rs L1620–1638 — the single most valuable comment in the codebase); stale-advertisement race; same-second resend dedup on `sign_state_event`; convergence probe rationale.
- **Ordering guarantees**: `commits_since` oldest-first (`git am` order); connect-relays-before-send; state-event-authorizes-push; retract-on-failed-push; fan-out-after-landing.
- **Non-obvious `None`/error contracts** where callers depend on them (merge-base `Ok(None)` = unrelated history, unborn HEAD → `None`).
- **Perf "why"**: object-cache rationale in history walks; commit cap for virtual lists.
Delete: every field doc that restates the field name, method summary one-liners, `/// Get the profile.`-style docs, organizational comments, test-helper docs, per-constant restatements, duplicated platform tables in `paths`.
Expected reduction: ~1,030 comment lines across backend crates → roughly 200.
---
## 5. Test disposition
Rule: a test earns its place if breaking the behavior would silently break a user (protocol wire formats, interop with `git`/`ngit`/`nak`, state machines users hit). Delete tests of internal ordering, trivial helpers, and coalescing machinery.
### Keep (critical interop, ~73 tests)
- `signed_core/model.rs`: announcement parsing, malformed-value dropping, `u`-tag fork semantics (3), maintainer authority (2), patch-set chain assembly (3), `latest_update` author scoping (2).
- `signed_core/inbox.rs`: threading/root resolution (5) + `group` thread-merging.
- `signed_core/filters.rs`: both (NIP-22 `K` vs NIP-34 `k`).
- `signed_core/status.rs`, `state.rs`: all (kind-30618 wire format, latest-wins, authority, round-trip).
- `signed_git/tests.rs`: `split_patch_series…`, `patch_commits…`, `parses_real_format_patch_output`, `push_all_mirrors…`, `remote_has_refs…`, `fetch_repo_refs_imports_heads_under_a_prefix`, `repo_ref_state_lists…`, `root_commit_reports…`, `blocks_parent_components`, `merge_base_finds…`, `working_copy_cloned_from_the_mirror…`, `head_commit_and_commits_since…` (pins `git am` order), plus `fast_forward_branches_moves_the_mirror_and_keeps_local_work` — kept as a safety invariant, not structure.
- `signed_git/nip34.rs`: all 10 (on-disk marker interop with `nak`/`ngit`).
- `signed_state/backend.rs`: the 6 URL-format/denial-classification tests.
- `signed_state/repo.rs`: all 3 (format-patch header, NIP-22 comment builder, maintainer filters).
- `signed_state/checkouts.rs`: `same_repo_url…`, `checkout_status_reports_ahead_branches_only`, `checkout_push_status_counts_unpushed_commits_only`.
- `signed_state/local_repos.rs`: the 2 sidebar-contract tests (own announcement dropped / other owner's linked).
- `settings`: `save_and_load_roundtrip`, `partial_json_merges_with_defaults` (breaking these silently resets user settings).
### Delete (~38 tests, ~1,100 lines)
- `signed_git/tests.rs` (16): scan walker, `init_repository` contents, `set_origin`, `worktree_last_commits` (2), `all_commits_lists_every_commit`, `find_readme_prefers_markdown`, `current_branch_tracks_checkout`, `worktree_snapshot_reflects…`, diff display (4), `worktree_dirty…`, `worktree_commits_ahead…`.
- `signed_state` (10): `push_outcome_reports_partial_failures`, `resolve_orders_remembered_freshest_first`, `resolve_deduplicates_paths…`, the 4 `local_repos` trivial pass-through/name tests, all 4 `refresh.rs` coalescing tests.
- `signed_core` (9): the 4 `InboxReadState` policy tests, 2 `fork_candidates` ordering/policy tests, 3 `utils` formatting tests.
- `settings` (3): keep only the 2 named above.
Consolidate retained helpers while there: three git-env helpers → one, five bare-server `git init --bare` blocks → one fixture, delete the inline `run` closure at tests.rs L634.
Out of scope but flagged: `signed_ui/pixel_avatar` (identicon determinism — borderline, visual), `dock/tests/render_smoke.rs` (UI smoke).
---
## 6. Risks
- **`PullRequest<'a>` lifetimes** at `pull_requests/detail.rs` call sites — mitigated by keeping it a borrow newtype, not an owning copy.
- **`Repo` wrapper vs background tasks**: `RepoStore` spawns git work via `cx.background_spawn`. The wrapper must stay `Send` when moved into tasks exactly as `gix::Repository` is today; no interior mutation planned, so this is mechanical.
- **Phase 2 `RepoAddr` newtype** touches ~30 call sites in one commit — small each, many files; consider `impl From<Coordinate>` bridges to keep the diff mechanical.
- **Backend push orchestration dedup** (Phase 5.2) is the only phase that reorders logic; land it alone in its own commit behind the existing tests (URL-format + denial-classification + `push_all`/`fetch_repo_refs` interop tests are the net).
## 7. Open decisions
| # | Question | Default |
|---|---|---|
| D1 | Delete the empty `import_dialog` stub + menu entry, or keep the shell? | Keep shell (UI territory, 0 cost) |
| D2 | `SyncProgress` pipeline | Delete; re-add with the feature |
| D3 | `BackendEvent::Error` (9 emitters, 0 subscribers) | Wire one workspace subscriber |
| D4 | `merge_pull_request` (unwired feature) | Delete; recover from history when merge UI is built |
| D5 | wasm32 code paths + `nostr-memory` dep | Remove (no wasm target in workspace) |
| D6 | `paths`: keep as a second function-crate, or fold into `utils`? | Keep separate (no `nostr` dep for path users) |
---
## Appendix A — `signed_git` function → `Repo` method mapping
| Current free fn | Becomes |
|---|---|
| `merge_base`, `head_commit_id`, `commits_since`, `root_commit`, `refs_with_prefix`, `delete_refs_with_prefix` | `Repo::merge_base`, `head`, `commits_since`, `root_commit`, `refs_with_prefix`, `delete_refs_with_prefix` |
| `init_repository` | `Repo::init` |
| `worktree_current_branch`/`current_branch` | `Repo::current_branch` |
| `worktree_ref_exists` | `Repo::ref_exists` |
| `worktree_branches`/`repo_branches` | `Repo::branches` |
| `worktree_ref_state`/`repo_ref_state` | `Repo::ref_state` |
| `repo_tags` | `Repo::tags` |
| `fast_forward_branches` | `Repo::fast_forward_branches` |
| `worktree_checkout_branch`/`worktree_checkout_tag` | `Repo::checkout_branch` / `checkout_tag` |
| `worktree_snapshot` | `Repo::snapshot` (+ branches/tags/current_branch, absorbing `load_repo_data`) |
| `worktree_dirty` | `Repo::is_dirty` |
| `worktree_commits_ahead` | `Repo::commits_ahead` |
| `worktree_all_commits`/`all_commits` | `Repo::all_commits` |
| `worktree_last_commits`/`last_commits` | `Repo::last_commits` |
| `worktree_commit_range_commits` | `Repo::commit_range` |
| `worktree_commit` | `Repo::commit` |
| `worktree_commit_diff`/`commit_diff` | `Repo::commit_diff` |
| `worktree_commit_range_diff` | `Repo::range_diff` |
| `worktree_entries`, `worktree_read`, `find_readme` | `Repo::entries`, `read`, `find_readme` |
| `clone_repo` | `Repo::clone` |
| `push_commit_ref`, `push_main`, `push_all` | `Repo::push_ref`, `push_main`, `push_all` |
| `remote_has_refs` | `Repo::remote_has_refs` |
| `ensure_origin`, `set_origin`, `origin_url` | `Repo::ensure_origin`, `set_origin`, `origin_url` |
| `fetch_repo_refs`, `fetch_all` | `Repo::fetch_refs`, `fetch` |
| `apply_patch`, `format_patch_between` | `Repo::apply_patch`, `format_patch_between` |
| `detect_nip34`, `set_nostr_repo` | `Repo::nip34_binding`, `set_nostr_repo` |
| `find_git_repos` | stays free in `scan.rs` (directory-tree scan, no repo opened) — acceptable exception, or `utils` |
| `sanitize_path_component`, `fork_namespace` | assoc fns on `GitCache` (they build its layout) |
| `split_patch_series`, `patch_diffs`, `patch_commits` | assoc fns on a small `PatchParser` impl (pure parsing, no `Repo`) |
| `is_grasp_url` + private nip34 url helpers | private assoc fns of the NIP-34 detection impl |
| `repository_signature`, `open_with_cache`, `resolve_commit`, `move_head`, `force_checkout`, `edit_local_config` | private methods/assoc fns on `Repo` |
-17
View File
@@ -1,18 +1 @@
# TODO # TODO
## `BackendEvent::SyncProgress`
File: `crates/signed_state/src/backend.rs`
Kept intentionally. The progress pipeline (the `SyncProgress` variant, the `sync_progress`
field and its accessor, and the progress task in `sync_bootstrap`) is retained for a planned
sync progress indicator. No subscriber exists yet. Do not remove it without revisiting that
plan.
## `login` / `logout` family
File: `crates/signed_state/src/backend.rs`
No UI path calls these. `import_dialog::open` is an empty stub. Decide whether to
delete the family (`login`, `login_with_new_identity`, `login_with_nsec`,
`login_with_bunker`, `logout`) or wire the stub to `Backend::login`.