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:
Charles Wiltgen
2026-09-15 08:41:22 -07:00
parent e5f1018b97
commit ba04043bf8
5 changed files with 117 additions and 9 deletions
@@ -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:
+29 -2
View File
@@ -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
View File
@@ -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": {
+29 -2
View File
@@ -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: