Skip to content

Initial implementation.#1

Merged
plx merged 41 commits intomainfrom
experiment/one-shot-implementation
Apr 13, 2026
Merged

Initial implementation.#1
plx merged 41 commits intomainfrom
experiment/one-shot-implementation

Conversation

@plx
Copy link
Copy Markdown
Owner

@plx plx commented Apr 12, 2026

Opening for human and agent review.

plx and others added 30 commits April 10, 2026 22:23
Establish the four foundational v1 decisions in docs/architecture.md,
create the full module layout with stub files, and bootstrap the
binary+library crate with all dependencies and CI workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement full CLI surface: copy (default), list, info, validate
subcommands with global options (--source, --dest, -C, -q, -v) and
copy-specific options (--dry-run, --overwrite). All commands return
placeholder "not implemented" errors. Add 8 CLI integration tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement RepoRelPath with path normalization (accepts absolute/relative,
rejects root-escape and repo-root-as-file). Add all domain types:
RepoContext, ValidationReport, CopyPlan, PlannedEntry, CopyOp,
InfoReport, GitIgnoreStatus, WorktreeincludeStatus, and supporting enums.
13 path normalization unit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement GitBackend trait with GitCli concrete type. Methods:
show_toplevel, list_worktrees, tracked_paths, check_ignore,
list_worktreeinclude_candidates, read_bool_config. All Git output
parsed from NUL-delimited formats. 8 parser unit tests for worktree
list and check-ignore output parsing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement resolve_context() that turns CLI inputs into a validated
RepoContext. Handles auto-detection of linked vs main worktree,
requires --dest for copy from main worktree, rejects source==dest
and destinations outside the worktree family. 5 unit tests with
mock GitBackend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Discover and validate all .gitignore, .worktreeinclude, and
.git/info/exclude files using GitignoreBuilder. Reports errors for
in-repo files and warnings for global excludes. Detects symlinked
.worktreeinclude files. 5 validation unit tests with real git repos.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement per-directory .worktreeinclude evaluation with last-match-wins
semantics. Collects applicable files from repo root to queried path's
parent, builds matchers per directory using GitignoreBuilder. Returns
Included/ExcludedByNegation/NoMatch with file, line, and pattern.
13 unit tests covering globs, negation, nesting, anchored patterns,
directory-only patterns, **, and case-insensitive matching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire list command: resolve context, validate, enumerate candidates via
git ls-files --exclude-per-directory=.worktreeinclude, filter through
git check-ignore, sort and print. Wire validate command with error/
warning output. Verbose list mode includes worktreeinclude explanations.
7 integration tests with real git repos covering root patterns, nested
overrides, tracked file exclusion, ** patterns, and sorted output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Info shows per-path status: tracked/ignored/worktreeinclude status,
source existence/kind, copy eligibility, and destination state when
a dest worktree is known. Uses both Git backend (ls-files, check-ignore)
and worktreeinclude explanation engine. 5 integration tests covering
tracked files, ignored+included, missing files, and multi-path queries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement planner with destination classification (Missing, UpToDate,
UntrackedConflict, TrackedConflict, TypeConflict, UnsafePath), sorted
deterministic entries, and dry-run rendering. Implement executor with
atomic temp-file-then-rename writes, symlink safety checks, permission
preservation, and continue-on-error semantics. Wire copy command with
full pipeline: context resolution, validation, candidate enumeration,
ignore filtering, planning, and execution. FileSystem trait enables
mock testing. 6 planner unit tests, 7 copy integration tests including
worktree auto-detection, dry-run, overwrite, and conflict handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 6 deterministic differential tests and 1 property-based test
comparing wiff list output against Git oracle (ls-files + check-ignore).
Tests cover: simple patterns, nested overrides, **, negation chains,
tracked file exclusion, and .git/info/exclude.

Address Codex review findings:
- Fix inverted ValidationSeverity doc comments
- Use trim_end_matches for show_toplevel instead of trim()
- Remove trim() on NUL-delimited path entries
- Enforce source=main worktree and dest=linked worktree for copy
- Make RepoRelPath::from_normalized pub(crate)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive crate-level docs in lib.rs explaining architecture,
key decisions, and module organization. Rewrite README.md with usage
examples, .worktreeinclude format guide, eligibility rules, safety
guarantees, and command reference. Expand architecture.md with command
pipeline and testing strategy sections. Add doc-build job to CI matrix.
Add release workflow stub triggered by version tags. Fix rustdoc
broken intra-doc link warning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The info command was using ad hoc destination checks (existence, file type,
content equality) instead of the planner's classify_destination logic. This
meant it never detected tracked conflicts, untracked conflicts, or unsafe
paths — reporting generic "exists (differs)" instead.

Now info reuses planner::classify_destination to emit proper DestinationState
labels (tracked-conflict, untracked-conflict, type-conflict, unsafe-path,
up-to-date, missing) aligned with the planner's taxonomy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address Codex review feedback: classify_destination assumes source is a
regular file (matching planner preconditions). When source is missing or
non-regular, the read-based comparison would incorrectly fall through to
UntrackedConflict. Now info only invokes full classification when source
exists and is a regular file; otherwise it reports basic existence.

Added edge-case test for missing source with existing destination.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion as complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nd predicted actions

The list -v output previously only showed a debug-formatted WorktreeincludeStatus.
Now it includes: source file size, gitignore source/line/pattern, worktreeinclude
source/line/pattern, and (when --dest is provided) the predicted copy action via
classify_destination — matching the spec contract from InitialSpecDocument.txt
lines 355-362.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Match planner preconditions: symlinks and non-regular-file sources are
skipped by the planner, so verbose list should report "skip (unsupported
source type)" instead of running classify_destination on them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…plete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nd negation caveat

Two bugs fixed:
1. Path duplication for nested .worktreeinclude files: evaluate_file used
   file_dir.join(rel_path) which doubled directory segments (e.g. config/config/foo).
   Now uses repo_root.join(rel_path) for correct absolute path construction.

2. Git negation caveat not enforced: per-line matchers couldn't cross-reference
   patterns, so `dir/` + `!dir/keep` incorrectly deselected dir/keep. Now builds
   a single combined matcher per file and explicitly checks parent directories
   when a whitelist (negation) match occurs — if a parent directory is selected,
   the negation cannot override it (matching Git's documented caveat).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
validate.rs was directly shelling out to `git config core.excludesFile`,
violating the architectural boundary that all Git process invocations
should go through git.rs. This adds a `read_config` method to `GitBackend`
and rewrites `find_global_excludes` to use it, restoring testability with
mock backends.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add four additional tests to strengthen validation of the nested path
and negation caveat fixes:
- nested anchored pattern does NOT match outside its directory scope
- deeply nested anchored pattern (3+ levels)
- negation caveat in nested .worktreeinclude files
- negation without directory pattern still deselects (no false caveat)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The within-file negation caveat was already enforced (a `dir/` pattern
in the same file blocks `!dir/keep`), but the cross-file case was not:
if root `.worktreeinclude` selected a directory, a deeper file's negation
could still deselect files inside it.

Refactors evaluate_file into build_file_match_context + evaluate_against_context
so matchers are retained for a post-loop cross_file_ancestor_check. When the
final result is ExcludedByNegation, all accumulated matchers are checked for
ancestor directory selections — matching Git's documented behavior that negation
cannot override a parent directory selection regardless of which file the
patterns come from.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-key-cases as complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The fix was already applied in commit 384f1bc which moved the global
excludes git config lookup behind the GitBackend trait. Verified that
no direct Git process invocations remain outside git.rs in production
code, and all existing tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The info command was skipping the validation phase that copy and list
both run, allowing it to report results even when .gitignore or
.worktreeinclude files contain errors. Now info mirrors the same
validate-then-proceed pipeline as the other commands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Assert that validation blocks before any info output is produced
  (stdout should not contain "path:" on validation failure)
- Remove no-op warning test that didn't exercise real warning behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `-z` flag causes git to NUL-terminate each attribute field (replacing
newlines), with a double-NUL separating records. The old parser split on
NUL and then iterated `.lines()` within each chunk, which meant attributes
like `bare` in their own NUL-delimited field were never associated with
the correct worktree record.

Replace with a stateful parser that processes NUL-delimited fields
sequentially: `worktree <path>` starts a new record, subsequent fields
are attributes of the current record, and an empty field (double-NUL)
finalizes it. Update all test data to use the real -z byte format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sing

Add edge case tests for locked/prunable attributes, paths with spaces,
and missing trailing double-NUL to strengthen parser coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
plx and others added 8 commits April 12, 2026 05:10
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
walkdir does not follow symlinks by default, so symlink entries have
is_symlink()=true and is_file()=false. The symlink check was nested
inside the is_file() guard and was therefore unreachable for actual
symlinks. Restructure discover_and_validate to check for symlinked
.worktreeinclude entries before the is_file() gate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…re-not-rejected-by-validation

Add integration test that exercises `wiff validate` against a repo with
a symlinked .worktreeinclude, verifying the CLI reports the error and
exits with a failure code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lidation as complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add integration tests for tracked destination conflicts in copy command:
- copy_skips_tracked_destination_conflict: verifies tracked dest files are skipped
- copy_skips_tracked_destination_even_with_overwrite: verifies --overwrite cannot
  override the tracked-conflict protection

Add differential explanation-parity tests comparing per-path explanation
tuples (source file, line, pattern) from `wiff info` against
`git check-ignore -v -n`, covering: simple patterns, globs, multi-line
rules, negation chains, doublestar patterns, directory-slash patterns,
and .git/info/exclude sources.

These fill gaps identified in ImplementationPlan.txt step 12 (tracked
destination conflicts) and step 13 (per-path explanation parity).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mplete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Validation previously only reported parse/read failures. Per the spec,
it should also warn about suspicious-but-legal patterns. This adds two
lint-style heuristics:

- Shadowed negation: a negation like `!dir/file` after `dir/` was
  excluded is ineffective because git won't descend into excluded
  directories.
- Duplicate pattern: the same pattern text appearing more than once
  in the same file.

Both are reported as ValidationSeverity::Warning (non-fatal).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pattern-warnings as complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@plx plx self-assigned this Apr 12, 2026
Copy link
Copy Markdown
Owner Author

@plx plx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AGENT: this is a very good first draft. I've left some comments.

The big one is that I forgot to ask to setup a justfile to use for all project commands; for reference see the one in my other project: https://github.com/plx/ferric-rules .

Aside from that there's a few questions and a proposed refactoring—otherwise looks pretty good.

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread README.md Outdated
Comment thread Cargo.toml Outdated
Comment thread src/cli.rs Outdated
Comment thread src/cli.rs Outdated
Comment thread src/cli.rs Outdated
Comment thread src/cli.rs Outdated
@plx plx merged commit 9ef72de into main Apr 13, 2026
6 checks passed
@plx plx deleted the experiment/one-shot-implementation branch April 13, 2026 14:31
plx added a commit that referenced this pull request Apr 29, 2026
The architecture doc and module-level docs were written before the gix
migration (PR #3) and the worktreeinclude-modes feature (PR #5). This
re-checks them against the current code surface:

- Decision #1: "Git CLI is authoritative" reframed as the GitBackend
  trait with two implementations (GitGix default, GitCli via
  WAFT_GIT_BACKEND=cli) backed by parity tests.
- Decision #2: per-directory semantics are now one of three pluggable
  engines (claude-2026-04 default, git, wt-0.39); the per-directory
  discussion lives in its own section.
- Repo layout adds config.rs, policy_filter.rs, worktreeinclude_engine.rs,
  the new test files, and the planning docs.
- Command pipeline updated to 9 stages: policy resolution, select_candidates
  dispatch, post-selection policy filter.
- README eligibility rule covers when_missing=all-ignored and the
  exclusion sets; the nested .worktreeinclude paragraph leads with the
  default (root-only) behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
plx added a commit that referenced this pull request Apr 30, 2026
* Document subcommands module and update architecture layout.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Refresh docs for gix backend and worktreeinclude modes.

The architecture doc and module-level docs were written before the gix
migration (PR #3) and the worktreeinclude-modes feature (PR #5). This
re-checks them against the current code surface:

- Decision #1: "Git CLI is authoritative" reframed as the GitBackend
  trait with two implementations (GitGix default, GitCli via
  WAFT_GIT_BACKEND=cli) backed by parity tests.
- Decision #2: per-directory semantics are now one of three pluggable
  engines (claude-2026-04 default, git, wt-0.39); the per-directory
  discussion lives in its own section.
- Repo layout adds config.rs, policy_filter.rs, worktreeinclude_engine.rs,
  the new test files, and the planning docs.
- Command pipeline updated to 9 stages: policy resolution, select_candidates
  dispatch, post-selection policy filter.
- README eligibility rule covers when_missing=all-ignored and the
  exclusion sets; the nested .worktreeinclude paragraph leads with the
  default (root-only) behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant