Skip to content

feat(sync): validate sparse-checkout scope before leasing #1567

Description

@coygeek

Summary

Validate sparse-checkout and skip-worktree omissions before Crabbox acquires, resolves, prepares, or borrows an SSH lease for an ordinary local sync. Keep the existing post-acquisition manifest rebuild so files created during acquisition still participate, and make the rejection name the supported recovery paths.

Problem to solve

Crabbox correctly fails closed when a tracked path is hidden by sparse-checkout or skip-worktree state but remains in the effective sync manifest. sync-plan detects that condition locally, but the error does not explain how to recover. A sync-enabled run discovers the same local condition only after provisioning and preparing a lease.

This reproduces against exact current main:

git clone https://github.com/openclaw/crabbox.git
cd crabbox
git checkout 58323474be23508b971c5e5f1fb3eb43dd6b97af
go build -trimpath -o bin/crabbox-main ./cmd/crabbox
crabbox_main="$PWD/bin/crabbox-main"

repro_dir="$(mktemp -d)"
cd "$repro_dir"
git init
git config user.name "Crabbox sparse repro"
git config user.email "repro@example.invalid"
mkdir -p visible hidden
printf 'visible\n' > visible/keep.txt
printf 'hidden\n' > hidden/drop.txt
git add visible/keep.txt hidden/drop.txt
git commit -m "test: add sparse fixture"
git sparse-checkout init --cone
git sparse-checkout set visible

"$crabbox_main" sync-plan

sync-plan exits 6 with:

build sync file list: tracked path "hidden/drop.txt" is hidden by sparse checkout or skip-worktree state but remains in sync manifest scope

The message identifies the safety condition but does not say that the checkout can be materialized or the hidden path can be removed from the effective manifest with sync.include, sync.exclude, or .crabboxignore.

Running the equivalent workload provisions before returning the same local error:

"$crabbox_main" run --provider local-container --target linux -- true

The exact-main reproduction spent 23.5 seconds creating an Ubuntu 26.04 ARM64 container, waiting for SSH, and printing a run context before releasing the lease and exiting 6. The user command never ran. The lease cleanup completed successfully.

Two controls restore the local plan. git sparse-checkout disable materializes both tracked paths and makes sync-plan report two candidate files. Keeping the checkout sparse while excluding hidden/drop.txt through .crabboxignore also succeeds and reports only the visible tracked file plus the root ignore file.

Proposed behavior

Before Crabbox mutates provider state for an ordinary Git-backed sync, run a bounded local validation that detects in-scope sparse-checkout and skip-worktree omissions. If validation fails, return the existing exit class and path evidence without acquiring, resolving, preparing, or borrowing a lease.

Keep rebuilding the authoritative manifest after acquisition. The current post-acquisition rebuild includes files created while a lease is being acquired, and the new preflight must not turn that throwaway validation result into the transfer manifest.

Make the rejection actionable. It should direct the user to materialize a full checkout, or to remove the hidden path from the effective manifest with an include or ordered exclude when that path is intentionally outside the remote workload. A concise documentation link is enough if the CLI text remains bounded.

Acceptance criteria

  • A Git checkout with an in-scope hidden tracked file exits 6 before any new SSH lease acquisition or ready-pool borrow.
  • Existing-lease routes reject the same local input before provider resolve or prepare work that is unnecessary for the failed sync.
  • The diagnostic retains the affected path and explains both supported recovery categories: materialize the checkout, or remove the path from effective sync scope.
  • sync-plan and sync-enabled run use consistent recovery guidance for the same hidden path.
  • Hidden paths outside sync.include or removed by ordered excludes remain accepted.
  • A sparse configuration that materializes every tracked path remains accepted.
  • The authoritative manifest is still rebuilt after successful acquisition, and files created during acquisition remain included.
  • --no-sync, fresh remote checkouts, delegated providers that own synchronization, and other paths that do not use the ordinary local manifest retain their current behavior.
  • A failed local sparse preflight creates no provider lease, container, or ready-pool side effect.
  • Focused tests cover new acquisition, existing-lease preparation, ready-pool borrowing, actionable error text, excluded hidden paths, fully materialized sparse checkouts, and the post-acquisition rebuild control.

Affected area

  • Local sync-manifest validation in internal/cli/repo.go.
  • Pre-acquisition routing and the post-acquisition manifest rebuild in internal/cli/run.go.
  • Sparse-manifest and run-ordering coverage in internal/cli/repo_test.go and internal/cli/run_test.go.
  • Sparse-checkout recovery guidance in docs/commands/sync-plan.md and docs/features/sync.md.

Non-goals

  • Syncing an in-scope tracked file whose contents are absent locally.
  • Automatically disabling sparse checkout or changing Git index state.
  • Reusing a pre-acquisition file list as the final transfer manifest.
  • Changing ordered include or exclude semantics.
  • Adding arbitrary non-Git directory synchronization, which remains tracked separately in issue #1334.
  • Weakening the fail-closed protection added for sparse and skip-worktree omissions.

Alternatives considered

Document the recovery only

Documentation would help users who already know where to look, but run would still provision a lease for a locally knowable error. The existing non-Git sync preflight already establishes that local manifest-source failures can stop before provider work.

Build the final manifest before acquisition

This avoids duplicate local work but creates a stale transfer list. Current regression coverage requires files created during acquisition to appear in the final sync. A bounded validation followed by the existing rebuild preserves both behaviors.

Materialize the checkout automatically

Changing sparse-checkout or index state mutates the user's Git workspace and can pull a much larger tree than intended. Crabbox should explain the choice and leave that mutation to the caller.

Supporting context

The complete 65-commit range from the release baseline through exact current main, all 24 open issues, all 16 open pull requests, every open issue authored by coygeek, and relevant closed issues and merged pull requests were checked. No current item covers pre-acquisition sparse-scope validation plus actionable recovery guidance.

Merged pull request #1241 added the exact fail-closed sparse-manifest protection and deliberately preserved post-acquisition manifest behavior. Merged pull request #1253 is the closest lifecycle precedent: it made the non-Git manifest-source error actionable and rejected that locally knowable condition before provider work while still rebuilding the transfer manifest later. Neither change covers the sparse-scope ordering and recovery gap above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:bulk-filedClawSweeper detected a high recent issue-filing volume from this author.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions