Repository navigation
ci: clone meta_core, which loop_lib now depends on - #14
Merged
Merged
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
This was referenced Oct 25, 2026
gold-silver-copper
force-pushed
the
fix/meta-config-checks
branch
2 times, most recently
from
October 25, 2026 23:28
a081ca5 to
c11ab26
Compare
Clones loop_lib and meta_core from the same branch when it exists, falling back to main. Implements [[tasks/harmony-2749]]
Implements [[tasks/harmony-2749]]
Implements [[tasks/harmony-2749]]
gold-silver-copper
force-pushed
the
fix/meta-config-checks
branch
from
October 26, 2026 00:00
c11ab26 to
a730852
Compare
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.
CI only:
loop_libnow depends onmeta_core, so CI clones both, from the same branch when it exists.One change set across these PRs, all on branch
fix/meta-config-checks(meta's CI builds each child from that branch):Fixes three findings from the 2026-10-24 fuzz run (all three reproduced on main
4dc5fa3before this change). KB:tasks/harmony-2749, incidentsfuzz-2026-10-24-12,-15and-18.What changed and why
12:
.metapaths and names are validated once, when the config is parsed (meta_core::config::parse_meta_config). Before, an absolutepath, a../path, or a name like../../../../xmademeta git clonewrite outside the workspace, mademeta execrun in unrelated repos, and put worktrees outside.worktrees/. Every caller joinspathonto the config's directory, so checking at the single parse point covers them all. Worktree creation also checks each child destination right before adding it, because the root repo's checkout inside the worktree can hold a symlink where a project's worktree goes. The rules:vendor/tree-sitter-markdown(as gitkb's config has) is fine, because worktrees put the project at<worktree>/<name>._-+@, so that one.metanames the same directory on every platform: no Unicode that macOS or Windows would normalize or fold into another name, noC:,<>:"|?*or~1short names. Also no trailing dot or space (Windows drops it, soa.isa), no device name (NUL,COM1), no.gitcomponent (a project inside a git directory, e.g. its hooks), and no path under.worktrees, as written or through a symlinkpathis relative and stays inside the config's directory: no.., no absolute paths, no existing symlink that leads outside. It is normalized (./a//b/becomesa/b)..meta(meta: true).repocannot start with-, becausegit clone <repo> <path>would read it as an option,(the--tagfilter splits on,)A bad entry fails the command with the file and project named:
A nested
.metathat fails to parse or validate is now an error, both fromwalk_meta_treeand frommeta git clone --recursive/update. Before, its repos were silently dropped and the command exited 0.15: the store lock is an OS file lock (
std::fs::File::try_lock:flockon Unix,LockFileExon Windows). The OS releases it when the holder exits or crashes, so there is no PID and no staleness check. A leftover lock file (empty, garbage, or naming a reused PID) is just a file that gets locked again. The file is never deleted, so two racing processes cannot lock two different files. If the lock is held for more than 5 s, the command fails with a clear message. The lock file is0600(an older one is tightened), because anyone who can open it could hold the lock and block every store write; on Windows a lock directory other accounts can read is refused for the same reason. Store, cache and registry-config files are written0600and read only when they are regular files owned by you that no one else can write (including through a macOS ACL or a Windows access list, inherited or not); the lock file gets the same check for opening. A plugin is run only if no directory or link on the way to it, and not the file itself, can be changed by another user. A lock file that is a symlink is refused, and store writes use a fresh temp file per writer. The PID scheme,is_staleand the WindowsOpenProcessFFI are deleted.meta git worktree create/add/remove/prune, and the cleanup afterexec --ephemeral, now exit non-zero when the store update fails. Before, they printed a warning and exited 0, and the worktree never got TTL or ephemeral cleanup.createrolls back what it added when it cannot record it, using meta-git-safety's rollback (which keeps a branch it cannot prove it made);removereads its store record before deleting, so the record is found even through a symlinked path. Each child worktree destination is checked to stay inside the set before git adds it. When an ephemeralexeccommand fails and its cleanup fails too, the cleanup error is still printed. The worktree plugin also prints the error's full cause chain ({e:#}), so the reason is visible.Multi-user machines:
~/.metais created0700. A data or lock directory that another user owns or can write to is refused before anything in it is read or used. The same applies when it sits behind a directory another user could rename it in: the check walks the path as written, through each symlink it follows, and every directory holding an entry must belong to you or root and be writable by no one else unless sticky (like/tmp). On macOS a directory whose ACL has an allow entry is refused too (ACLs can grant write the mode bits do not show). On Windows the same rule reads each directory's owner and access list: an account other than you, SYSTEM, Administrators or TrustedInstaller that owns the data directory or may change it (or rename it through a directory above) makes meta refuse it, and a link or junction on the way is refused. This covers: the worktree store (soprunecannot act on planted entries), the context cache, the registry config, and global plugins. Plugin discovery prints a warning and skips a refused~/.meta/plugins, and any.meta/pluginsfound walking up from cwd that another user owns or can write to (such as a planted/tmp/.meta/plugins). Plugins in your own checkouts are unaffected unless a directory on the way is group- or world-writable; under umask 002 (the Fedora/RHEL default) checkouts are, so a workspace's.meta/pluginsis skipped with a warning and achmodhint. Programs on PATH are the user's own choice, like thegitmeta runs, and get no such check: only one that leads into a plugin directory refused in the same run (through any link or folder on the way) is skipped. The refusal comes with achmod go-whint.meta_core::meta_dir()now returns an error when there is no home directory, instead of falling back to the shared/tmp/meta-fallback.18: untrusted text is escaped in human-readable output through one helper,
meta_core::display::escape. It writes control characters (C0, DEL, C1), bidi controls and invisible format characters (zero-width, tag characters) as visible\u{1b},\nand so on. It is used in:meta project list: tree, root remote, dependentsmeta context: also escapes|so a value stays in its table cell, because agents read this markdownsshremote-mismatch report and theupdatestray-directory warningmeta_cliworktree-context and orphan warnings, and verbose pathsloop_lib(themeta execengine): directory names in progress and result lines, and dry-run and verbose paths.loop_libnow depends onmeta_corefor this, so its CI andloop_cli's clonemeta_core.Errors are escaped where they leave each program: the
meta-gitandmeta-projectplugins'execute, andmeta'smain. They usedisplay::escape_lines, which keeps newlines, so multi-line errors read as before.--jsonoutput is unchanged; it was already escaped.worktrees_dirin.meta, where worktrees are created and removed, must lie inside the workspace (a relative path, or an absolute one inside it); a directory elsewhere is set withMETA_WORKTREES. A.worktreessymlink leading outside is refused too, and so is a worktree set directory that is a symlink;pruneacts on an expired worktree only while its stored (canonical) path still resolves to itself, so a path that has since become a symlink, or sits under one, is skipped. No project may sit inside the worktree directory, as written or where it resolves, and aworktrees_dirthat breaks these rules is an error when the config is read.How it was verified
.metafile of the real harmony workspace (16 files, copied to a scratch dir) parses with the new rules:meta project list --recursivelists the same 97 projects as main.05-meta-paths-escape-workspace,07-pidless-lock-silent-store-loss,R2-04-terminal-escape-injection) were re-run with throwaway binaries in a scratchHOME. On main all three reproduce. With this change:w1andw2, and prune sees the TTL.project_list.bats(path outside the workspace, symlink escape, terminal escapes) andworktree_edge_cases.bats(leftover lock file, store not writable on create and on remove, data dir writable by others)meta_core: config rules, path normalization, symlink escape, lock, anddisplay; plusmeta_clicontextandmeta_project_clitree testscargo fmt --check,cargo clippy --workspace --all-targets -D warnings, andcargo test --workspacepass, except tworegistry::tests::*_fails_outside_workspacetests. Those fail on unmodified main too on this machine, because they depend on the environment. Theproject_list,worktree_edge_cases,worktreeandcontextbats suites (132 tests) pass.Please look closely at
a640b54, meta_git_lib9f8e270, meta_git_cli79af652). Both changed the same worktree code, someta_git_libandmeta_git_cliare now one squashed commit each on that main. Where both did the same job, meta-git-safety's code was kept (create rollback,git_worktree_add, remove/prune checks) and this PR's own rollback and its tests were dropped. This PR adds on top: store failures in remove/prune fail instead of warning, every child destination is checked beforegit worktree add, and paths, file names and git output in the new safety messages are escaped. Two of its tests usedworktrees_dirvalues the config rules refuse (.and../link); they now assert the refusal or use a link inside the workspace.47eeca3). That change lets--include/--excludeselect repos whose path has..(a repo outside the root, or a second spelling such aslibs/../api). Finding 12 forbids such paths, so these configs are now refused when read, before any repo is selected or any command runs. Its two new tests intests/exec.batswere adapted to that (the recursive--configtest drops its..back-edge; the--excludetest now checks a duplicate spelling is refused). repo_filter's handling of..stays as written but no longer sees such paths.C:\src) inherit "Authenticated Users: Modify", so their.meta\pluginsis skipped and aMETA_DATA_DIRthere is refused, with a hint to remove other accounts' write access (icacls) or use the profile. That is the same rule as on Unix.0775. A~/.metaor a checkout's.meta/pluginsunder such a tree is refused with achmod go-whint (the store fails, plugins are skipped with a warning). An exception for "your own private group" was tried and dropped: POSIX ACLs and other accounts sharing that primary group can widen group access in ways the mode bits do not show..metais parsed, and checked, after its repo is cloned.aanda/bmust movea/binto a nested.meta. Themeta_mcpatomic-batch test used such a config; meta_mcp#12 switches it to a plain directory inside the workspace root's repository, which keeps it covering the same refusal (a project that is not the root of its own repository).meta git commit --editno longer writes its message template to a fixed/tmp/META_COMMIT_EDITMSG; it uses an exclusively created file in~/.meta/tmp. So it now needs a private data directory, like the worktree store (reviewers disagreed on whether a random file in the system temp dir would do; the owner's rule is no shared/tmpdefault).lockdhave it. Where there is none, a store write fails with "Failed to lock …" instead of silently doing nothing.loop_lib(published) now depends onmeta_core(published) with the range<1. Child-repo CI buildsmeta_coreas 0.0.0 or 0.1.0, and releases use the workspace version, so both satisfy it. The release workflow publishesmeta_corebeforeloop_lib.worktrees_diroutside the workspace is now refused, with a pointer toMETA_WORKTREES.meta_plugin_protocolprints what plugins hand it; the plugins escape their errors before that. (meta git snapshotno longer exists: meta-git-safety deleted it.)escape_linesescapes every control character except\n, because several errors are multi-line by design. Paths in the errors this PR writes are escaped where the message is built.Overlap with other open meta PRs
meta_git_cli(worktreemod.rs,create.rs,remove.rs,prune.rs,add.rs,ssh.rs,update.rs) andmeta_cli(main.rs,context.rs) are also touched by themeta-git-safetyandmeta-shell-argswork. The edits here are small and local (store error handling and escaping at print sites), so whichever merges second should rebase easily.