mirror of
https://github.com/CharlesWiltgen/Axiom.git
synced 2026-09-20 19:58:20 +08:00
fix(hooks): stop reading a system temp root as an Apple project marker
Both cursor adapter contract tests failed on main: they assert that a temp workspace is not an Apple project, and the detector said it was. Root cause is not the tests and not the adapter — the upward walk found an Apple marker sitting in the shared temp root itself ($TMPDIR/plan-test.swift, left by an unrelated tool), so every cwd beneath $TMPDIR inherited it. Same class as GH #52's ~/.swiftpm: tool state, not project evidence. - neutralize the temp root in the upward marker walk ($TMPDIR plus /tmp, /var/tmp, /private/tmp, abspath and realpath forms) - treat the temp root as a vacuous scan root, so containment there never falls through to a whole-tree scan - revert the failing tests to green with three new detector tests: a marker in the temp root is not evidence, the temp root is not a project, and a project that genuinely lives inside a temp dir is STILL detected (over-correction guard) Regenerated axiom-codex/ and axiom-cursor/ mirrors.
This commit is contained in:
@@ -15,6 +15,7 @@ no match statements, no runtime PEP 604 unions.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# Presence of any of these in a directory marks it an Apple project. ".swift"
|
||||
# covers Package.swift and Project.swift, so only Podfile needs an exact name.
|
||||
@@ -38,6 +39,31 @@ UPWARD_MAX_LEVELS = 6 # ancestor cap when there is no .git root
|
||||
DOWNWARD_MAX_DEPTH = 4 # downward-scan depth below the scan root
|
||||
MAX_ENTRIES = 10000 # downward scan safety cap → fail-open on hit
|
||||
|
||||
# Marker names at a system temp root carry no signal: the directory is shared,
|
||||
# per-user, long-lived, and collects other programs' scratch files. A single
|
||||
# stray `plan-test.swift` in macOS's $TMPDIR made every cwd beneath it read as an
|
||||
# Apple project — which fired the Cursor prompt router in non-Apple workspaces and
|
||||
# broke two adapter contract tests (Axiom-3k2i). Same class as GH #52's
|
||||
# ~/.swiftpm: tool state, not project evidence. Only the temp root itself is
|
||||
# neutralized, so a project that genuinely lives inside a temp directory is still
|
||||
# detected by its own markers.
|
||||
TEMP_ROOT_NAMES = ("/tmp", "/var/tmp", "/private/tmp")
|
||||
|
||||
|
||||
def _system_temp_roots() -> frozenset[str]:
|
||||
"""System temp directories in both abspath and realpath form."""
|
||||
roots = set(TEMP_ROOT_NAMES)
|
||||
try:
|
||||
roots.add(tempfile.gettempdir())
|
||||
except Exception:
|
||||
pass # gettempdir is documented not to raise, but never fail the gate
|
||||
env = os.environ.get("TMPDIR")
|
||||
if env:
|
||||
roots.add(env)
|
||||
return frozenset(
|
||||
form for root in roots for form in (os.path.abspath(root), os.path.realpath(root))
|
||||
)
|
||||
|
||||
|
||||
def _is_marker(name: str) -> bool:
|
||||
"""True if `name` identifies an Apple project.
|
||||
@@ -158,6 +184,7 @@ def is_apple_project(start: str) -> bool:
|
||||
return True # nonexistent/unreadable start (deleted cwd, etc.) → fail-open
|
||||
home = os.environ.get("HOME")
|
||||
home = os.path.abspath(home) if home else None
|
||||
temp_roots = _system_temp_roots()
|
||||
scan_root = cur
|
||||
found_repo_root = False
|
||||
prev = None
|
||||
@@ -168,7 +195,7 @@ def is_apple_project(start: str) -> bool:
|
||||
# NOT bounded by that cap — a git root is found however deep we were
|
||||
# opened, so a real Apple repo opened many directories deep is never
|
||||
# misread as non-Apple (the cap used to short-circuit this — GH #45).
|
||||
if levels <= UPWARD_MAX_LEVELS and _dir_has_marker(cur):
|
||||
if levels <= UPWARD_MAX_LEVELS and cur not in temp_roots and _dir_has_marker(cur):
|
||||
return True
|
||||
if os.path.exists(os.path.join(cur, ".git")): # file (worktree) or dir
|
||||
# A .git at $HOME (dotfiles repo) must NOT widen the scan root:
|
||||
@@ -194,7 +221,7 @@ def is_apple_project(start: str) -> bool:
|
||||
prev = cur
|
||||
levels += 1
|
||||
cur = parent
|
||||
if _is_vacuous_scan_root(scan_root, home, found_repo_root):
|
||||
if scan_root in temp_roots or _is_vacuous_scan_root(scan_root, home, found_repo_root):
|
||||
return False
|
||||
return _downward_has_marker(scan_root)
|
||||
except Exception:
|
||||
|
||||
@@ -354,6 +354,33 @@ class TestIsAppleProject(unittest.TestCase):
|
||||
self.assertTrue(pd.is_apple_project(work))
|
||||
|
||||
|
||||
def test_marker_in_the_system_temp_root_is_not_evidence(self):
|
||||
# Axiom-3k2i: macOS's per-user $TMPDIR is long-lived and collects other
|
||||
# programs' scratch files. One stray `plan-test.swift` at its top level made
|
||||
# EVERY cwd beneath it read as an Apple project, which fired the Cursor
|
||||
# prompt router in non-Apple workspaces and broke two adapter contract tests.
|
||||
root = tempfile.gettempdir()
|
||||
probe = os.path.join(root, "axiom-detect-probe-{}.swift".format(os.getpid()))
|
||||
touch(probe)
|
||||
try:
|
||||
with tempfile.TemporaryDirectory(dir=root) as work:
|
||||
self.assertFalse(pd.is_apple_project(work))
|
||||
finally:
|
||||
os.remove(probe)
|
||||
|
||||
def test_temp_root_itself_is_not_a_project(self):
|
||||
# Shared scratch space: containment there is as meaningless as it is at
|
||||
# $HOME, so the walk must not fall through to a whole-tree scan.
|
||||
self.assertFalse(pd.is_apple_project(tempfile.gettempdir()))
|
||||
|
||||
def test_project_inside_the_temp_root_is_still_detected(self):
|
||||
# Guard against over-correcting: only the temp ROOT is neutralized, so a
|
||||
# project that genuinely lives in a temp directory is still found.
|
||||
with tempfile.TemporaryDirectory(dir=tempfile.gettempdir()) as work:
|
||||
touch(os.path.join(work, "Package.swift"))
|
||||
self.assertTrue(pd.is_apple_project(work))
|
||||
|
||||
|
||||
class TestResolveContextDecision(unittest.TestCase):
|
||||
def test_never_skips_even_in_apple_dir(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
|
||||
Generated
+29
-2
@@ -15,6 +15,7 @@ no match statements, no runtime PEP 604 unions.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# Presence of any of these in a directory marks it an Apple project. ".swift"
|
||||
# covers Package.swift and Project.swift, so only Podfile needs an exact name.
|
||||
@@ -38,6 +39,31 @@ UPWARD_MAX_LEVELS = 6 # ancestor cap when there is no .git root
|
||||
DOWNWARD_MAX_DEPTH = 4 # downward-scan depth below the scan root
|
||||
MAX_ENTRIES = 10000 # downward scan safety cap → fail-open on hit
|
||||
|
||||
# Marker names at a system temp root carry no signal: the directory is shared,
|
||||
# per-user, long-lived, and collects other programs' scratch files. A single
|
||||
# stray `plan-test.swift` in macOS's $TMPDIR made every cwd beneath it read as an
|
||||
# Apple project — which fired the Cursor prompt router in non-Apple workspaces and
|
||||
# broke two adapter contract tests (Axiom-3k2i). Same class as GH #52's
|
||||
# ~/.swiftpm: tool state, not project evidence. Only the temp root itself is
|
||||
# neutralized, so a project that genuinely lives inside a temp directory is still
|
||||
# detected by its own markers.
|
||||
TEMP_ROOT_NAMES = ("/tmp", "/var/tmp", "/private/tmp")
|
||||
|
||||
|
||||
def _system_temp_roots() -> frozenset[str]:
|
||||
"""System temp directories in both abspath and realpath form."""
|
||||
roots = set(TEMP_ROOT_NAMES)
|
||||
try:
|
||||
roots.add(tempfile.gettempdir())
|
||||
except Exception:
|
||||
pass # gettempdir is documented not to raise, but never fail the gate
|
||||
env = os.environ.get("TMPDIR")
|
||||
if env:
|
||||
roots.add(env)
|
||||
return frozenset(
|
||||
form for root in roots for form in (os.path.abspath(root), os.path.realpath(root))
|
||||
)
|
||||
|
||||
|
||||
def _is_marker(name: str) -> bool:
|
||||
"""True if `name` identifies an Apple project.
|
||||
@@ -158,6 +184,7 @@ def is_apple_project(start: str) -> bool:
|
||||
return True # nonexistent/unreadable start (deleted cwd, etc.) → fail-open
|
||||
home = os.environ.get("HOME")
|
||||
home = os.path.abspath(home) if home else None
|
||||
temp_roots = _system_temp_roots()
|
||||
scan_root = cur
|
||||
found_repo_root = False
|
||||
prev = None
|
||||
@@ -168,7 +195,7 @@ def is_apple_project(start: str) -> bool:
|
||||
# NOT bounded by that cap — a git root is found however deep we were
|
||||
# opened, so a real Apple repo opened many directories deep is never
|
||||
# misread as non-Apple (the cap used to short-circuit this — GH #45).
|
||||
if levels <= UPWARD_MAX_LEVELS and _dir_has_marker(cur):
|
||||
if levels <= UPWARD_MAX_LEVELS and cur not in temp_roots and _dir_has_marker(cur):
|
||||
return True
|
||||
if os.path.exists(os.path.join(cur, ".git")): # file (worktree) or dir
|
||||
# A .git at $HOME (dotfiles repo) must NOT widen the scan root:
|
||||
@@ -194,7 +221,7 @@ def is_apple_project(start: str) -> bool:
|
||||
prev = cur
|
||||
levels += 1
|
||||
cur = parent
|
||||
if _is_vacuous_scan_root(scan_root, home, found_repo_root):
|
||||
if scan_root in temp_roots or _is_vacuous_scan_root(scan_root, home, found_repo_root):
|
||||
return False
|
||||
return _downward_has_marker(scan_root)
|
||||
except Exception:
|
||||
|
||||
+3
-3
@@ -343,8 +343,8 @@
|
||||
},
|
||||
{
|
||||
"path": "scripts/project_detect.py",
|
||||
"sha256": "3c7a5f0c8092e0712848a22052c7d49e7d04cef8b93826413f7d1f24b6f936b4",
|
||||
"bytes": 10189
|
||||
"sha256": "c4cd1310c95915b32dbffe571d1fe5f5e4a282d3c167934c572ae2880d405184",
|
||||
"bytes": 11429
|
||||
},
|
||||
{
|
||||
"path": "scripts/subagent-start.py",
|
||||
@@ -1879,7 +1879,7 @@
|
||||
],
|
||||
"totals": {
|
||||
"files": 375,
|
||||
"bytes": 7370347
|
||||
"bytes": 7371587
|
||||
},
|
||||
"excludedMirrors": 30,
|
||||
"classes": {
|
||||
|
||||
Generated
+29
-2
@@ -15,6 +15,7 @@ no match statements, no runtime PEP 604 unions.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# Presence of any of these in a directory marks it an Apple project. ".swift"
|
||||
# covers Package.swift and Project.swift, so only Podfile needs an exact name.
|
||||
@@ -38,6 +39,31 @@ UPWARD_MAX_LEVELS = 6 # ancestor cap when there is no .git root
|
||||
DOWNWARD_MAX_DEPTH = 4 # downward-scan depth below the scan root
|
||||
MAX_ENTRIES = 10000 # downward scan safety cap → fail-open on hit
|
||||
|
||||
# Marker names at a system temp root carry no signal: the directory is shared,
|
||||
# per-user, long-lived, and collects other programs' scratch files. A single
|
||||
# stray `plan-test.swift` in macOS's $TMPDIR made every cwd beneath it read as an
|
||||
# Apple project — which fired the Cursor prompt router in non-Apple workspaces and
|
||||
# broke two adapter contract tests (Axiom-3k2i). Same class as GH #52's
|
||||
# ~/.swiftpm: tool state, not project evidence. Only the temp root itself is
|
||||
# neutralized, so a project that genuinely lives inside a temp directory is still
|
||||
# detected by its own markers.
|
||||
TEMP_ROOT_NAMES = ("/tmp", "/var/tmp", "/private/tmp")
|
||||
|
||||
|
||||
def _system_temp_roots() -> frozenset[str]:
|
||||
"""System temp directories in both abspath and realpath form."""
|
||||
roots = set(TEMP_ROOT_NAMES)
|
||||
try:
|
||||
roots.add(tempfile.gettempdir())
|
||||
except Exception:
|
||||
pass # gettempdir is documented not to raise, but never fail the gate
|
||||
env = os.environ.get("TMPDIR")
|
||||
if env:
|
||||
roots.add(env)
|
||||
return frozenset(
|
||||
form for root in roots for form in (os.path.abspath(root), os.path.realpath(root))
|
||||
)
|
||||
|
||||
|
||||
def _is_marker(name: str) -> bool:
|
||||
"""True if `name` identifies an Apple project.
|
||||
@@ -158,6 +184,7 @@ def is_apple_project(start: str) -> bool:
|
||||
return True # nonexistent/unreadable start (deleted cwd, etc.) → fail-open
|
||||
home = os.environ.get("HOME")
|
||||
home = os.path.abspath(home) if home else None
|
||||
temp_roots = _system_temp_roots()
|
||||
scan_root = cur
|
||||
found_repo_root = False
|
||||
prev = None
|
||||
@@ -168,7 +195,7 @@ def is_apple_project(start: str) -> bool:
|
||||
# NOT bounded by that cap — a git root is found however deep we were
|
||||
# opened, so a real Apple repo opened many directories deep is never
|
||||
# misread as non-Apple (the cap used to short-circuit this — GH #45).
|
||||
if levels <= UPWARD_MAX_LEVELS and _dir_has_marker(cur):
|
||||
if levels <= UPWARD_MAX_LEVELS and cur not in temp_roots and _dir_has_marker(cur):
|
||||
return True
|
||||
if os.path.exists(os.path.join(cur, ".git")): # file (worktree) or dir
|
||||
# A .git at $HOME (dotfiles repo) must NOT widen the scan root:
|
||||
@@ -194,7 +221,7 @@ def is_apple_project(start: str) -> bool:
|
||||
prev = cur
|
||||
levels += 1
|
||||
cur = parent
|
||||
if _is_vacuous_scan_root(scan_root, home, found_repo_root):
|
||||
if scan_root in temp_roots or _is_vacuous_scan_root(scan_root, home, found_repo_root):
|
||||
return False
|
||||
return _downward_has_marker(scan_root)
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user