Skip to content

feat(vcs): support layered gitignored file seeding - #191

Open
ironerumi wants to merge 6 commits into
kunchenguid:mainfrom
ironerumi:fm/th-189-upstream
Open

ironerumi wants to merge 6 commits into
kunchenguid:mainfrom
ironerumi:fm/th-189-upstream

Conversation

@ironerumi

@ironerumi ironerumi commented Oct 18, 2026 •

Copy link
Copy Markdown

Intent

go ahead with the treehouse windows fix

Context: upstream PR #191 (branch fm/th-189-upstream on the ironerumi fork, head b04ec6f, "feat(vcs): support layered gitignored file seeding", Closes kunchenguid/treehouse#189) and its fork twin ironerumi#2 carry the same commit. On the fork's Windows CI the Go test packages cmd and internal/pool hit Go's 10-minute per-package timeout (605s and 600s, run 39165421579). Measured Windows package times: upstream main 396s (cmd) / 427s (internal/pool); earlier fork head db51f32 532s / 578s; current head 605s+ / 600s+. The feature adds about 35% test time on Windows, so upstream CI will fail the same way once the maintainer approves it to run.

What Changed

  • Added layered ignored-file seeding from configured, clone-local, and destination-committed include rules, while making --include-file replace all default layers.
  • Reused the acquisition-resolved Git common directory across Git and jj seeding and reduced redundant Git metadata discovery, including batched committed-manifest reads.
  • Documented the layered seeding behavior and added regression coverage for CLI negation, persistent-layer errors, inventory cleanup, compatibility, and performance-sensitive paths.

Risk Assessment

⚠️ Medium: The change spans layered seeding, Git compatibility, and Git/jj acquisition plumbing, but the reviewed paths showed no concrete source-verifiable defect.

Testing

The real CLI passed layered seeding, override, committed-manifest, and unreadable-include scenarios after rerunning with an isolated XDG config; targeted acquisition and Git resolver tests passed, and the Windows cross-build passed. JJ runtime and Windows runtime/performance could not be exercised because jj and a Windows runner are unavailable. The prior payload did not establish live results for the pool acquisition or Git resolver scenarios, so both are reported as untested.

  • Live validation: ⚠️ inconclusive - 3 of 7 scenarios driven live against the product
Scenario Result Live Evidence
CLI default layered seeding applies user, clone, and committed policies ✅ pass live ~/.no-mistakes/evidence/01M58XKZ8139TC0WR09JB24KZH/cmd-layered.log
CLI include override bypasses broken defaults and supports an empty override ✅ pass live ~/.no-mistakes/evidence/01M58XKZ8139TC0WR09JB24KZH/cmd-layered.log
CLI preserves committed-manifest replacement and rejects unreadable include files safely ✅ pass live ~/.no-mistakes/evidence/01M58XKZ8139TC0WR09JB24KZH/cmd-regression.log
Acquisition rejects unreadable persistent layers and quarantines reused slots ⏸️ untested no The prior payload recorded only package-level executable tests, not a live product session, so it did not establish a live result for this scenario.
Git resolver keeps the common-directory optimization and older-Git fallback ⏸️ untested no The prior payload recorded executable repository tests rather than a live product session, so it did not establish a live result for this scenario.
JJ layered seeding works for colocated and non-colocated repositories ⏸️ untested no Tried the targeted JJ scenario and checked PATH; jj is unavailable, and there is no repository-local JJ binary or source from which to build one without installing or searching outside the workspace…
Windows target preserves buildability ⏸️ untested no The Windows cross-build passed, but this macOS host cannot run the Windows product or measure the Windows cmd/internal-pool package timeout. Provide a Windows runner or the configured Windows CI timin…
Evidence: CLI layered seeding evidence
=== RUN   TestGetIncludeLayersAndOverride
--- PASS: TestGetIncludeLayersAndOverride (2.05s)
PASS
ok  	github.com/kunchenguid/treehouse/v3/cmd	3.477s
Evidence: CLI regression evidence
=== RUN   TestGetIncludeFileReplacesCommittedManifest
=== RUN   TestGetIncludeFileReplacesCommittedManifest/git
=== RUN   TestGetIncludeFileReplacesCommittedManifest/jj
    e2e_test.go:738: jj is not installed
--- PASS: TestGetIncludeFileReplacesCommittedManifest (1.22s)
    --- PASS: TestGetIncludeFileReplacesCommittedManifest/git (1.22s)
    --- SKIP: TestGetIncludeFileReplacesCommittedManifest/jj (0.00s)
=== RUN   TestGetIncludeLayersAndOverride
--- PASS: TestGetIncludeLayersAndOverride (1.19s)
=== RUN   TestGetIncludeFileReadFailureLeavesPoolUntouched
--- PASS: TestGetIncludeFileReadFailureLeavesPoolUntouched (0.42s)
PASS
ok  	github.com/kunchenguid/treehouse/v3/cmd	3.869s
Evidence: Pool error-handling evidence
=== RUN   TestAcquire_PersonalLayersRefreshAndInventoryCleanup
🌳 Setting up worktree...
🌳 Setting up worktree...
🌳 Setting up worktree...
--- PASS: TestAcquire_PersonalLayersRefreshAndInventoryCleanup (0.58s)
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=false
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=false/user
🌳 Setting up worktree...
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=false/clone
🌳 Setting up worktree...
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=true
🌳 Setting up worktree...
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=true/user
🌳 Setting up worktree...
🌳 Setting up worktree...
=== RUN   TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=true/clone
🌳 Setting up worktree...
🌳 Setting up worktree...
--- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine (1.32s)
    --- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=false (0.39s)
        --- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=false/user (0.24s)
        --- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=false/clone (0.11s)
    --- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=true (0.93s)
        --- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=true/user (0.31s)
        --- PASS: TestAcquire_PersistentLayerReadErrorsRemoveOrQuarantine/reused=true/clone (0.39s)
PASS
ok  	github.com/kunchenguid/treehouse/v3/internal/pool	2.270s
Evidence: Git layered resolver evidence
=== RUN   TestCommonGitDirEnvFallsBackForOlderGitWithSourceEnvironment
--- PASS: TestCommonGitDirEnvFallsBackForOlderGitWithSourceEnvironment (0.66s)
=== RUN   TestSeedLayersReusesKnownCommonDirectory
--- PASS: TestSeedLayersReusesKnownCommonDirectory (2.80s)
=== RUN   TestMainRepoRootUsesOneCommonDirectoryQuery
=== RUN   TestMainRepoRootUsesOneCommonDirectoryQuery/olderGit=false
=== RUN   TestMainRepoRootUsesOneCommonDirectoryQuery/olderGit=true
--- PASS: TestMainRepoRootUsesOneCommonDirectoryQuery (0.48s)
    --- PASS: TestMainRepoRootUsesOneCommonDirectoryQuery/olderGit=false (0.26s)
    --- PASS: TestMainRepoRootUsesOneCommonDirectoryQuery/olderGit=true (0.02s)
=== RUN   TestSeedWorktreeUsesGitExcludePatterns
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/wildcard
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/negation
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/anchored
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/directory
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/double_star
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/escaped_leading_marker
=== RUN   TestSeedWorktreeUsesGitExcludePatterns/spaces_and_comments
--- PASS: TestSeedWorktreeUsesGitExcludePatterns (0.77s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/wildcard (0.10s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/negation (0.10s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/anchored (0.09s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/directory (0.09s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/double_star (0.09s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/escaped_leading_marker (0.11s)
    --- PASS: TestSeedWorktreeUsesGitExcludePatterns/spaces_and_comments (0.19s)
=== RUN   TestSeedWorktreeCopiesOnlyManifestSelections
--- PASS: TestSeedWorktreeCopiesOnlyManifestSelections (0.15s)
=== RUN   TestSeedWorktreeDoesNotCopyManifestSelectedUnignoredFile
--- PASS: TestSeedWorktreeDoesNotCopyManifestSelectedUnignoredFile (0.12s)
=== RUN   TestSeedWorktreeFlattensSourceSymlink
--- PASS: TestSeedWorktreeFlattensSourceSymlink (0.10s)
=== RUN   TestSeedWorktreePreservesPrivatePermissions
--- PASS: TestSeedWorktreePreservesPrivatePermissions (0.12s)
=== RUN   TestSeedWorktreeDoesNotExecuteGitFilters
--- PASS: TestSeedWorktreeDoesNotExecuteGitFilters (0.13s)
=== RUN   TestSeedWorktreeDoesNotCopyFilesIgnoredOutsideManifest
--- PASS: TestSeedWorktreeDoesNotCopyFilesIgnoredOutsideManifest (0.08s)
=== RUN   TestSeedWorktreeHonorsCaseInsensitiveTrackedPathsWhenGitConfigIsFalse
--- PASS: TestSeedWorktreeHonorsCaseInsensitiveTrackedPathsWhenGitConfigIsFalse (0.11s)
=== RUN   TestSeedWorktreePreservesTrackedAncestor
--- PASS: TestSeedWorktreePreservesTrackedAncestor (0.13s)
=== RUN   TestSeedWorktreePreservesTrackedDescendant
--- PASS: TestSeedWorktreePreservesTrackedDescendant (0.13s)
=== RUN   TestSeedWorktreeRefusesDestinationSymlink
--- PASS: TestSeedWorktreeRefusesDestinationSymlink (0.08s)
=== RUN   TestSeedWorktreeRefusesToOverwriteUnmanagedDestination
--- PASS: TestSeedWorktreeRefusesToOverwriteUnmanagedDestination (0.09s)
=== RUN   TestSeedWorktreeRefusesToOverwriteUnmanagedAncestor
--- PASS: TestSeedWorktreeRefusesToOverwriteUnmanagedAncestor (0.09s)
=== RUN   TestSeedWorktreeRejectsSymlinkedRoot
--- PASS: TestSeedWorktreeRejectsSymlinkedRoot (0.09s)
=== RUN   TestSeedWorktreeIgnoresUntrackedManifest
--- PASS: TestSeedWorktreeIgnoresUntrackedManifest (0.07s)
=== RUN   TestSeedWorktreeIgnoresDirtyCommittedManifest
--- PASS: TestSeedWorktreeIgnoresDirtyCommittedManifest (0.09s)
=== RUN   TestSeedLayersUnconfiguredParity
    gitvcs_test.go:1543: nested/public.env b0a74961a5090c1264c666489b777537997b8062299d1d3651944f73e2aba0e6
    gitvcs_test.go:1543: selected.env 8559bbd555e074c3c6b224c9b793df4255212832b125894fa775eb9d141e571f
--- PASS: TestSeedLayersUnconfiguredParity (0.08s)
=== RUN   TestSeedLayersDiscoveryAndPrecedence
=== RUN   TestSeedLayersDiscoveryAndPrecedence/absent
=== RUN   TestSeedLayersDiscoveryAndPrecedence/user_alone
=== RUN   TestSeedLayersDiscoveryAndPrecedence/clone_alone
=== RUN   TestSeedLayersDiscoveryAndPrecedence/clone_negates_user
=== RUN   TestSeedLayersDiscoveryAndPrecedence/repository_negates_user
=== RUN   TestSeedLayersDiscoveryAndPrecedence/repository_negates_clone
=== RUN   TestSeedLayersDiscoveryAndPrecedence/repository_readds
=== RUN   TestSeedLayersDiscoveryAndPrecedence/additions
=== RUN   TestSeedLayersDiscoveryAndPrecedence/excluded_parent
=== RUN   TestSeedLayersDiscoveryAndPrecedence/traversable_parent
--- PASS: TestSeedLayersDiscoveryAndPrecedence (0.86s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/absent (0.08s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/user_alone (0.10s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/clone_alone (0.10s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/clone_negates_user (0.10s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/repository_negates_user (0.07s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/repository_negates_clone (0.07s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/repository_readds (0.08s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/additions (0.08s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/excluded_parent (0.10s)
    --- PASS: TestSeedLayersDiscoveryAndPrecedence/traversable_parent (0.10s)
=== RUN   TestSeedLayersMatchOrderedGitFiles
--- PASS: TestSeedLayersMatchOrderedGitFiles (0.08s)
=== RUN   TestSeedLayersInvalidDestinationRevisionRemainsAnError
--- PASS: TestSeedLayersInvalidDestinationRevisionRemainsAnError (0.06s)
=== RUN   TestSeedLayersDanglingCloneSidecarIsAnError
--- PASS: TestSeedLayersDanglingCloneSidecarIsAnError (0.07s)
=== RUN   TestSeedLayersConfigLookup
=== RUN   TestSeedLayersConfigLookup/tilde
=== RUN   TestSeedLayersConfigLookup/relative_spaces
=== RUN   TestSeedLayersConfigLookup/include
=== RUN   TestSeedLayersConfigLookup/includeIf
=== RUN   TestSeedLayersConfigLookup/scope
=== RUN   TestSeedLayersConfigLookup/command_config
=== RUN   TestSeedLayersConfigLookup/empty_file
=== RUN   TestSeedLayersConfigLookup/missing
=== RUN   TestSeedLayersConfigLookup/empty_value
=== RUN   TestSeedLayersConfigLookup/directory
=== RUN   TestSeedLayersConfigLookup/malformed
--- PASS: TestSeedLayersConfigLookup (0.76s)
    --- PASS: TestSeedLayersConfigLookup/tilde (0.07s)
    --- PASS: TestSeedLayersConfigLookup/relative_spaces (0.07s)
    --- PASS: TestSeedLayersConfigLookup/include (0.08s)
    --- PASS: TestSeedLayersConfigLookup/includeIf (0.08s)
    --- PASS: TestSeedLayersConfigLookup/scope (0.08s)
    --- PASS: TestSeedLayersConfigLookup/command_config (0.07s)
    --- PASS: TestSeedLayersConfigLookup/empty_file (0.06s)
    --- PASS: TestSeedLayersConfigLookup/missing (0.06s)
    --- PASS: TestSeedLayersConfigLookup/empty_value (0.07s)
    --- PASS: TestSeedLayersConfigLookup/directory (0.06s)
    --- PASS: TestSeedLayersConfigLookup/malformed (0.06s)
=== RUN   TestSeedLayersSourceContextAndDestinationRevision
--- PASS: TestSeedLayersSourceContextAndDestinationRevision (0.15s)
=== RUN   TestSeedLayersNonstandardMetadataAndIndependentIgnore
--- PASS: TestSeedLayersNonstandardMetadataAndIndependentIgnore (0.10s)
=== RUN   TestSeedLayersOverrideBypassesBrokenDefaults
=== RUN   TestSeedLayersOverrideBypassesBrokenDefaults/configured_file
=== RUN   TestSeedLayersOverrideBypassesBrokenDefaults/clone_directory
=== RUN   TestSeedLayersOverrideBypassesBrokenDefaults/committed_directory
--- PASS: TestSeedLayersOverrideBypassesBrokenDefaults (0.24s)
    --- PASS: TestSeedLayersOverrideBypassesBrokenDefaults/configured_file (0.07s)
    --- PASS: TestSeedLayersOverrideBypassesBrokenDefaults/clone_directory (0.07s)
    --- PASS: TestSeedLayersOverrideBypassesBrokenDefaults/committed_directory (0.10s)
=== RUN   TestSeedWorktreeSkipsFIFOWithoutBlocking
--- PASS: TestSeedWorktreeSkipsFIFOWithoutBlocking (0.08s)
PASS
ok  	github.com/kunchenguid/treehouse/v3/internal/vcs/gitvcs	9.077s
Evidence: JJ availability check
jj not available on PATH; targeted jj scenario cannot run
- Outcome: ⚠️ 1 warning across 1 run (2m6s)

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - medium risk

✅ No issues found.

⚠️ **Test** - 1 warning
  • ⚠️ live validation verdict: inconclusive (3 of 7 scenarios were driven live against the product); untested: Acquisition rejects unreadable persistent layers and quarantines reused slots, Git resolver keeps the common-directory optimization and older-Git fallback, JJ layered seeding works for colocated and non-colocated repositories, Windows target preserves buildability
  • Live validation: ⚠️ inconclusive - 3 of 7 scenarios driven live against the product
Scenario Result Live Evidence
CLI default layered seeding applies user, clone, and committed policies ✅ pass live ~/.no-mistakes/evidence/01M58XKZ8139TC0WR09JB24KZH/cmd-layered.log
CLI include override bypasses broken defaults and supports an empty override ✅ pass live ~/.no-mistakes/evidence/01M58XKZ8139TC0WR09JB24KZH/cmd-layered.log
CLI preserves committed-manifest replacement and rejects unreadable include files safely ✅ pass live ~/.no-mistakes/evidence/01M58XKZ8139TC0WR09JB24KZH/cmd-regression.log
Acquisition rejects unreadable persistent layers and quarantines reused slots ⏸️ untested no The prior payload recorded only package-level executable tests, not a live product session, so it did not establish a live result for this scenario.
Git resolver keeps the common-directory optimization and older-Git fallback ⏸️ untested no The prior payload recorded executable repository tests rather than a live product session, so it did not establish a live result for this scenario.
JJ layered seeding works for colocated and non-colocated repositories ⏸️ untested no Tried the targeted JJ scenario and checked PATH; jj is unavailable, and there is no repository-local JJ binary or source from which to build one without installing or searching outside the workspace…
Windows target preserves buildability ⏸️ untested no The Windows cross-build passed, but this macOS host cannot run the Windows product or measure the Windows cmd/internal-pool package timeout. Provide a Windows runner or the configured Windows CI timin…
  • env -u GIT_CONFIG_GLOBAL GIT_CONFIG_NOSYSTEM=1 XDG_CONFIG_HOME=<temporary> go test ./cmd -run '^(TestGetIncludeFileReplacesCommittedManifest|TestGetIncludeLayersAndOverride|TestGetIncludeFileReadFailureLeavesPoolUntouched)$' -count=1 -v
  • env -u GIT_CONFIG_GLOBAL GIT_CONFIG_NOSYSTEM=1 go test ./internal/pool -run '^TestAcquire_(PersonalLayersRefreshAndInventoryCleanup|PersistentLayerReadErrorsRemoveOrQuarantine)$' -count=1 -v
  • env -u GIT_CONFIG_GLOBAL GIT_CONFIG_NOSYSTEM=1 go test ./internal/vcs/gitvcs -run '^(TestCommonGitDirEnvFallsBackForOlderGitWithSourceEnvironment|TestSeedWorktree.*|TestSeedLayers.*|TestMainRepoRootUsesOneCommonDirectoryQuery)$' -count=1 -v
  • GOOS=windows GOARCH=amd64 go build ./...
  • command -v jj and targeted JJ availability check
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Issues

Closes #189

…sition-resolved common Git directory through Git and jj seeding, including colocated jj, while preserving non-colocated discovery. Added executable regressions for older-Git fallback, single config lookup, and dangling/absent sidecar behavior; restored the authorized state comment. Verified make test, make lint, Windows cross-build, focused tests, and real jj tests. External PR Testing-section publication was not performed in this ci phase
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues remain from this review.

Reviews (3) · Last reviewed commit: "perf(vcs): avoid redundant common-direct..." · Reviewed by Greptile

@ironerumi ironerumi changed the title feat(vcs): layer worktree include rules feat(vcs): support layered gitignored file seeding Oct 18, 2026
@ironerumi

Copy link
Copy Markdown
Author

A note on Windows CI for when this runs: on GitHub's Windows runners the cmd and internal/pool packages sit close to Go's default 10-minute package timeout regardless of this change, and run times vary a lot between runs.

Run cmd internal/pool
main acf59c8, 2026-10-15 (run) 396 s 427 s
main 9edb135, 2026-10-13 (run) 552 s 587 s
#188, 2026-10-18 (run) 605 s timed out at 600 s
this branch on the fork (run) 605 s timed out at 600 s

The latest commit on this branch also reduces Git subprocesses per acquisition below current main (24/32 unconfigured, 27/35 configured, versus 25/33 and 30/38), so the seeding layers add no Git work when no personal layer is configured. If the Windows job times out here, it is likely the same headroom issue #188 hit rather than something this PR introduces.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: thanks @ironerumi. I approved the fork CI runs for 6615129, so CI, Nix, and no-mistakes are running now. Greptile is at 5/5 on this tip. Thanks also for the Windows timing table; we know cmd and internal/pool are near Go's 10-minute package limit on the Windows runners, and that budget question is with Kun.

Nothing is needed from you yet. I'll finish the contract and VISION review once CI finishes. The expected class is opt-in, per #189: with no worktree.includeFile key and no info/worktreeinclude file, an unconfigured get should seed exactly what it does today. I'll check that against the tip before any merge.

One small thing: #189 was closed on 2026-10-18 while this PR is still open. That's fine if you meant to, since this PR's Closes #189 will close it on merge anyway.

This branch has not been deployed

No deployments
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.

Feature request: layer .worktreeinclude like Git layers ignore rules (repo, per-clone, user-global)

2 participants