Count-and-gate engine for consolidating scattered worktrees/clones into origin/master: inventory, collision report, restore-tested backup, PASS/FAIL verify, and a cleanup that refuses unless verify passes in-process. Merging stays human-driven; this tool only counts and gates (sprint 3.28).
| Tier | tools |
| Role | unclassified (baselined) |
| Path | crates/tools/consolidate-worktrees |
| Edition | 2021 |
| Targets | tools-consolidate-worktrees, tools_consolidate_worktrees, cli_test |
| Public items | 31 across 8 modules |
| Tests | 54 |
What it is for
Decidable core of the consolidation playbook: parsers, extractors, verdicts, and the cleanup gate — each a pure function of its arguments — plus gitcmd, the one process edge every git call goes through (sprint 3.28).
Capabilities
gitcmd (other)
The process edge: every git invocation in the tool goes through here —
| Item |
|---|
fn git(repo : & Path, args : & & str) -> Result <String> |
fn base_ref(repo : & Path) -> String |
fn local_branches(repo : & Path) -> Result <Vec <String>> |
fn ahead_count(repo : & Path, base : & str, branch : & str) -> Result <usize> |
fn patch_unmatched(repo : & Path, base : & str, branch : & str) -> Option <usize> |
fn content_landed(repo : & Path, base : & str, branch : & str) -> Option <bool> |
integrate (other)
The integration step the playbook used to leave to hand-run git: take a merged-ready
| Item |
|---|
fn overlapping_files(branch_changed : & String, base_changed : & String) -> Vec <String> |
IntegrationKind
The integration step the playbook used to leave to hand-run git: take a merged-ready
| Item |
|---|
pub enum IntegrationKind |
fn classify_integration(has_merge_base : bool, branch_in_base : bool, base_in_branch : bool,) -> IntegrationKind |
MergeVerdict
Merged-ness verdict (consensus m3, hardened at Gate 4 B1): ancestry
| Item |
|---|
pub enum MergeVerdict |
MergeVerdict :: fn is_merged(self) -> bool |
fn resolve_verdict(ahead : usize, patch_unmatched : Option <usize>, content_landed : Option <bool>,) -> MergeVerdict |
migrations (other)
Migration version extraction and duplicate detection. Number collisions
| Item |
|---|
fn parse_migration_path(path : & str) -> Option <(String, u64, String)> |
fn duplicate_versions(refs : & MigrationRef) -> Vec <VersionCollision> |
MigrationRef
Migration version extraction and duplicate detection. Number collisions
| Item |
|---|
pub struct MigrationRef |
VersionCollision
Migration version extraction and duplicate detection. Number collisions
| Item |
|---|
pub struct VersionCollision |
WorktreeEntry
Parser for git worktree list --porcelain output.
| Item |
|---|
pub struct WorktreeEntry |
fn parse_worktree_list(porcelain : & str) -> Vec <WorktreeEntry> |
Check
Verify report aggregation and the cleanup gate. The gate is pure: the
| Item |
|---|
pub struct Check |
fn all_pass(checks : & Check) -> bool |
fn render_report(checks : & Check) -> String |
CleanupGate
Verify report aggregation and the cleanup gate. The gate is pure: the
| Item |
|---|
pub struct CleanupGate |
fn cleanup_allowed(gate : & CleanupGate) -> Result <(), String> |
sprints (other)
Sprint/userstory doc numbering: extraction and collision detection.
| Item |
|---|
fn parse_sprint_doc(path : & str) -> Option <(String, String)> |
fn sprint_collisions(docs : & SprintDocRef) -> Vec <SprintCollision> |
SprintCollision
Sprint/userstory doc numbering: extraction and collision detection.
| Item |
|---|
pub struct SprintCollision |
SprintDocRef
Sprint/userstory doc numbering: extraction and collision detection.
| Item |
|---|
pub type SprintDocRef: (String, String, String) |
status (other)
Classifier for git status --porcelain lines: what is uncommitted, and
| Item |
|---|
pub const JUNK_COMPONENTS: & & str |
fn is_junk(path : & str) -> bool |
StatusSummary
Classifier for git status --porcelain lines: what is uncommitted, and
| Item |
|---|
pub struct StatusSummary |
fn summarize_status(porcelain : & str) -> StatusSummary |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
tools_consolidate_worktrees
gitcmdintegratemergedmigrationsporcelainreportsprintsstatus
flowchart TD n_tools_consolidate_worktrees["tools_consolidate_worktrees"] n_tools_consolidate_worktrees --> n_gitcmd["gitcmd"] n_tools_consolidate_worktrees --> n_integrate["integrate"] n_tools_consolidate_worktrees --> n_merged["merged"] n_tools_consolidate_worktrees --> n_migrations["migrations"] n_tools_consolidate_worktrees --> n_porcelain["porcelain"] n_tools_consolidate_worktrees --> n_report["report"] n_tools_consolidate_worktrees --> n_sprints["sprints"] n_tools_consolidate_worktrees --> n_status["status"]
Public surface
`gitcmd`
| Item | What it is |
|---|---|
fn git(repo : & Path, args : & & str) -> Result <String> | — |
fn base_ref(repo : & Path) -> String | The consolidation base: origin/master when a remote-tracking ref exists (the normal case), plain master otherwise (fixtures, offline mirrors). |
fn local_branches(repo : & Path) -> Result <Vec <String>> | — |
fn ahead_count(repo : & Path, base : & str, branch : & str) -> Result <usize> | Commit count on branch not reachable from base (ancestry). |
fn patch_unmatched(repo : & Path, base : & str, branch : & str) -> Option <usize> | Commits on branch whose patch-id is absent from base — git cherry lines starting with + |
fn content_landed(repo : & Path, base : & str, branch : & str) -> Option <bool> | True when every path the branch changed (vs its merge-base) is byte-identical between branch and base — the residue a multi-commit squash leaves |
`integrate`
| Item | What it is |
|---|---|
pub enum IntegrationKind | How a branch relates to the base it would land on. |
fn classify_integration(has_merge_base : bool, branch_in_base : bool, base_in_branch : bool,) -> IntegrationKind | Classify the integration from three ancestry facts the caller reads out of git |
fn overlapping_files(branch_changed : & String, base_changed : & String) -> Vec <String> | The files a divergent merge could conflict on: those changed on BOTH sides since the merge base |
`merged`
| Item | What it is |
|---|---|
pub enum MergeVerdict | — |
MergeVerdict :: fn is_merged(self) -> bool | Only the two Merged verdicts satisfy the verify gate. |
fn resolve_verdict(ahead : usize, patch_unmatched : Option <usize>, content_landed : Option <bool>,) -> MergeVerdict | Resolution chain: ancestry, then content, with patch-id only ever used as NEGATIVE evidence (unmatched > 0 confirms unmerged when content also says so) |
`migrations`
| Item | What it is |
|---|---|
pub struct MigrationRef | A migration file as seen on one source (branch or checkout). |
fn parse_migration_path(path : & str) -> Option <(String, u64, String)> | Extract (dir, version, file) from a repo-relative path shaped like crates/<crate>/migrations/012_add_col.sql |
pub struct VersionCollision | Same dir + same version + more than one distinct FILE name = a number collision |
fn duplicate_versions(refs : & MigrationRef) -> Vec <VersionCollision> | — |
`porcelain`
| Item | What it is |
|---|---|
pub struct WorktreeEntry | One checkout as git reports it |
fn parse_worktree_list(porcelain : & str) -> Vec <WorktreeEntry> | Parse the porcelain format: blank-line-separated stanzas of worktree <path> / HEAD <sha> / branch refs/heads/<name> with optional bare markers like prunable or detached. |
`report`
| Item | What it is |
|---|---|
pub struct Check | — |
fn all_pass(checks : & Check) -> bool | — |
fn render_report(checks : & Check) -> String | Render one line per check (PASS/FAIL, name, detail) and a final `VERIFY: PASS\ |
pub struct CleanupGate | Everything cleanup needs to decide, gathered by the binary immediately before acting (consensus m4: the OID is pinned at verify time and must still match). |
fn cleanup_allowed(gate : & CleanupGate) -> Result <(), String> | Ok(()) means cleanup may proceed; Err carries the operator-readable refusal reason. |
`sprints`
| Item | What it is |
|---|---|
fn parse_sprint_doc(path : & str) -> Option <(String, String)> | Extract (number, slug) from a doc filename like docs/userstories/sprint-3.28-consolidate-worktrees-tool.md → ("3.28", "consolidate-worktrees-tool") |
pub type SprintDocRef: (String, String, String) | One sprint doc sighting: (number, slug, source branch). |
pub struct SprintCollision | Same number claimed by more than one distinct slug = collision. |
fn sprint_collisions(docs : & SprintDocRef) -> Vec <SprintCollision> | — |
`status`
| Item | What it is |
|---|---|
pub const JUNK_COMPONENTS: & & str | Directory names that are build output, never work |
fn is_junk(path : & str) -> bool | True when any path component is a known build-output directory, or the path sits under the gitignored .claude/worktrees/ directory. |
pub struct StatusSummary | — |
fn summarize_status(porcelain : & str) -> StatusSummary | Parse git status --porcelain (v1) output into the three buckets. |
No pub use re-exports: every item above is declared in this crate.
Boundary
Depends on no other workspace tier.
Shares tier tools with 84 other crates: tools-advisory-reach, tools-archive-guard, tools-artifact-scaffold, tools-ask-ai-core, tools-ask-ais, tools-ask-gemini, tools-book, tools-book-report, … (84 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | tools |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/tools/consolidate-worktrees |
| Vocabulary in force (lexicon) | current |
Dependencies
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
anyhow | ^1 | — | no | always |
clap | ^4 | derive | no | always |
Development, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `tools-cli-conformance` | tools | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
tempfile | ^3 | — | no | always |
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["tools-consolidate-worktrees"] SELF -->|development| n_tools_cli_conformance["tools-cli-conformance"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| bin | tools-consolidate-worktrees | `src/main.rs` |
| lib | tools_consolidate_worktrees | `src/lib.rs` |
| test | cli_test | `tests/cli_test.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | none detected |
| database access | none detected |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | yes |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
| Variable | Read in |
|---|---|
HOME | src/main.rs |
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 42 |
| Integration tests | 12 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
gitcmd | 6 | 0 | 0 |
integrate | 3 | 0 | 0 |
merged | 2 | 0 | 0 |
migrations | 4 | 0 | 0 |
porcelain | 2 | 0 | 0 |
report | 5 | 0 | 0 |
sprints | 4 | 0 | 0 |
status | 4 | 0 | 0 |
What the tests establish, by name:
backup_bundles_verifies_restore_tests_and_archives_loose_files—tests/cli_test.rscleanup_refuses_without_yes_and_without_green_verify—tests/cli_test.rscleanup_removes_only_merged_worktrees_and_keeps_dirs_with_loose_files—tests/cli_test.rsclone_roots_are_backed_up_verified_and_movable—tests/cli_test.rscollisions_finds_duplicate_migration_versions_across_branches—tests/cli_test.rsevil_merge_branch_fails_verify—tests/cli_test.rshelp_names_every_subcommand—tests/cli_test.rsinventory_counts_loose_files—tests/cli_test.rsinventory_lists_every_worktree_with_branch_and_counts—tests/cli_test.rssquash_merged_branch_passes_verify_via_patch_id—tests/cli_test.rsverify_fails_with_unmerged_branch_and_names_it—tests/cli_test.rsverify_passes_once_everything_is_merged_and_clean—tests/cli_test.rsa_file_changed_on_both_sides_is_the_overlap_to_review—src/integrate.rsbase_ancestor_of_branch_is_a_fast_forward—src/integrate.rsbranch_contained_in_base_is_already_merged—src/integrate.rsdisjoint_changes_cannot_conflict—src/integrate.rsdivergence_requires_a_merge—src/integrate.rsno_common_history_is_unrelated—src/integrate.rsoverlap_is_sorted_and_deduped—src/integrate.rscli_definition_conforms—src/main.rsancestry_merged_needs_no_other_evidence—src/merged.rscherry_alone_never_proves_merged—src/merged.rscherry_positive_evidence_stands_without_content—src/merged.rscontent_refuted_with_cherry_unavailable_is_unmerged—src/merged.rsevil_merge_blocks_despite_cherry_all_matched—src/merged.rsgenuinely_unmerged_counts_unmatched—src/merged.rsmerged_verdicts_satisfy_the_gate—src/merged.rsnothing_available_is_unknown_and_blocks—src/merged.rssquash_merged_branch_is_merged_by_content—src/merged.rsparses_versioned_migration_paths—src/migrations.rs- _… 24 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 23 | 31 |
Public modules with a //! block | 8 | 8 |
pie showData
title Public items with rustdoc
"Documented" : 23
"No rustdoc detected" : 8
Metrics
| Metric | Value |
|---|---|
| Rust source files | 10 |
| Source lines | 1840 |
| Code lines | 1482 |
| Public API items | 31 |
| Public modules | 8 |
| Tests | 54 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 2 |
| Workspace reverse dependencies | 0 |
pie showData
title Public API by kind
"constant" : 1
"enum" : 2
"function" : 19
"method" : 1
"struct" : 7
"type alias" : 1
pie showData
title Rust source composition
"Code" : 1482
"Blank or comment" : 358
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.