fix(transcend): skip repository copies in scratchpads
A scan on the real host offered 4276 candidates, 4253 of them files of two repository copies in one peer's scratchpad. scratch_candidates now walks with os.walk, skips any directory holding .git, roles, plugins, pyproject.toml, package.json or a Makefile, and descends at most one level below a scratchpad; the real scan drops to 59 candidates. test_transcend adds a nested helper that stays, a deeper one and a repository copy that drop out (negative control against the unfiltered scan fails). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
194f5e28bd
commit
3e2f7a7ddb
3 files changed
+25
-6
No files matched your search
@@ -19,7 +19,9 @@ transcend.py mark --state <same> <path> carried|dropped|later
|
|||||||
scratchpad, every peer session's scratchpad, and the agent homes
|
scratchpad, every peer session's scratchpad, and the agent homes
|
||||||
(`~/.claude`, `~/.agents`, `~/.codex`) for skills, commands, agents, hooks
|
(`~/.claude`, `~/.agents`, `~/.codex`) for skills, commands, agents, hooks
|
||||||
and rules the skills repository does not ship. It keeps scripts, tests and
|
and rules the skills repository does not ship. It keeps scripts, tests and
|
||||||
skills, and skips files named like secrets and files over 1 MB. Carried or
|
skills at most one directory below a scratchpad, and skips files named like
|
||||||
|
secrets, files over 1 MB, and repository copies (a directory holding `.git`,
|
||||||
|
`roles`, `plugins`, `pyproject.toml`, `package.json` or a `Makefile`). Carried or
|
||||||
dropped candidates return only when their content changes; `later` returns
|
dropped candidates return only when their content changes; `later` returns
|
||||||
every time.
|
every time.
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ SECRET_NAME = re.compile(
|
|||||||
r"(secret|token|credential|passw|auth|\.env$|id_[a-z0-9]+$|\.pem$|\.key$)", re.IGNORECASE
|
r"(secret|token|credential|passw|auth|\.env$|id_[a-z0-9]+$|\.pem$|\.key$)", re.IGNORECASE
|
||||||
)
|
)
|
||||||
MAX_BYTES = 1_000_000
|
MAX_BYTES = 1_000_000
|
||||||
|
MAX_DEPTH = 1
|
||||||
|
REPO_MARKERS = {".git", "roles", "plugins", "pyproject.toml", "package.json", "Makefile"}
|
||||||
HOME_SOURCES = {
|
HOME_SOURCES = {
|
||||||
".claude/skills": "skill",
|
".claude/skills": "skill",
|
||||||
".agents/skills": "skill",
|
".agents/skills": "skill",
|
||||||
@@ -74,11 +76,21 @@ def session_names(home: Path) -> dict[str, str]:
|
|||||||
|
|
||||||
def scratch_candidates(root: Path, source: str, owner: str) -> list[dict]:
|
def scratch_candidates(root: Path, source: str, owner: str) -> list[dict]:
|
||||||
found = []
|
found = []
|
||||||
for path in sorted(p for p in root.rglob("*") if p.is_file()):
|
for current, dirs, files in os.walk(root):
|
||||||
kind = file_kind(path)
|
here = Path(current)
|
||||||
if kind:
|
depth = len(here.relative_to(root).parts)
|
||||||
found.append({"source": source, "owner": owner, "kind": kind, "path": str(path)})
|
if depth and REPO_MARKERS & {*dirs, *files}:
|
||||||
return found
|
dirs.clear()
|
||||||
|
continue
|
||||||
|
if depth >= MAX_DEPTH:
|
||||||
|
dirs.clear()
|
||||||
|
for name in files:
|
||||||
|
kind = file_kind(here / name)
|
||||||
|
if kind:
|
||||||
|
found.append(
|
||||||
|
{"source": source, "owner": owner, "kind": kind, "path": str(here / name)}
|
||||||
|
)
|
||||||
|
return sorted(found, key=lambda item: item["path"])
|
||||||
|
|
||||||
|
|
||||||
def shipped(skills_repo: Path) -> set[str]:
|
def shipped(skills_repo: Path) -> set[str]:
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ class TestTranscend(unittest.TestCase):
|
|||||||
self._write(self.own / "auth_token.py", "secret")
|
self._write(self.own / "auth_token.py", "secret")
|
||||||
self._write(self.own / "big.py", "x" * 1_000_001)
|
self._write(self.own / "big.py", "x" * 1_000_001)
|
||||||
self._write(self.peer / "helper.sh", "echo hi")
|
self._write(self.peer / "helper.sh", "echo hi")
|
||||||
|
self._write(self.peer / "probe" / "nested.py", "print(2)")
|
||||||
|
self._write(self.peer / "probe" / "deep" / "too_deep.py", "print(3)")
|
||||||
|
self._write(self.peer / "copy" / "pyproject.toml", "[project]")
|
||||||
|
self._write(self.peer / "copy" / "copied.py", "print(4)")
|
||||||
for name in ("kvb-x", "caveman", "custom"):
|
for name in ("kvb-x", "caveman", "custom"):
|
||||||
self._write(self.home / ".claude" / "skills" / name / "SKILL.md", name)
|
self._write(self.home / ".claude" / "skills" / name / "SKILL.md", name)
|
||||||
self._write(self.home / ".codex" / "rules" / "default.rules", "allow")
|
self._write(self.home / ".codex" / "rules" / "default.rules", "allow")
|
||||||
@@ -78,6 +82,7 @@ class TestTranscend(unittest.TestCase):
|
|||||||
"probe.py": ("own", "self", "script"),
|
"probe.py": ("own", "self", "script"),
|
||||||
"test_probe.py": ("own", "self", "test"),
|
"test_probe.py": ("own", "self", "test"),
|
||||||
"helper.sh": ("peer", "Peer", "script"),
|
"helper.sh": ("peer", "Peer", "script"),
|
||||||
|
"nested.py": ("peer", "Peer", "script"),
|
||||||
"custom": ("home", "operator", "skill"),
|
"custom": ("home", "operator", "skill"),
|
||||||
"default.rules": ("home", "operator", "rule"),
|
"default.rules": ("home", "operator", "rule"),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user