Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9cf3d58
Ignore notes & snippets subdirs in `git`
goodboy Apr 10, 2026
d3d6f64
Reorganize `.gitignore` by skill/purpose
goodboy Apr 11, 2026
ba86d48
Add `lastfailed` cache inspection to `/run-tests` skill
goodboy Apr 14, 2026
b1a0753
Expand `/run-tests` venv pre-flight to cover all cases
goodboy Apr 14, 2026
b524ee4
Bump `xonsh` to latest pre `0.23` release
goodboy Apr 16, 2026
64ddc42
Pin `xonsh` to GH `main` in editable mode
goodboy Apr 17, 2026
d318f1f
Add `'subint'` spawn backend scaffold (#379)
goodboy Apr 17, 2026
d2ea8aa
Handle py3.14+ incompats as test skips
goodboy Apr 17, 2026
b8f243e
Impl min-viable `subint` spawn backend (B.2)
goodboy Apr 17, 2026
03bf2b9
Avoid skip `.ipc._ringbuf` import when no `cffi`
goodboy Apr 17, 2026
8a8d01e
Doc the `_interpreters` private-API choice in `_subint`
goodboy Apr 18, 2026
31cbd11
Fix subint destroy race via dedicated OS thread
goodboy Apr 18, 2026
c041518
Add prompt-IO log for subint destroy-race fix
goodboy Apr 18, 2026
99541fe
Bound subint teardown shields with hard-kill timeout
goodboy Apr 18, 2026
09466a1
Add `._debug_hangs` to `.devx` for hang triage
goodboy Apr 18, 2026
34d9d48
Raise `subint` floor to py3.14 and split dep-groups
goodboy Apr 20, 2026
2ed5e6a
Add `subint` cancellation + hard-kill test audit
goodboy Apr 20, 2026
4a32545
Doc `subint` backend hang classes + arm `dump_on_hang`
goodboy Apr 20, 2026
a65fded
Add prompt-io log for `subint` hang-class docs
goodboy Apr 20, 2026
189f4e3
Wall-cap `subint` audit tests via `pytest-timeout`
goodboy Apr 21, 2026
a6cbac9
Bump lock-file for `pytest-timeout` + 3.13 gated wheel-deps
goodboy Apr 21, 2026
5998774
Add global 200s `pytest-timeout`
goodboy Apr 21, 2026
985ea76
Skip `test_stale_entry_is_deleted` hanger with `subint`s
goodboy Apr 21, 2026
f3cea71
Expand `subint` sigint-starvation hang catalog
goodboy Apr 21, 2026
3b26b59
Add `skipon_spawn_backend` pytest marker
goodboy Apr 22, 2026
4b2a088
Mark `subint`-hanging tests with `skipon_spawn_backend`
goodboy Apr 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 131 additions & 22 deletions .claude/skills/run-tests/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ allowed-tools:
- Bash(python -m pytest *)
- Bash(python -c *)
- Bash(python --version *)
- Bash(git rev-parse *)
- Bash(UV_PROJECT_ENVIRONMENT=py* uv run python *)
- Bash(UV_PROJECT_ENVIRONMENT=py* uv run pytest *)
- Bash(UV_PROJECT_ENVIRONMENT=py* uv sync *)
- Bash(UV_PROJECT_ENVIRONMENT=py* uv pip show *)
- Bash(git rev-parse *)
- Bash(ls *)
- Bash(cat *)
- Bash(jq * .pytest_cache/*)
- Read
- Grep
- Glob
- Task
- AskUserQuestion
---

Run the `tractor` test suite using `pytest`. Follow this
Expand Down Expand Up @@ -90,41 +95,104 @@ python -m pytest tests/ -x --tb=short --no-header --tpt-proto uds
python -m pytest tests/ -x --tb=short --no-header -k "cancel and not slow"
```

## 3. Pre-flight checks (before running tests)
## 3. Pre-flight: venv detection (MANDATORY)

### Worktree venv detection
**Always verify a `uv` venv is active before running
`python` or `pytest`.** This project uses
`UV_PROJECT_ENVIRONMENT=py<MINOR>` naming (e.g.
`py313`) — never `.venv`.

If running inside a git worktree (`git rev-parse
--git-common-dir` differs from `--git-dir`), verify
the Python being used is from the **worktree's own
venv**, not the main repo's. Check:
### Step 1: detect active venv

Run this check first:

```sh
python -c "
import sys, os
venv = os.environ.get('VIRTUAL_ENV', '')
prefix = sys.prefix
print(f'VIRTUAL_ENV={venv}')
print(f'sys.prefix={prefix}')
print(f'executable={sys.executable}')
"
```

### Step 2: interpret results

**Case A — venv is active** (`VIRTUAL_ENV` is set
and points to a `py<MINOR>/` dir under the project
root or worktree):

Use bare `python` / `python -m pytest` for all
commands. This is the normal, fast path.

**Case B — no venv active** (`VIRTUAL_ENV` is empty
or `sys.prefix` points to a system Python):

Use `AskUserQuestion` to ask the user:

> "No uv venv is active. Should I activate one
> via `UV_PROJECT_ENVIRONMENT=py<MINOR> uv sync`,
> or would you prefer to activate your shell venv
> first?"

Options:
1. **"Create/sync venv"** — run
`UV_PROJECT_ENVIRONMENT=py<MINOR> uv sync` where
`<MINOR>` is detected from `python --version`
(e.g. `313` for 3.13). Then use
`py<MINOR>/bin/python` for all subsequent
commands in this session.
2. **"I'll activate it myself"** — stop and let the
user `source py<MINOR>/bin/activate` or similar.

**Case C — inside a git worktree** (`git rev-parse
--git-common-dir` differs from `--git-dir`):

Verify Python resolves from the **worktree's own
venv**, not the main repo's:

```sh
python -c "import tractor; print(tractor.__file__)"
```

If the path points outside the worktree (e.g. to
the main repo), set up a local venv first:
If the path points outside the worktree, create a
worktree-local venv:

```sh
UV_PROJECT_ENVIRONMENT=py<MINOR> uv sync
```

where `<MINOR>` matches the active cpython minor
version (detect via `python --version`, e.g.
`py313` for 3.13, `py314` for 3.14). Then use
`py<MINOR>/bin/python` for all subsequent commands.
Then use `py<MINOR>/bin/python` for all commands.

**Why this matters**: without a worktree-local venv,
subprocesses spawned by tractor resolve modules from
the main repo's editable install, causing spurious
`AttributeError` / `ModuleNotFoundError` for code
that only exists on the worktree's branch.
**Why this matters**: without the correct venv,
subprocesses spawned by tractor resolve modules
from the wrong editable install, causing spurious
`AttributeError` / `ModuleNotFoundError`.

### Import + collection checks
### Fallback: `uv run`

Always run these, especially after refactors or
module moves — they catch import errors instantly:
If the user can't or won't activate a venv, all
`python` and `pytest` commands can be prefixed
with `UV_PROJECT_ENVIRONMENT=py<MINOR> uv run`:

```sh
# instead of: python -m pytest tests/ -x
UV_PROJECT_ENVIRONMENT=py313 uv run pytest tests/ -x

# instead of: python -c 'import tractor'
UV_PROJECT_ENVIRONMENT=py313 uv run python -c 'import tractor'
```

`uv run` auto-discovers the project and venv,
but is slower than a pre-activated venv due to
lock-file resolution on each invocation. Prefer
activating the venv when possible.

### Step 3: import + collection checks

After venv is confirmed, always run these
(especially after refactors or module moves):

```sh
# 1. package import smoke check
Expand Down Expand Up @@ -217,7 +285,48 @@ python -c 'import tractor' && python -m pytest tests/ -x -q --co 2>&1 | tail -3
python -m pytest tests/test_local.py tests/test_rpc.py tests/test_spawning.py tests/discovery/test_registrar.py -x --tb=short --no-header
```

### Re-run last failures only:
### Inspect last failures (without re-running):

When the user asks "what failed?", "show failures",
or wants to check the last-failed set before
re-running — read the pytest cache directly. This
is instant and avoids test collection overhead.

```sh
python -c "
import json, pathlib, sys
p = pathlib.Path('.pytest_cache/v/cache/lastfailed')
if not p.exists():
print('No lastfailed cache found.'); sys.exit()
data = json.loads(p.read_text())
# filter to real test node IDs (ignore junk
# entries that can accumulate from system paths)
tests = sorted(k for k in data if k.startswith('tests/'))
if not tests:
print('No failures recorded.')
else:
print(f'{len(tests)} last-failed test(s):')
for t in tests:
print(f' {t}')
"
```

**Why not `--cache-show` or `--co --lf`?**

- `pytest --cache-show 'cache/lastfailed'` works
but dumps raw dict repr including junk entries
(stale system paths that leak into the cache).
- `pytest --co --lf` actually *collects* tests which
triggers import resolution and is slow (~0.5s+).
Worse, when cached node IDs don't exactly match
current parametrize IDs (e.g. param names changed
between runs), pytest falls back to collecting
the *entire file*, giving false positives.
- Reading the JSON directly is instant, filterable
to `tests/`-prefixed entries, and shows exactly
what pytest recorded — no interpretation.

**After inspecting**, re-run the failures:
```sh
python -m pytest --lf -x --tb=short --no-header
```
Expand Down
65 changes: 35 additions & 30 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,46 +106,55 @@ venv.bak/
# all files under
.git/

# any commit-msg gen tmp files
.claude/skills/commit-msg/msgs/
.claude/git_commit_msg_LATEST.md
.claude/*_commit_*.md
.claude/*_commit*.toml
.claude/*_commit*.txt
.claude/skills/commit-msg/msgs/*
# require very explicit staging for anything we **really**
# want put/kept in repo.
notes_to_self/
snippets/

# ------- AI shiz -------
# `ai.skillz` symlinks,
# (machine-local, deploy via deploy-skill.sh)
.claude/skills/py-codestyle
.claude/skills/close-wkt
.claude/skills/plan-io
.claude/skills/prompt-io
.claude/skills/resolve-conflicts
.claude/skills/inter-skill-review

.claude/skills/pr-msg/msgs/*
# XXX, for rn, so i can telescope this file.
!/.claude/skills/pr-msg/pr_msg_LATEST.md
# /open-wkt specifics
.claude/skills/open-wkt
.claude/wkts/
claude_wkts

# /code-review-changes specifics
.claude/skills/code-review-changes
# review-skill ephemeral ctx (per-PR, single-use)
.claude/review_context.md
.claude/review_regression.md

# per-skill session/conf (machine-local)
.claude/skills/*/conf.toml
# /pr-msg specifics
.claude/skills/pr-msg/*
# repo-specific
!.claude/skills/pr-msg/format-reference.md
# XXX, so u can nvim-telescope this file.
# !.claude/skills/pr-msg/pr_msg_LATEST.md

# ai.skillz symlinks (machine-local, deploy via deploy-skill.sh)
.claude/skills/py-codestyle
.claude/skills/code-review-changes
.claude/skills/close-wkt
.claude/skills/open-wkt
.claude/skills/plan-io
.claude/skills/prompt-io
.claude/skills/resolve-conflicts
.claude/skills/inter-skill-review
.claude/skills/yt-url-lookup
# /commit-msg specifics
# - any commit-msg gen tmp files
.claude/*_commit_*.md
.claude/*_commit*.txt
.claude/skills/commit-msg/*
!.claude/skills/commit-msg/style-duie-reference.md

# hybrid skills — symlinked SKILL.md + references
.claude/skills/commit-msg/SKILL.md
.claude/skills/pr-msg/SKILL.md
.claude/skills/pr-msg/references
# use prompt-io instead?
.claude/plans

# nix develop --profile .nixdev
.nixdev*

# :Obsession .
Session.vim

# `gish` local `.md`-files
# TODO? better all around automation!
# -[ ] it'd be handy to also commit and sync with wtv git service?
Expand All @@ -159,7 +168,3 @@ gh/

# LLM conversations that should remain private
docs/conversations/

# Claude worktrees
.claude/wkts/
claude_wkts
Loading
Loading