feat(sync): allow nested local roots via a managed primary-folder ignore block#157
Merged
Conversation
…ore block
An additional mapping's local root may now nest inside the primary
mapping's local root (./results inside the repo) — the natural place
for a results directory — instead of forcing an awkward sibling
placement. Remote roots stay disjoint-only.
okdev maintains a managed block in the primary root's .stignore
("// okdev:begin/end managed sync excludes"; syncthing comments are
"//", not "#") holding one /rel-path pattern per nested child, so the
subtree travels only through its own folder. Ordering is a hard
guarantee, not a race: adding a mapping changes the sync config hash,
which restarts the sync process chain, and the block is merged into the
file BEFORE the local syncthing daemon starts — its first scan already
honors the exclusion.
Removing a mapping never silently widens the primary folder: the child
folder is pruned from both syncthing instances and local files are
kept, but the ignore entry is retained as a tombstone with an
explanatory comment; re-inclusion is the user's explicit one-line
deletion. Sync start prints a notice for newly retained entries, and
status --details lists active and retained excludes.
Other local overlaps (equal roots, nesting between additional mappings,
an extra containing the primary) remain rejected. Kind e2e now runs the
nested layout end-to-end: exclusion effective from first scan (no leak
into /workspace), results flow through the child folder, and removal
leaves a tombstone with the subtree still excluded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ted mappings - Skill: SKILL.md gets a sync-capabilities line; workflows.md gains the result-collection-via-sync recipe; troubleshooting.md explains the two by-design "not syncing" cases (direction contract, managed excludes / tombstones) before fault-hunting. - docs/troubleshooting.md: drop the stale single-mapping claim, add the same by-design guidance, and qualify the versioning section (directional mappings are structurally immune to cross-side overwrites). - command-reference: nested-local + tombstone semantics under `okdev sync`; the already-active no-op wording follows the new default-mode semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #156: the disjoint-only rule forced results directories to live outside the repo (
../collected), which is awkward. An additional mapping's local root may now nest inside the primary mapping's local root —./resultsinside the repo just works. Remote roots stay disjoint-only.How the exclusion is guaranteed (not raced)
The primary folder must not carry the nested subtree, or the same files would have two opposing authorities. okdev maintains a managed block in the primary root's
.stignore:(Syncthing's comment syntax is
//—#...lines would be patterns.) Ordering is structural: adding a mapping changes the sync config hash → the sync process chain restarts → the block is merged into the file before the local syncthing daemon starts, so the daemon's first scan already honors it. If a hot-apply path is ever added, it must go throughPUT /rest/db/ignores(atomic), never raw file edits.Removal semantics: never silently widen a folder
Removing a nested mapping prunes its syncthing folder (both sides) and keeps local files, but the ignore entry is retained as a tombstone:
Without this, removal would bulk-sync the subtree through the code channel — a surprise transfer with data-overwrite potential. Re-inclusion is an explicit one-line deletion. Sync start prints a notice for newly retained entries;
status --detailslists active and retained excludes.Validation matrix
.stignore), nesting between additional mappings, an extra containing the primary, any remote overlapTesting
/rel/path, sorted), managed-block merge (create / idempotent / tombstone / re-add / user content preserved before & after block), no-op without file+patterns, status rendering../collectedmapping — exclusion effective from first scan (/workspace/collectednever appears on the pod while results flow through the child folder), managed block asserted in.stignore, status shows the exclude; then the mapping is removed and the tombstone + still-excluded behavior is asserted end-to-end.gofmt -lclean;go test -race ./...passes.🤖 Generated with Claude Code