From c6b79fd97013c8ba562232c1b80ad2cca7ce2f15 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:01:35 +0000 Subject: [PATCH 1/8] session: the import probe never ran, and the advice keyed to a symptom that stopped firing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two halves of the same defect, both measured in a fresh container today. The probe: `--check` asks whether the interpreter that will run the suite can import pytest, PyYAML and xdist. It said `import importlib`, which does not bind `importlib.util`, so the snippet raised AttributeError on every real CPython, the `&&` short-circuited, and the OK line printed. This session's first `--check` reported `python3: 3.12 OK` and `pytest: 3.12 OK` beside a `python3 -m pytest` answering `No module named pytest` and a `pytest -n auto` dying on `unrecognized arguments: -n`. The test that was meant to cover exactly this passed throughout: it drove a shell script standing in for python, which answered the fixture's line and could not disagree with the snippet. So the probe is now one string ($IMPORT_PROBE) the suite lifts out and runs on a real interpreter, and a probe that cannot run is reported as a failure rather than falling through to OK — "could not ask" and "asked, nothing missing" were indistinguishable, which is the shape this series keeps finding. The advice: AGENTS.md told a session to bootstrap *if* pytest misbehaved. The container now ships 3.12 with pytest, mypy, ruff and flake8 on it, so the symptom no longer appears and the remaining failure reads like a bad flag. A remedy keyed to a symptom is only as good as the symptom, so the instruction is now unconditional: bootstrap before the first test command. ~10s cold, ~1s warm. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- AGENTS.md | 37 ++++++++++--------- scripts/session_bootstrap.sh | 27 ++++++++++++-- tests/test_session_bootstrap.py | 64 +++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e3a2b9e4..8ec02d3c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,31 +92,36 @@ For the full workflow narrative, conventions, and registry schemas, read ## Running tests in a remote (web/mobile) session -Two facts, both measured, both worth one line each: +Two facts, both measured: -1. **Run the suite in parallel.** A remote container has 4 cores and the suites - are subprocess-heavy with no single slow test: PyAutoBrain's 554 tests take - 96s on one core and 28s on four. `pytest-xdist` is installed by the - session-start hook, so the command is just: +1. **Bootstrap first — before the first test command, not when something looks + wrong.** A session holding several organs registers no SessionStart hook + (Claude Code reads hooks from the project directory, which is the repos' + *parent*), so nothing has set this session up yet: ``` - python3 -m pytest -q -n auto + bash PyAutoMind/scripts/session_bootstrap.sh # ~10s cold, ~1s warm + bash PyAutoMind/scripts/session_bootstrap.sh --check # report only ``` -2. **If `python3 -m pytest` or `pytest` misbehaves, the environment is stale, - not the code.** A session holding several organs registers no SessionStart - hook (Claude Code reads hooks from the project directory, which is the - repos' *parent*). Knock on the door directly, once, in the first turn: + This was phrased as a *remedy* — run it if `python3 -m pytest` misbehaves, + the symptom being `No module named pytest` or collection `ImportError`s + naming `yaml`. Measured 2026-08-27: that trigger no longer fires. The + container now ships Python 3.12 with `pytest`, `mypy`, `ruff` and `flake8` + all on it, so the session looks right, and the failure it still has — + `pytest -n auto` dying with `unrecognized arguments: -n` — reads like a bad + flag rather than a stale environment. A remedy keyed to a symptom is only as + good as the symptom; run it unconditionally instead. + +2. **Then run the suite in parallel.** 4 cores, subprocess-heavy suites, no + single slow test: PyAutoBrain's 554 tests take 96s on one core and 28s on + four. `pytest-xdist` arrives with the bootstrap above, which is why that is + step 1: ``` - bash PyAutoMind/scripts/session_bootstrap.sh # fix it - bash PyAutoMind/scripts/session_bootstrap.sh --check # report only + python3 -m pytest -q -n auto ``` - The symptom to recognise: collection `ImportError`s naming `yaml`, or - `No module named pytest`. Both are the session resolving a pytest that is not - this workspace's — never a broken test module. - ## When you are asked to add a new prompt Write the file under `draft///.md` — pick the work-type diff --git a/scripts/session_bootstrap.sh b/scripts/session_bootstrap.sh index 20512b98..6b94bd0d 100755 --- a/scripts/session_bootstrap.sh +++ b/scripts/session_bootstrap.sh @@ -34,6 +34,12 @@ VENV="${PYAUTO_SESSION_VENV:-$HOME/.pyauto/session-py312}" say() { printf '[bootstrap] %s\n' "$*" >&2; } +# What a suite here needs before it can even collect: pytest itself, PyYAML +# (imported at collection time), and xdist (the `-n auto` every remote-session +# instruction names). One string, so the suite can lift it out and run it on a +# real interpreter rather than on a stand-in that cannot disagree with it. +IMPORT_PROBE='import importlib.util, sys; sys.stdout.write(" ".join(m for m in ("pytest", "yaml", "xdist") if not importlib.util.find_spec(m)))' + py312_ready() { [ -x "$VENV/bin/python" ] \ && "$VENV/bin/python" -c 'import sys; raise SystemExit(sys.version_info[:2] != (3, 12))' >/dev/null 2>&1 \ @@ -153,8 +159,25 @@ if [ "${1:-}" = "--check" ]; then # this workspace's code are checked; a linter needs no deps. case "$tool" in python3|pytest) - if missing="$("$interp" -c 'import importlib,sys; sys.stdout.write(" ".join(m for m in ("pytest","yaml","xdist") if not importlib.util.find_spec(m)))' 2>/dev/null)" \ - && [ -n "$missing" ]; then + # Two failure modes, and they must not look alike. + # + # The probe RAN and named modules -> report them. The probe + # could not run at all -> that is a failure too, and used to + # be reported as success: the snippet said `import importlib` + # (which does not bind `importlib.util`), so on every real + # CPython it raised AttributeError, the `&&` short-circuited, + # and the OK line printed. Measured in a fresh container on + # 2026-08-27: `pytest: 3.12 OK` beside a `python3 -m pytest` + # that answered `No module named pytest`. The test that was + # meant to cover this drove a SHELL SCRIPT standing in for + # python, which answered happily — so the guard and the bug + # coexisted. Hence $IMPORT_PROBE, lifted out and run on a + # real interpreter by the suite. + if ! missing="$("$interp" -c "$IMPORT_PROBE" 2>/dev/null)"; then + say "$tool: 3.12 but the import probe would not run ($path) — treat this session as unbootstrapped"; rc=1 + continue + fi + if [ -n "$missing" ]; then say "$tool: 3.12 but cannot import: $missing ($path) — it will fail collection, not the code"; rc=1 continue fi diff --git a/tests/test_session_bootstrap.py b/tests/test_session_bootstrap.py index 0bc10f8a..b48b3142 100644 --- a/tests/test_session_bootstrap.py +++ b/tests/test_session_bootstrap.py @@ -461,6 +461,70 @@ def test_check_fails_a_pytest_that_has_the_right_version_but_cannot_import(tmp_p assert r.returncode != 0 +def _import_probe(): + """The probe as the script really spells it, lifted from the script.""" + for line in BOOTSTRAP.read_text().splitlines(): + if line.startswith("IMPORT_PROBE="): + return line.split("=", 1)[1].strip().strip("'") + raise AssertionError("session_bootstrap.sh no longer defines IMPORT_PROBE") + + +def test_the_import_probe_runs_on_a_real_interpreter(): + """The check above drove a shell script standing in for python. + + A stand-in answers whatever the fixture says, so it can never disagree with + the snippet — and the snippet was wrong: `import importlib` does not bind + `importlib.util`, so on every real CPython the probe raised AttributeError, + the `&&` short-circuited, and `--check` printed `3.12 OK`. Measured in a + fresh container on 2026-08-27, beside a `python3 -m pytest` that answered + `No module named pytest`. + + So this one runs the real string on the real interpreter. Sufficiency, not + plumbing: the fixture cannot be the thing under test. + """ + import importlib.util + import sys + + r = subprocess.run([sys.executable, "-c", _import_probe()], + capture_output=True, text=True, timeout=60) + assert r.returncode == 0, ( + "the probe cannot run, so --check silently skips it: " + r.stderr) + expected = {m for m in ("pytest", "yaml", "xdist") + if not importlib.util.find_spec(m)} + assert set(r.stdout.split()) == expected + + +def test_check_fails_when_the_import_probe_cannot_run(tmp_path): + """"Could not ask" is a failure, not a pass. + + The two outcomes used to be indistinguishable — both fell through to the OK + line — which is what let a broken probe read as a healthy session. + """ + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + interp = fake_bin / "fake-python3.12" + interp.write_text( + "#!/bin/sh\n" + "case \"$2\" in\n" + " *version_info*) echo '3.12' ;;\n" + " *find_spec*) exit 1 ;;\n" # the AttributeError case + "esac\n" + ) + interp.chmod(0o755) + shim = fake_bin / "pytest" + shim.write_text(f"#!{interp}\n") + shim.chmod(0o755) + + env = dict(os.environ) + env["PATH"] = f"{fake_bin}:{env['PATH']}" + r = subprocess.run(["bash", str(BOOTSTRAP), "--check"], capture_output=True, + text=True, env=env, timeout=180) + + assert "pytest: 3.12 OK" not in r.stderr, r.stderr + assert "import probe would not run" in r.stderr, r.stderr + assert r.returncode != 0 + + # -------------------------------------------------------------------------- # 6. Everything else the venv owns # From 0e02d976c52b43f49c3e0fa8d8d92941a0858380 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:02:47 +0000 Subject: [PATCH 2/8] agents: name the shallow-clone leg where it does harm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 1 fixed the source of the ancestry lie — the hook unshallows — but nothing a session reads says so, and `--check` is the only thing that reports the state. This session arrived with 78 of PyAutoMind's 4478 commits, so `--is-ancestor` would have answered "not an ancestor" for merged branches: the answer the ship and close-out procedures act on. Recorded beside the bootstrap instruction, since the bootstrap is the fix. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- AGENTS.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 8ec02d3c..22b0faf6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -113,6 +113,14 @@ Two facts, both measured: flag rather than a stale environment. A remedy keyed to a symptom is only as good as the symptom; run it unconditionally instead. + It also **unshallows the clones**, which is the leg with teeth. A remote + session clones shallow — measured 2026-08-27: this repo arrived with 78 of + 4478 commits — and `git merge-base --is-ancestor` then answers "not an + ancestor" for a commit whose ancestry is merely *absent* from the clone. + That is the answer the ship and close-out procedures act on when proving a + branch merged, so an unbootstrapped session can read a merged branch as + unmerged and stop a close-out on nothing. + 2. **Then run the suite in parallel.** 4 cores, subprocess-heavy suites, no single slow test: PyAutoBrain's 554 tests take 96s on one core and 28s on four. `pytest-xdist` arrives with the bootstrap above, which is why that is From be0121ad571807a377c9eb07981627c60dbcc403 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:14:19 +0000 Subject: [PATCH 3/8] session: the venv fix broke every uv tool, and --check called them OK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Measured this session, after a clean bootstrap: mypy, flake8, black, poetry and pyright all died with ModuleNotFoundError naming themselves, with the package installed two directories away. `uv tool list` said "Failed find package flake8 in tool environment". `--check` reported every one of them `3.12 OK`. Cause, and it is our own fix's blast radius. uv creates each tool env with `bin/python` as a symlink to whatever `python3` was at install time — /usr/local/bin/python3 — and the hook then repoints that path at the session venv. Crucially it repoints it with a WRAPPER SCRIPT, because an earlier pass proved a symlink there loses the venv; the wrapper `exec`s the venv's python, which replaces argv, so CPython never sees the tool env beside it and resolves sys.prefix to the venv. The tool's own site-packages is then off sys.path. ruff survived (native binary) and pytest survived (shimmed straight at the venv). Everything else a session lints with was dead. So a remote session has been linting clean by not linting at all, while CI runs the same tools for real — the local-clean/CI-red generator this series exists to kill, one layer down from where it was last found. Two changes: - `repair_uv_tools` (also `--repair-uv-tools`) repoints any tool env whose interpreter does not resolve to its own prefix at the base 3.12 interpreter, and verifies the repoint rather than assuming it. It asks sys.prefix rather than tracing the link, because the wrapper is invisible to readlink. - `--check` now runs each tool. Version was necessary and never sufficient: these tools reached a 3.12 interpreter, just not theirs. The test that reproduces it needs the wrapper, not a bare symlink — a tool env whose python merely symlinks elsewhere still finds its own pyvenv.cfg and is fine. That is why the breakage arrived with the fix that introduced the wrapper. Left in scripts/ rather than promoted into policy/session_start_hook.sh: the hook is generated into all four organs and two of them cannot be attached to this session, so changing it would make firewall_gate red on repos this session cannot fix. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- scripts/session_bootstrap.sh | 69 ++++++++++++++++++++ tests/test_session_bootstrap.py | 109 ++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) diff --git a/scripts/session_bootstrap.sh b/scripts/session_bootstrap.sh index 6b94bd0d..97b64d78 100755 --- a/scripts/session_bootstrap.sh +++ b/scripts/session_bootstrap.sh @@ -98,6 +98,53 @@ extras_state() { return "$rc" } +# The session's OTHER interpreters: uv's tool environments. +# +# uv creates each tool env with `bin/python` as a SYMLINK to whatever `python3` +# was at install time — here `/usr/local/bin/python3`. The hook then repoints +# that same path at the session venv, so every tool env's python now resolves +# its prefix to the VENV: `sys.prefix` is the venv, the tool's own +# site-packages is never on `sys.path`, and the console script dies with +# `ModuleNotFoundError: No module named 'flake8'` — with flake8 sitting +# installed two directories away. +# +# Measured 2026-08-27, post-bootstrap: mypy, flake8, black and poetry all dead +# this way; `ruff` survived (a native binary) and `pytest` survived (its shim +# points straight at the venv, which has pytest). `--check` called every one of +# them "3.12 OK", because the interpreter they reach IS 3.12 — it is simply the +# wrong one. A session then lints clean by not linting at all, and CI is the +# thing that finds out. +# +# The fix is one link: a venv's `bin/python` must resolve to a BASE interpreter, +# never to a path this hook hijacks. +repair_uv_tools() { + local tools_dir base tool link prefix + base="$("$VENV/bin/python" -c 'import sys, os; print(os.path.join(sys.base_prefix, "bin", "python3.12"))' 2>/dev/null)" + [ -x "$base" ] || base="$(command -v python3.12 2>/dev/null)" + [ -x "$base" ] || return 0 + tools_dir="${PYAUTO_UV_TOOLS_DIR:-$(uv tool dir 2>/dev/null || echo "$HOME/.local/share/uv/tools")}" + [ -d "$tools_dir" ] || return 0 + for tool in "$tools_dir"/*/; do + link="${tool}bin/python" + [ -L "$link" ] || continue + # Ask the interpreter where it thinks it lives, rather than tracing the + # link: `/usr/local/bin/python3` is a WRAPPER SCRIPT (a symlink there + # would lose the venv — see the system-default note), so `readlink -f` + # stops at the wrapper and reports nothing about the venv behind it. + # sys.prefix is the outcome; anything else is the mechanism. + prefix="$("$link" -c 'import sys; print(sys.prefix)' 2>/dev/null)" || continue + [ -n "$prefix" ] || continue + [ "$prefix" = "${tool%/}" ] && continue # resolves to its own env: correct + ln -sfn "$base" "$link" + prefix="$("$link" -c 'import sys; print(sys.prefix)' 2>/dev/null)" + if [ "$prefix" = "${tool%/}" ]; then + say "repointed $(basename "${tool%/}") at $base (it resolved into $VENV, not its own env)" + else + say "WARNING: $(basename "${tool%/}") still resolves to ${prefix:-nothing} — it will not run" + fi + done +} + shallow_repos() { local root repo out="" root="$(dirname "$MIND_DIR")" @@ -107,6 +154,14 @@ shallow_repos() { printf '%s' "${out# }" } +# A seam for the suite (and for a hand repair): run just this leg. The tools +# directory and the venv are both overridable, so the test can build a pair of +# real environments and reproduce the breakage exactly. +if [ "${1:-}" = "--repair-uv-tools" ]; then + repair_uv_tools + exit 0 +fi + if [ "${1:-}" = "--check" ]; then rc=0 if py312_ready; then @@ -183,6 +238,15 @@ if [ "${1:-}" = "--check" ]; then fi ;; esac + # Version is necessary, not sufficient — and neither is an + # importable pytest. The last thing left is whether the tool RUNS: + # a uv tool env whose python resolves into the session venv reaches + # a 3.12 interpreter that cannot see the tool's own site-packages, + # and answers ModuleNotFoundError. Ask it. + if ! "$path" --version >/dev/null 2>&1; then + say "$tool: 3.12 but will not run ($path) — run this script with no arguments"; rc=1 + continue + fi say "$tool: 3.12 OK ($path)" else say "$tool: running on $real, not 3.12 ($path) — its results will disagree with CI"; rc=1 @@ -226,6 +290,11 @@ for repo in "$root"/*/; do CLAUDE_PROJECT_DIR="${repo%/}" "$hook" || say "WARNING: ${repo%/} hook failed" done +# The hook rebuilds uv's tools on 3.12; this repairs the link that rebuild +# cannot fix from inside, because the path it depends on is one the hook itself +# repoints afterwards. +repair_uv_tools + # Make the fix apply to THIS process tree too, not only to shells the session # starts after the env file is read. A caller that sources us gets the PATH; a # caller that runs us gets the message. diff --git a/tests/test_session_bootstrap.py b/tests/test_session_bootstrap.py index b48b3142..da4f253f 100644 --- a/tests/test_session_bootstrap.py +++ b/tests/test_session_bootstrap.py @@ -36,6 +36,7 @@ import json import os import subprocess +import sys from pathlib import Path import pytest @@ -743,3 +744,111 @@ def test_bootstrap_runs_every_sibling_repos_hook(tmp_path): assert r.returncode == 0, r.stderr assert (ran / "FakeHeart").exists(), r.stderr assert (ran / "FakeHands").exists(), r.stderr + + +# -------------------------------------------------------------------------- +# 7. uv's tool environments +# +# The session venv fix has a blast radius nobody costed. uv creates every tool +# env with `bin/python` as a symlink to whatever `python3` was at install time +# — `/usr/local/bin/python3` — and the hook then repoints THAT path at the +# session venv. So each tool env's interpreter resolves its prefix to the venv, +# the tool's own site-packages never reaches `sys.path`, and the console script +# dies on `ModuleNotFoundError` with the package installed two directories away. +# +# Measured 2026-08-27 in a bootstrapped session: mypy, flake8, black, poetry and +# pyright all dead this way, while `--check` reported each as `3.12 OK` — the +# interpreter they reach IS 3.12, it is just not theirs. A session then lints +# clean by not linting, and CI finds out. +# -------------------------------------------------------------------------- + + +def _venv(path): + subprocess.run([sys.executable, "-m", "venv", str(path)], check=True, + capture_output=True, timeout=180) + return path + + +def _prefix_of(python): + r = subprocess.run([str(python), "-c", "import sys; print(sys.prefix)"], + capture_output=True, text=True, timeout=60) + return r.stdout.strip() + + +def _repair(tools_dir, venv): + env = dict(os.environ) + env["PYAUTO_UV_TOOLS_DIR"] = str(tools_dir) + env["PYAUTO_SESSION_VENV"] = str(venv) + return subprocess.run(["bash", str(BOOTSTRAP), "--repair-uv-tools"], + capture_output=True, text=True, env=env, timeout=300) + + +def test_a_tool_env_pointed_at_the_session_venv_is_repaired(tmp_path): + """The exact breakage: a tool env resolving to somebody else's prefix.""" + session_venv = _venv(tmp_path / "session") + tools = tmp_path / "tools" + tool = _venv(tools / "widget") + + # The breakage needs the WRAPPER, not a bare symlink. CPython looks for + # `pyvenv.cfg` beside the executable as invoked, so a tool env whose python + # merely symlinks elsewhere still finds its own config and is fine. What + # loses it is `/usr/local/bin/python3` being a shell wrapper that `exec`s + # the venv (it is a wrapper precisely because a symlink there lost the venv + # — the other half of this same script): the exec replaces argv, and the + # tool env is gone. + hijacked = tmp_path / "usr-local-python3" + hijacked.write_text(f'#!/bin/sh\nexec "{session_venv}/bin/python" "$@"\n') + hijacked.chmod(0o755) + + link = tool / "bin" / "python" + link.unlink() + link.symlink_to(hijacked) + assert _prefix_of(link) == str(session_venv), "fixture did not reproduce it" + + r = _repair(tools, session_venv) + assert "repointed widget" in r.stderr, r.stderr + assert _prefix_of(link) == str(tool) + + +def test_a_healthy_tool_env_is_left_alone(tmp_path): + session_venv = _venv(tmp_path / "session") + tools = tmp_path / "tools" + tool = _venv(tools / "widget") + before = (tool / "bin" / "python").resolve() + + r = _repair(tools, session_venv) + assert "repointed" not in r.stderr, r.stderr + assert (tool / "bin" / "python").resolve() == before + + +def test_check_fails_a_tool_that_has_the_right_version_but_will_not_run(tmp_path): + """`3.12 OK` was printed for tools that answered ModuleNotFoundError. + + The version probe asks the interpreter; it never asks the tool. A tool that + cannot run is the same class of finding as one on the wrong interpreter — + both mean the session is not linting what CI lints. + """ + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + interp = fake_bin / "fake-python3.12" + interp.write_text( + "#!/bin/sh\n" + "case \"$2\" in\n" + " *version_info*) echo '3.12' ;;\n" + " *find_spec*) printf '' ;;\n" + " --version) exit 1 ;;\n" # ModuleNotFoundError, as measured + "esac\n" + ) + interp.chmod(0o755) + broken = fake_bin / "black" # a linter, not one of the two probed + broken.write_text(f"#!{interp}\nimport black\n") + broken.chmod(0o755) + + env = dict(os.environ) + env["PATH"] = f"{fake_bin}:{env['PATH']}" + r = subprocess.run(["bash", str(BOOTSTRAP), "--check"], capture_output=True, + text=True, env=env, timeout=180) + + assert "black: 3.12 OK" not in r.stderr, r.stderr + assert "will not run" in r.stderr, r.stderr + assert r.returncode != 0 From 33c81de6064bad4c6f2883ca0d1700d0ce35c0d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:14:33 +0000 Subject: [PATCH 4/8] repos_sync: generate the remote-session block, so a fifth organ cannot miss it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The follow-up pass 3 named and deliberately did not take. Three passes in a row found a repo whose copy of this guidance had not learned what the previous pass measured, and one found two organs still shipping a bug a third had fixed. Pass 3's reason for deferring was that the per-repo halves differ: each copy named its own test count, its own timings, its own declared deps. Those are exactly what rots — a number generated into every repo's always-loaded context is a confident wrong answer in the other three the moment a suite grows — so they are removed rather than encoded, and a test pins their absence. A repo's own dependencies stay in its .claude/session-python.txt, where the hook reads them at run time. Same shape as the never-rewrite-history policy: one source (policy/remote_sessions.md), N generated copies, a drift check. Opt-in by markers, so a repo this session cannot attach is skipped rather than failing a CI leg on its behalf — PyAutoHeart and PyAutoHands still carry their own text and are a marker addition away, which is the honest state of the rollout. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- AGENTS.md | 85 ++++++++++++---------- policy/remote_sessions.md | 44 +++++++++++ scripts/repos_sync.py | 53 ++++++++++++++ tests/test_repos_sync_remote_block.py | 101 ++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 39 deletions(-) create mode 100644 policy/remote_sessions.md create mode 100644 tests/test_repos_sync_remote_block.py diff --git a/AGENTS.md b/AGENTS.md index 22b0faf6..f0515aaf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -90,45 +90,52 @@ For the full workflow narrative, conventions, and registry schemas, read loosely-related changes, split into separate prompt files before issuing. 4. **`tmp/` is scratch.** Never commit anything under it. -## Running tests in a remote (web/mobile) session - -Two facts, both measured: - -1. **Bootstrap first — before the first test command, not when something looks - wrong.** A session holding several organs registers no SessionStart hook - (Claude Code reads hooks from the project directory, which is the repos' - *parent*), so nothing has set this session up yet: - - ``` - bash PyAutoMind/scripts/session_bootstrap.sh # ~10s cold, ~1s warm - bash PyAutoMind/scripts/session_bootstrap.sh --check # report only - ``` - - This was phrased as a *remedy* — run it if `python3 -m pytest` misbehaves, - the symptom being `No module named pytest` or collection `ImportError`s - naming `yaml`. Measured 2026-08-27: that trigger no longer fires. The - container now ships Python 3.12 with `pytest`, `mypy`, `ruff` and `flake8` - all on it, so the session looks right, and the failure it still has — - `pytest -n auto` dying with `unrecognized arguments: -n` — reads like a bad - flag rather than a stale environment. A remedy keyed to a symptom is only as - good as the symptom; run it unconditionally instead. - - It also **unshallows the clones**, which is the leg with teeth. A remote - session clones shallow — measured 2026-08-27: this repo arrived with 78 of - 4478 commits — and `git merge-base --is-ancestor` then answers "not an - ancestor" for a commit whose ancestry is merely *absent* from the clone. - That is the answer the ship and close-out procedures act on when proving a - branch merged, so an unbootstrapped session can read a merged branch as - unmerged and stop a close-out on nothing. - -2. **Then run the suite in parallel.** 4 cores, subprocess-heavy suites, no - single slow test: PyAutoBrain's 554 tests take 96s on one core and 28s on - four. `pytest-xdist` arrives with the bootstrap above, which is why that is - step 1: - - ``` - python3 -m pytest -q -n auto - ``` + +## Remote sessions (Claude Code on web and mobile) + +Three facts, all measured in a web/mobile container, where this file is loaded +and little else is. They ride in every organ because a session may hold any +subset of them — and the session that needs this most is the one holding +several, which is exactly the session no hook fires in. + +- **Bootstrap in the first turn, unconditionally** — before the first test + command, not as a remedy once something looks wrong: + + ``` + bash PyAutoMind/scripts/session_bootstrap.sh # ~10s cold, ~1s warm + bash PyAutoMind/scripts/session_bootstrap.sh --check # report only + ``` + + A session holding several organs registers no SessionStart hook — Claude Code + reads project hooks from the project directory, which in that layout is the + repos' *parent*, not a repo — so nothing has set this session up. It was once + phrased as a remedy keyed to `No module named pytest` or collection + `ImportError`s naming `yaml`; that symptom stopped appearing when the + container image moved to Python 3.12, while the environment is still wrong in + ways that read like a bad command rather than a stale session (`pytest -n + auto` → `unrecognized arguments: -n`). The bootstrap also **unshallows the + clones**: a remote session clones shallow, and `git merge-base --is-ancestor` + then answers "not an ancestor" for a commit whose ancestry is merely absent — + the answer the ship and close-out procedures act on when proving a branch + merged. + +- **Then run the suite in parallel.** 4 cores, subprocess-heavy suites, no + single slow test: about 3.5x. `python3 -m pytest -q -n auto`, with + `pytest-xdist` supplied by the bootstrap above. + +- **There is no `gh`, and installing one does not help.** A remote session + reaches GitHub through the `mcp__github__*` tools, already scoped to the + session's repos. `gh` installs in two seconds and is a trap: it authenticates, + then 403s every repo-scoped call, because the egress proxy serves neither the + REST repo paths nor GraphQL beyond a pinned set of PR-review operations — a + binary that looks healthy and fails everything that matters. It also defeats + the surface probe, which keys off `gh auth status`. Read + `PyAutoBrain/skills/GITHUB_ACCESS.md` at the top of any run that touches + GitHub; it maps each `gh` operation onto its MCP tool. Spell that path from + the workspace root, as written: a multi-organ session is cwd'd at the repos' + *parent*, so a bare `skills/…` reads as a missing file rather than a missing + repo prefix. + ## When you are asked to add a new prompt diff --git a/policy/remote_sessions.md b/policy/remote_sessions.md new file mode 100644 index 00000000..b1681983 --- /dev/null +++ b/policy/remote_sessions.md @@ -0,0 +1,44 @@ +## Remote sessions (Claude Code on web and mobile) + +Three facts, all measured in a web/mobile container, where this file is loaded +and little else is. They ride in every organ because a session may hold any +subset of them — and the session that needs this most is the one holding +several, which is exactly the session no hook fires in. + +- **Bootstrap in the first turn, unconditionally** — before the first test + command, not as a remedy once something looks wrong: + + ``` + bash PyAutoMind/scripts/session_bootstrap.sh # ~10s cold, ~1s warm + bash PyAutoMind/scripts/session_bootstrap.sh --check # report only + ``` + + A session holding several organs registers no SessionStart hook — Claude Code + reads project hooks from the project directory, which in that layout is the + repos' *parent*, not a repo — so nothing has set this session up. It was once + phrased as a remedy keyed to `No module named pytest` or collection + `ImportError`s naming `yaml`; that symptom stopped appearing when the + container image moved to Python 3.12, while the environment is still wrong in + ways that read like a bad command rather than a stale session (`pytest -n + auto` → `unrecognized arguments: -n`). The bootstrap also **unshallows the + clones**: a remote session clones shallow, and `git merge-base --is-ancestor` + then answers "not an ancestor" for a commit whose ancestry is merely absent — + the answer the ship and close-out procedures act on when proving a branch + merged. + +- **Then run the suite in parallel.** 4 cores, subprocess-heavy suites, no + single slow test: about 3.5x. `python3 -m pytest -q -n auto`, with + `pytest-xdist` supplied by the bootstrap above. + +- **There is no `gh`, and installing one does not help.** A remote session + reaches GitHub through the `mcp__github__*` tools, already scoped to the + session's repos. `gh` installs in two seconds and is a trap: it authenticates, + then 403s every repo-scoped call, because the egress proxy serves neither the + REST repo paths nor GraphQL beyond a pinned set of PR-review operations — a + binary that looks healthy and fails everything that matters. It also defeats + the surface probe, which keys off `gh auth status`. Read + `PyAutoBrain/skills/GITHUB_ACCESS.md` at the top of any run that touches + GitHub; it maps each `gh` operation onto its MCP tool. Spell that path from + the workspace root, as written: a multi-organ session is cwd'd at the repos' + *parent*, so a bare `skills/…` reads as a missing file rather than a missing + repo prefix. diff --git a/scripts/repos_sync.py b/scripts/repos_sync.py index d9b4e270..89c81a90 100644 --- a/scripts/repos_sync.py +++ b/scripts/repos_sync.py @@ -77,6 +77,8 @@ MAP_END = "" HISTORY_BEGIN = "" HISTORY_END = "" +REMOTE_BEGIN = "" +REMOTE_END = "" ORGANS_BEGIN = "" ORGANS_END = "" @@ -108,10 +110,32 @@ SESSION_HOOKS = "session-start hooks (generated)" +# What a Claude Code web/mobile session must know before its first command: +# bootstrap unconditionally, run the suite in parallel, and reach GitHub through +# the MCP tools because there is no `gh`. Same shape as the history policy — +# one source file, N generated copies, a drift check — and inline everywhere for +# the same reason: a session may hold any subset of the organs, and the session +# that needs this most (several organs, so no hook fires) is the one where only +# repo content is guaranteed to be loaded. +# +# It was hand-written per repo until three passes in a row found a copy that had +# not learned what the previous pass measured, and one pass found two organs +# still shipping a bug a third had fixed. The per-repo halves were the argument +# against generating it, so they are gone: no test counts, no timings, no +# declared deps — those rot, and a rotting number in every repo's context is +# worse than none. A repo's own dependencies stay in its +# `.claude/session-python.txt`, which the hook reads at run time. +REMOTE_SESSIONS_FILE = "policy/remote_sessions.md" + + def load_history_policy(mind_root): return (mind_root / HISTORY_POLICY_FILE).read_text().rstrip("\n") +def load_remote_sessions(mind_root): + return (mind_root / REMOTE_SESSIONS_FILE).read_text().rstrip("\n") + + def load_session_hook(mind_root): return (mind_root / SESSION_HOOK_FILE).read_text() @@ -532,6 +556,30 @@ def check_history_blocks(root, repos, hpol): return problems +def check_remote_blocks(root, repos, remote): + """Same contract as the history block: opt in with the markers, and the copy + must then be the canonical text verbatim. + + Opt-in, not mandatory, so a repo that has not added the markers is skipped + rather than failing a session (or a CI leg) that cannot see it. That is also + the honest state of this rollout — see the module comment on + REMOTE_SESSIONS_FILE.""" + problems = [] + for name in repos: + agents = root / name / "AGENTS.md" + if not agents.exists(): + continue + text = agents.read_text() + if REMOTE_BEGIN not in text or REMOTE_END not in text: + continue # opt-in: repo hasn't added the remote-session markers yet + if extract_block(text, REMOTE_BEGIN, REMOTE_END) != remote: + problems.append( + f"'{name}': remote-session block is stale — run " + f"`python3 PyAutoMind/scripts/repos_sync.py --write`" + ) + return problems + + # -------------------------------------------------------------------------- # CLAUDE.md → AGENTS.md pointer (repo hygiene) # -------------------------------------------------------------------------- @@ -1174,6 +1222,7 @@ def main(): smap = system_map(categories, repos) hpol = load_history_policy(mind_root) + remote = load_remote_sessions(mind_root) hook_text = load_session_hook(mind_root) if args.write: @@ -1191,6 +1240,8 @@ def main(): for name in repos: write_block(root / name / "AGENTS.md", hpol, HISTORY_BEGIN, HISTORY_END, required=False) + write_block(root / name / "AGENTS.md", remote, + REMOTE_BEGIN, REMOTE_END, required=False) for rel, bold in PUBLIC_TABLE_TARGETS: write_block(root / rel, organ_public_table(repos, bold=bold), ORGANS_BEGIN, ORGANS_END, required=False) @@ -1210,6 +1261,8 @@ def main(): lambda: check_map_blocks(root, repos, smap), "never-rewrite-history blocks (generated)": lambda: check_history_blocks(root, repos, hpol), + "remote-session blocks (generated)": + lambda: check_remote_blocks(root, repos, remote), "public front-door organ tables (generated)": lambda: check_public_tables(root, repos), "hub organism blurb (organs present)": lambda: check_hub_blurb(root, repos), diff --git a/tests/test_repos_sync_remote_block.py b/tests/test_repos_sync_remote_block.py new file mode 100644 index 00000000..d87b5776 --- /dev/null +++ b/tests/test_repos_sync_remote_block.py @@ -0,0 +1,101 @@ +"""The remote-session block is generated, so no organ can be born without it. + +Three passes of mobile-performance review in a row found a repo whose copy of +this guidance had not learned what the previous pass measured — and one of them +found two organs still shipping a bug an earlier pass had fixed, in a file that +looked like cosmetic drift and was executable. The text was hand-written per +repo because the copies genuinely differed: each named its own test count, its +own timings, its own declared deps. + +Those per-repo halves are the thing that rots, so the canonical text has none of +them, and this file pins that: a number in every repo's always-loaded context is +worse than no number as soon as it is wrong. + +Conventions, as in the sibling repos_sync tests: + +1. **Fictional fixtures only.** `tests/**` is KEEP-copied verbatim into the + public template, so nothing here names a real repository. +2. **Prove each leg FAILS.** A drift check that cannot fail is decoration. +""" + +import re +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) + +import repos_sync # noqa: E402 + +MIND = Path(__file__).resolve().parents[1] +CANON = repos_sync.load_remote_sessions(MIND) + +REPOS = {"OrganCore": {"category": "organ"}, "LibAlpha": {"category": "library"}} + + +def _repo(root, name, body): + (root / name).mkdir(parents=True, exist_ok=True) + (root / name / "AGENTS.md").write_text(body) + + +def _blocked(text): + return f"# OrganCore\n\n{repos_sync.REMOTE_BEGIN}\n{text}\n{repos_sync.REMOTE_END}\n" + + +def test_a_repo_carrying_the_canonical_text_is_clean(tmp_path): + _repo(tmp_path, "OrganCore", _blocked(CANON)) + assert repos_sync.check_remote_blocks(tmp_path, REPOS, CANON) == [] + + +def test_a_stale_copy_is_drift(tmp_path): + stale = CANON.replace("unconditionally", "if pytest misbehaves") + assert stale != CANON + _repo(tmp_path, "OrganCore", _blocked(stale)) + problems = repos_sync.check_remote_blocks(tmp_path, REPOS, CANON) + assert len(problems) == 1 and "OrganCore" in problems[0] + assert "--write" in problems[0] + + +def test_a_repo_without_the_markers_is_skipped_not_failed(tmp_path): + """Opt-in: a session that cannot see an organ must not fail on its behalf. + + Half of the organs are attached in a typical remote session, and the check + runs there as well as in CI. + """ + _repo(tmp_path, "OrganCore", "# OrganCore\n\nno markers here\n") + assert repos_sync.check_remote_blocks(tmp_path, REPOS, CANON) == [] + + +def test_a_repo_that_is_not_checked_out_is_skipped(tmp_path): + assert repos_sync.check_remote_blocks(tmp_path, REPOS, CANON) == [] + + +def test_write_fills_the_block_and_is_idempotent(tmp_path): + _repo(tmp_path, "OrganCore", _blocked("stale text")) + agents = tmp_path / "OrganCore" / "AGENTS.md" + + repos_sync.write_block(agents, CANON, repos_sync.REMOTE_BEGIN, + repos_sync.REMOTE_END, required=False) + assert repos_sync.check_remote_blocks(tmp_path, REPOS, CANON) == [] + once = agents.read_text() + + repos_sync.write_block(agents, CANON, repos_sync.REMOTE_BEGIN, + repos_sync.REMOTE_END, required=False) + assert agents.read_text() == once + + +def test_the_canonical_text_carries_no_per_repo_numbers(): + """The reason it could not be generated before, removed rather than encoded. + + A test count or a timing is true of one repo on one day; generated into + every repo's always-loaded context, it is a confident wrong answer in the + other three the moment a suite grows. + """ + offenders = [line for line in CANON.splitlines() + if re.search(r"\b\d+\s*(tests|s on|cores? and)\b", line)] + assert not offenders, offenders + + +def test_the_text_names_the_three_things_a_session_must_do_first(): + for needle in ("session_bootstrap.sh", "-n auto", "GITHUB_ACCESS.md", + "is-ancestor"): + assert needle in CANON, f"the block no longer mentions {needle}" From ff1fb9fac1f1fa205b5d96e5683e35972c7131ea Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:16:11 +0000 Subject: [PATCH 5/8] prompt: file the two organ-scoped halves this session could not reach Both are rollouts that stop at the repos a session can attach. add_repo was refused for PyAutoHeart and PyAutoHands here, so: their opt-in to the generated remote-session block, and promoting repair_uv_tools from the bootstrap into the hook (where a single-repo session, which never calls the bootstrap, would get it) both need a four-organ session. Dashboard regenerated in the same commit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- dashboard.html | 21 ++++--- dashboard.md | 24 ++++--- .../session_fixes_reach_only_two_organs.md | 62 +++++++++++++++++++ 3 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 draft/maintenance/organs/session_fixes_reach_only_two_organs.md diff --git a/dashboard.html b/dashboard.html index b18f8d81..3ec74901 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

-
  • 2In flight
  • 3Parked
  • 5Planned
  • 134Backlog
+
  • 2In flight
  • 3Parked
  • 5Planned
  • 135Backlog

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

134 filed prompts, not started — sorted most-pickable first (priority, then size). 24 of them belong to an epic and are listed only under Epics below.

+

135 filed prompts, not started — sorted most-pickable first (priority, then size). 24 of them belong to an epic and are listed only under Epics below.

feature — 28 @@ -321,11 +321,12 @@

Backlog

Cluster-scale gradient-search benchmark (Prodigy vs Nautilus, point-source)🔬 researchautolens_profiling

-maintenance — 22 +maintenance — 23

Untrack the generated FITS test artifacts in autoarray🧹 maintenancelibrariessmallsupervisedmedium

autolens_workspace_developer rectangular experiments — Gut stash + rename🧹 maintenanceautolens_workspace_developersmallsupervisednormal

+

Mirror drifted library config keys into the workspace configs🧹 maintenanceworkspacessmallsupervisednormal

Un-park multi_galaxy/features/scaling_relation/slam once a capped run passes🧹 maintenanceworkspacessmallsupervisednormal

@@ -420,6 +421,12 @@

Backlog 2026-08-27 filed +Two organs are missing this session's fixes, and the hook still ships… + + + +2026-08-27 +filed Should TransformedMessage carry its own support, rather than the… @@ -465,7 +472,7 @@

Backlog wiki-currency's --check-version gate rots on every library main merge - + 2026-08-24 filed interferometer/jax_grad/gradient.py: eager and jitted likelihoods… @@ -705,12 +712,6 @@

Backlog Nightly release has been blocked 8 nights running — triage the streak - -2026-08-04 -filed -HowToLens ch4 tutorial 3: mask overlay is never actually drawn - -

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index e8822a44..e4e5af5e 100644 --- a/dashboard.md +++ b/dashboard.md @@ -45,7 +45,7 @@ anything you could not verify. | [In flight](#in-flight) (`active/`) | 2 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 134 | +| [Backlog](#backlog) (`draft/`) | 135 | ## Start here @@ -255,7 +255,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**134** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **24** of them belong to an epic and are listed only under [Epics](#epics) below. +**135** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **24** of them belong to an epic and are listed only under [Epics](#epics) below.
feature — 28 @@ -612,7 +612,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-maintenance — 22 +maintenance — 23
📋 autocti_workspace has no Navigator Check, so its CI can never roll… — ci · medium · supervised · high @@ -646,6 +646,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 Two organs are missing this session's fixes, and the hook still ships… — organs · small · supervised · normal + +``` +/start_dev draft/maintenance/organs/session_fixes_reach_only_two_organs.md +``` + +
+
📋 Mirror drifted library config keys into the workspace configs — workspaces · small · supervised · normal ``` @@ -1189,6 +1197,7 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | Date | Event | Task | |------|-------|------| | 2026-08-27 | issued | Why does XLA CPU's Eigen thread pool wedge on the multi_dataset vmap… | +| 2026-08-27 | filed | Two organs are missing this session's fixes, and the hook still ships… | | 2026-08-27 | filed | Should TransformedMessage carry its own support, rather than the… | | 2026-08-27 | filed | One construction path for plane-bound lensing quantities | | 2026-08-27 | filed | Multi-plane time delays | @@ -1197,12 +1206,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… | | 2026-08-26 | filed | The Brain board should work in a session that has no gh | | 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | -| 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… |
… 10 more (40 left) | Date | Event | Task | |------|-------|------| +| 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | | 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | | 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… | | 2026-08-24 | filed | Induct PyAutoReduce into the PyAutoHands release machinery (date… | @@ -1212,12 +1221,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-23 | filed | Brain board follow-ups: what real mornings surface | | 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | | 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | -| 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… |
… 10 more (30 left) | Date | Event | Task | |------|-------|------| +| 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | | 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | | 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… | | 2026-08-21 | filed | Rectangular mesh split: Bilinear (fast CPU default) vs RTU… | @@ -1227,12 +1236,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-19 | issued | @PyAutoFit TransformedMessage.factor_gradient crashes on first… | | 2026-08-19 | filed | Release board: local run_logs enrichment | | 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | -| 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs |
… 10 more (20 left) | Date | Event | Task | |------|-------|------| +| 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | | 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | | 2026-08-18 | parked | single-source-density-design | | 2026-08-18 | parked | prior-message-collapse-design | @@ -1242,12 +1251,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | | 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | | 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | -| 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| +| 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | | 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | | 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | | 2026-08-06 | filed | Rewrite PyAutoCTI docs/api — 55 of 89 autosummary entries are dead | @@ -1257,7 +1266,6 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-04 | filed | cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… | | 2026-08-04 | filed | autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) | | 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak | -| 2026-08-04 | filed | HowToLens ch4 tutorial 3: mask overlay is never actually drawn |
diff --git a/draft/maintenance/organs/session_fixes_reach_only_two_organs.md b/draft/maintenance/organs/session_fixes_reach_only_two_organs.md new file mode 100644 index 00000000..a5d02a87 --- /dev/null +++ b/draft/maintenance/organs/session_fixes_reach_only_two_organs.md @@ -0,0 +1,62 @@ +# Two organs are missing this session's fixes, and the hook still ships the uv bug + +Type: maintenance +Target: organs +Repos: +- PyAutoMind +- PyAutoHeart +- PyAutoHands +Difficulty: small +Autonomy: supervised +Priority: normal +Status: formalised +Filed: 2026-08-27 + +The 2026-08-27 mobile-workflow pass shipped two things that are, by +construction, only half-rolled-out. Both need a session with **all four organs +attached** — this one could not attach PyAutoHeart or PyAutoHands (`add_repo` +was refused), which is the same "a drift check is only as strong as the number +of repos your session can see" the last three passes each paid for once. + +## 1. Opt Heart and Hands into the generated remote-session block + +`PyAutoMind/policy/remote_sessions.md` is now the single source, written into +each repo's `AGENTS.md` between `` by +`repos_sync.py --write`, with a drift check. Mind and Brain carry the markers; +Heart and Hands still carry their own hand-written text, which is already a pass +behind (it says "bootstrap if pytest misbehaves", a trigger that no longer +fires). + +Add the markers to both, run `--write`, and confirm `--check` reports the block +for four repos rather than two. No gate order: `firewall_gate.yml` is +path-filtered to `scripts/repos_sync.py`, which this does not touch. + +## 2. Promote the uv-tool repair from the bootstrap into the hook + +`scripts/session_bootstrap.sh` gained `repair_uv_tools`: uv creates each tool +env's `bin/python` as a symlink to `/usr/local/bin/python3`, the hook replaces +that path with a wrapper that `exec`s the session venv, and the exec replaces +argv — so every tool env resolves `sys.prefix` to the venv and mypy, flake8, +black, poetry and pyright all die with `ModuleNotFoundError` naming themselves. +Measured 2026-08-27; `--check` called them all `3.12 OK` until the same commit +taught it to run each tool. + +The fix belongs in `policy/session_start_hook.sh`, beside `retool_uv_tools` +which creates the condition — the hook is what a **single-repo** session runs, +and that session never calls the bootstrap. It was left in `scripts/` because +the hook is generated into all four organs and two of them could not be +attached: changing it here would have made `firewall_gate.yml` red on repos this +session could not regenerate. + +So: move the function into the hook, regenerate all four copies, keep the +bootstrap's call (it is idempotent, and the bootstrap runs the hook anyway), and +keep the `--check` leg where it is. + +## Done when + +- `repos_sync.py --check` is clean with all four organs checked out, and the + remote-session block leg names four repos. +- A fresh remote session that runs only the SessionStart hook — no bootstrap + call — has a working `mypy`, `flake8` and `black`. +- The existing tests still pass, plus a hook-level version of + `test_a_tool_env_pointed_at_the_session_venv_is_repaired`. From aae5e2cf92cf3687e495783ae6694e0dbef8a57b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:17:19 +0000 Subject: [PATCH 6/8] =?UTF-8?q?prompt:=20record=20the=20board=20probe=20?= =?UTF-8?q?=E2=80=94=20options=202=20and=203=20are=20dead,=20build=20the?= =?UTF-8?q?=20seam?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prompt's Phase 0 is a four-curl probe, and it now has an answer rather than a plan to get one: $GH_TOKEN reaches /user and /rate_limit (200) and every repo-scoped path with 403 "GitHub access is not enabled for this session", unchanged from 2026-08-26. Option 2 needs an org-admin action that has not happened; option 3 rides the same credential. That selects option 1 by the prompt's own rule. Written down so the next session starts at the design rather than at the probe. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- draft/feature/pyautobrain/board_without_gh.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/draft/feature/pyautobrain/board_without_gh.md b/draft/feature/pyautobrain/board_without_gh.md index cedb04a5..3d7b1e02 100644 --- a/draft/feature/pyautobrain/board_without_gh.md +++ b/draft/feature/pyautobrain/board_without_gh.md @@ -74,6 +74,27 @@ Phase 0 is choosing, not coding. They are not equal in cost or in permanence. Prefer 2 if the probe says it works, else 1. Do not build 1 before running the probe — it is the more complex design and option 2 would obsolete it. +### The probe was run — 2026-08-27. Options 2 and 3 are dead; build 1. + +`$GH_TOKEN` is set, and from a subprocess it reaches exactly what it reached in +August: nothing repo-scoped. + +| Request (Bearer $GH_TOKEN, direct REST) | Result | +|---|---| +| `GET /user` | **200** — returns the login | +| `GET /rate_limit` | **200** | +| `GET /repos/PyAutoLabs/PyAutoBrain` | **403** — "GitHub access is not enabled for this session. An org admin must connect the Claude GitHub App for this organization." | +| `GET /repos/PyAutoLabs/PyAutoBrain/actions/runs` | **403** — same | + +So option 2 needs an org-admin action that has not happened, and option 3 dies +on the same credential path without the install being worth trying. **Option 1 +(the injection seam) is the design**, and the header stays `Autonomy: +supervised` because the seam is a contract between the `/board` skill and +`_board.py` that outlives whoever writes it. + +Re-run the probe before starting anyway — it is four `curl`s, and the day an +admin connects the App, option 2 obsoletes the seam. + ## Already done (2026-08-26 — `complete/2026/08/mobile-performance-review.md`) Do not redo these; build on them. From 7b01e81bfbc34b2242de02523ecd1f70e07a7127 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:19:12 +0000 Subject: [PATCH 7/8] prompt: phase the board's gh-less work per the Feature Agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pyauto-brain feature` scored the parent large (9) and returned split-into-phases. Phase 1 builds the injection seam and proves it on the seven overnight rows; phase 2 ports the remaining four legs, whose real question — whether each script grows its own seam — is answered in phase 1's contract. The parent keeps the design, the measurements and the probe evidence, and is marked phased so the dashboard stops offering it as pickable work. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- dashboard.html | 36 ++++++++------- dashboard.md | 42 +++++++++++------ draft/feature/pyautobrain/board_without_gh.md | 3 +- .../board_without_gh_phase1_seam.md | 46 +++++++++++++++++++ .../board_without_gh_phase2_legs.md | 28 +++++++++++ 5 files changed, 124 insertions(+), 31 deletions(-) create mode 100644 draft/feature/pyautobrain/board_without_gh_phase1_seam.md create mode 100644 draft/feature/pyautobrain/board_without_gh_phase2_legs.md diff --git a/dashboard.html b/dashboard.html index 3ec74901..eedc501d 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

-
  • 2In flight
  • 3Parked
  • 5Planned
  • 135Backlog
+
  • 2In flight
  • 3Parked
  • 5Planned
  • 137Backlog

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

135 filed prompts, not started — sorted most-pickable first (priority, then size). 24 of them belong to an epic and are listed only under Epics below.

+

137 filed prompts, not started — sorted most-pickable first (priority, then size). 24 of them belong to an epic and are listed only under Epics below.

-feature — 28 +feature — 30

Give PyAutoFit searches a seed — today no search can be made…✨ featureautofitmediumsupervisedmedium

+

Board phase 2: the remaining four legs onto the seam✨ featurepyautobrainsmallsupervisednormal

Brain board follow-ups: what real mornings surface✨ featurepyautobrainsmallsupervisednormal

Can create a list of InversionMatrix objects for each dataset✨ featureautoarraymediumsupervisednormal

Tune cluster-scale JOSS benchmarks toward their 5-minute targets✨ featureautolens_workspacemediumsupervisednormal

+

Board phase 1: the injection seam, proven on the overnight legs✨ featurepyautobrainmediumsupervisednormal

Token-light wiki index over the complete/ archive✨ featurepyautomindmediumsupervisednormal

@@ -455,18 +457,30 @@

Backlog +2026-08-27 +filed +Board phase 2: the remaining four legs onto the seam + + + +2026-08-27 +filed +Board phase 1: the injection seam, proven on the overnight legs + + + 2026-08-26 filed jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… - + 2026-08-26 filed The Brain board should work in a session that has no gh - + 2026-08-24 filed wiki-currency's --check-version gate rots on every library main merge @@ -700,18 +714,6 @@

Backlog cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… - -2026-08-04 -filed -autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) - - - -2026-08-04 -filed -Nightly release has been blocked 8 nights running — triage the streak - -

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index e4e5af5e..64084850 100644 --- a/dashboard.md +++ b/dashboard.md @@ -45,7 +45,7 @@ anything you could not verify. | [In flight](#in-flight) (`active/`) | 2 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 135 | +| [Backlog](#backlog) (`draft/`) | 137 | ## Start here @@ -255,10 +255,10 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**135** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **24** of them belong to an epic and are listed only under [Epics](#epics) below. +**137** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **24** of them belong to an epic and are listed only under [Epics](#epics) below.
-feature — 28 +feature — 30
📋 Numba CPU likelihood phase 1: batched MGE convolution + operated-matrix caching — autoarray · medium · supervised · high @@ -300,6 +300,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 Board phase 2: the remaining four legs onto the seam — pyautobrain · small · supervised · normal + +``` +/start_dev draft/feature/pyautobrain/board_without_gh_phase2_legs.md +``` + +
+
📋 Brain board follow-ups: what real mornings surface — pyautobrain · small · supervised · normal ``` @@ -324,6 +332,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 Board phase 1: the injection seam, proven on the overnight legs — pyautobrain · medium · supervised · normal + +``` +/start_dev draft/feature/pyautobrain/board_without_gh_phase1_seam.md +``` + +
+
📋 Token-light wiki index over the complete/ archive — pyautomind · medium · supervised · normal ``` @@ -1203,14 +1219,16 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-27 | filed | Multi-plane time delays | | 2026-08-27 | filed | LensCalc NumPy Hessian step is too coarse for multi-plane tracers | | 2026-08-27 | filed | Cross-validate multi-plane ray tracing | +| 2026-08-27 | filed | Board phase 2: the remaining four legs onto the seam | +| 2026-08-27 | filed | Board phase 1: the injection seam, proven on the overnight legs | | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… | -| 2026-08-26 | filed | The Brain board should work in a session that has no gh | -| 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge |
… 10 more (40 left) | Date | Event | Task | |------|-------|------| +| 2026-08-26 | filed | The Brain board should work in a session that has no gh | +| 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | | 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | | 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | | 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… | @@ -1219,13 +1237,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-23 | filed | pynufft removal: unswept downstream residue (1 hard break + stale… | | 2026-08-23 | filed | Phase 3: stop installing pynufft in Hands/Heart CI and PyAutoCTI… | | 2026-08-23 | filed | Brain board follow-ups: what real mornings surface | -| 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | -| 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray |
… 10 more (30 left) | Date | Event | Task | |------|-------|------| +| 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | +| 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | | 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | | 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | | 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… | @@ -1234,13 +1252,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-20 | filed | Numba CPU likelihood phase 1: batched MGE convolution +… | | 2026-08-19 | filed | autofit_profiling: bootstrap the repo + general PyAutoFit profiling… | | 2026-08-19 | issued | @PyAutoFit TransformedMessage.factor_gradient crashes on first… | -| 2026-08-19 | filed | Release board: local run_logs enrichment | -| 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… |
… 10 more (20 left) | Date | Event | Task | |------|-------|------| +| 2026-08-19 | filed | Release board: local run_logs enrichment | +| 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | | 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | | 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | | 2026-08-18 | parked | single-source-density-design | @@ -1249,13 +1267,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-17 | filed | Which other searches need prior-support handling — coverage audit… | | 2026-08-17 | filed | Teach repos_sync --write to stamp organ config surfaces | | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | -| 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | -| 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| +| 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | +| 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | | 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | | 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | | 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | @@ -1264,8 +1282,6 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-05 | filed | Give PyAutoFit searches a seed — today no search can be made… | | 2026-08-04 | filed | dataset/imaging/jwst_lw is untracked because the gitignore was never… | | 2026-08-04 | filed | cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… | -| 2026-08-04 | filed | autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) | -| 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak |
diff --git a/draft/feature/pyautobrain/board_without_gh.md b/draft/feature/pyautobrain/board_without_gh.md index 3d7b1e02..665a90cc 100644 --- a/draft/feature/pyautobrain/board_without_gh.md +++ b/draft/feature/pyautobrain/board_without_gh.md @@ -7,7 +7,8 @@ Repos: Difficulty: large Autonomy: supervised Priority: normal -Status: formalised +Status: phased +Split-into: draft/feature/pyautobrain/board_without_gh_phase1_seam.md, draft/feature/pyautobrain/board_without_gh_phase2_legs.md Filed: 2026-08-26 The board is the morning door, and on mobile it is mostly blind. Eleven of its diff --git a/draft/feature/pyautobrain/board_without_gh_phase1_seam.md b/draft/feature/pyautobrain/board_without_gh_phase1_seam.md new file mode 100644 index 00000000..31507875 --- /dev/null +++ b/draft/feature/pyautobrain/board_without_gh_phase1_seam.md @@ -0,0 +1,46 @@ +# Board phase 1: the injection seam, proven on the overnight legs + +Type: feature +Target: pyautobrain +Repos: +- PyAutoBrain +Difficulty: medium +Autonomy: supervised +Priority: normal +Status: formalised +Filed: 2026-08-27 +Parent: draft/feature/pyautobrain/board_without_gh.md + +Phase 1 of the board's gh-less work. The parent prompt holds the design, the +measurements and the 2026-08-27 probe that selected this option; read it first +and do not re-derive them. + +## Scope + +Build the seam and prove it on **one** leg family — the seven `overnight: +could not read /` rows, the largest block of the eleven. + +- `board/_board.py` accepts pre-fetched GitHub JSON (`--github-data `), + and `gh_json()` reads from it when present. The `gh` path is untouched, so a + dev box behaves exactly as today. +- The `/board` skill — which *is* the agent, and so is the only thing that can + call `mcp__github__*` — gathers that JSON when `command -v gh` fails, writes + it, and invokes the renderer with the flag. `_board.py` stays a pure + renderer; a subprocess cannot reach MCP tools and this is the whole reason + the seam exists. +- The file's shape is a documented contract in `board/AGENTS.md` (extend + "Reading the board in a remote session"), because it outlives whoever writes + it. + +## Guards + +- Tests drive the seam from a **fixture file**, never a live call. +- A leg with no injected data still reports `could not read` — the invariant + from the 2026-08-26 work: no leg may substitute an empty answer for an + unasked question. A green badge still requires a render that read everything. + +## Done when + +The overnight rows are live in a remote render, the degraded count drops by +seven, the dev-box path is byte-identical, and `tests/test_board_degraded.py` +plus the new fixture tests pass. diff --git a/draft/feature/pyautobrain/board_without_gh_phase2_legs.md b/draft/feature/pyautobrain/board_without_gh_phase2_legs.md new file mode 100644 index 00000000..49e37529 --- /dev/null +++ b/draft/feature/pyautobrain/board_without_gh_phase2_legs.md @@ -0,0 +1,28 @@ +# Board phase 2: the remaining four legs onto the seam + +Type: feature +Target: pyautobrain +Repos: +- PyAutoBrain +Difficulty: small +Autonomy: supervised +Priority: normal +Status: formalised +Filed: 2026-08-27 +Parent: draft/feature/pyautobrain/board_without_gh.md +Blocked-by: draft/feature/pyautobrain/board_without_gh_phase1_seam.md + +Phase 2: port the four legs phase 1 did not cover onto the seam it built — +`versions: no stamps resolved` (`bin/version_drift.sh`), `community: scan +unavailable (exit 4)` (`agents/conductors/community/_community.py`), `resume: +pending-release PR search failed`, and `upkeep: open-issue count unavailable`. + +Two of these live in scripts rather than in `_board.py`, so the phase's real +question is whether each script grows its own `--github-data` seam or whether +the board gathers on their behalf. Answer it in the phase-1 contract's terms; +do not invent a second shape. + +## Done when + +A remote render reports **zero** unread legs, or names each remaining one with +a reason that is not "no `gh`" — the parent prompt's headline criterion. From 358e731999d1fc39403df8d188248cd74e9b73ba Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 18:28:14 +0000 Subject: [PATCH 8/8] =?UTF-8?q?prompt:=20file=20issue=20for=20board-github?= =?UTF-8?q?-data-seam=20(#303)=20=E2=86=92=20start=5Flibrary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 of the board's gh-less work is issued: PyAutoBrain#303, prompt advanced draft/ → active/, registered in active.md with no worktree claimed (this is a remote session, so /start_library runs wherever the task is picked up). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016c1SLkLDW6aEZCVf95XXeu --- active.md | 16 ++++++++++++ .../board_without_gh_phase1_seam.md | 1 + dashboard.html | 14 +++++----- dashboard.md | 26 +++++++++---------- 4 files changed, 37 insertions(+), 20 deletions(-) rename {draft/feature/pyautobrain => active}/board_without_gh_phase1_seam.md (98%) diff --git a/active.md b/active.md index 7e67cf98..a3234376 100644 --- a/active.md +++ b/active.md @@ -1,5 +1,21 @@ # Active Tasks +## board-github-data-seam +- issue: https://github.com/PyAutoLabs/PyAutoBrain/issues/303 (issued 2026-08-27) +- issued: 2026-08-27 +- prompt: active/board_without_gh_phase1_seam.md +- status: library-dev — not started; routed by /start_dev, no worktree claimed yet + (this session is remote: no ~/Code/PyAutoLabs-wt, so /start_library runs on the + machine that picks the task up). +- worktree: ~/Code/PyAutoLabs-wt/board-github-data-seam/ (to be created by /start_library) +- repos: PyAutoBrain +- phase: 1 of 2 — phase 2 is draft/feature/pyautobrain/board_without_gh_phase2_legs.md, + blocked-by this one; parent design + probe evidence in + draft/feature/pyautobrain/board_without_gh.md +- why: the board is the morning door and eleven legs are dark on the surface it is + read from. The 2026-08-27 probe closed the two cheaper options — $GH_TOKEN 403s + every repo path — so the injection seam is the design. + ## transformed-message-factor-gradient-unpack - issue: https://github.com/PyAutoLabs/PyAutoFit/issues/1501 (issued 2026-08-19) - issued: 2026-08-19 diff --git a/draft/feature/pyautobrain/board_without_gh_phase1_seam.md b/active/board_without_gh_phase1_seam.md similarity index 98% rename from draft/feature/pyautobrain/board_without_gh_phase1_seam.md rename to active/board_without_gh_phase1_seam.md index 31507875..4b4d6417 100644 --- a/draft/feature/pyautobrain/board_without_gh_phase1_seam.md +++ b/active/board_without_gh_phase1_seam.md @@ -9,6 +9,7 @@ Autonomy: supervised Priority: normal Status: formalised Filed: 2026-08-27 +Issued: 2026-08-27 Parent: draft/feature/pyautobrain/board_without_gh.md Phase 1 of the board's gh-less work. The parent prompt holds the design, the diff --git a/dashboard.html b/dashboard.html index eedc501d..dfea4903 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

-
  • 2In flight
  • 3Parked
  • 5Planned
  • 137Backlog
+
  • 3In flight
  • 3Parked
  • 5Planned
  • 136Backlog

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

@PyAutoFit TransformedMessage.factor_gradient crashes on first callissue #1501 — issued 2026-08-19HOLD — do not start dev. Fix-or-delete hangs off the PyAutoFit#1498 logpdf-contract

+

Board phase 1: the injection seam, proven on the overnight legsissue #303 — issued 2026-08-27library-dev — not started; routed by /start_dev, no worktree claimed yet

Why does XLA CPU's Eigen thread pool wedge on the multi_dataset vmap…issue #1530 — issued 2026-08-27not started — research follow-up to the shipped jax-compile-stall epic

Parked markdown version

@@ -270,9 +271,9 @@

Planned

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

137 filed prompts, not started — sorted most-pickable first (priority, then size). 24 of them belong to an epic and are listed only under Epics below.

+

136 filed prompts, not started — sorted most-pickable first (priority, then size). 24 of them belong to an epic and are listed only under Epics below.

-feature — 30 +feature — 29 @@ -282,7 +283,6 @@

Backlog

Brain board follow-ups: what real mornings surface✨ featurepyautobrainsmallsupervisednormal

Can create a list of InversionMatrix objects for each dataset✨ featureautoarraymediumsupervisednormal

Tune cluster-scale JOSS benchmarks toward their 5-minute targets✨ featureautolens_workspacemediumsupervisednormal

-

Board phase 1: the injection seam, proven on the overnight legs✨ featurepyautobrainmediumsupervisednormal

Token-light wiki index over the complete/ archive✨ featurepyautomindmediumsupervisednormal

@@ -464,9 +464,9 @@

Backlog 2026-08-27 -filed -Board phase 1: the injection seam, proven on the overnight legs - +issued +Board phase 1: the injection seam, proven on the overnight legs + 2026-08-26 diff --git a/dashboard.md b/dashboard.md index 64084850..61102d9a 100644 --- a/dashboard.md +++ b/dashboard.md @@ -42,10 +42,10 @@ anything you could not verify. | Where | Count | |-------|------:| -| [In flight](#in-flight) (`active/`) | 2 | +| [In flight](#in-flight) (`active/`) | 3 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 137 | +| [Backlog](#backlog) (`draft/`) | 136 | ## Start here @@ -163,6 +163,14 @@ Issued — each has an open GitHub issue and usually a branch. The full record f

+
📋 Board phase 1: the injection seam, proven on the overnight legsissue #303 — issued 2026-08-27 — library-dev — not started; routed by /start_dev, no worktree claimed yet + +``` +/start_dev active/board_without_gh_phase1_seam.md +``` + +
+
📋 Why does XLA CPU's Eigen thread pool wedge on the multi_dataset vmap…issue #1530 — issued 2026-08-27 — not started — research follow-up to the shipped jax-compile-stall epic ``` @@ -255,10 +263,10 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**137** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **24** of them belong to an epic and are listed only under [Epics](#epics) below. +**136** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **24** of them belong to an epic and are listed only under [Epics](#epics) below.
-feature — 30 +feature — 29
📋 Numba CPU likelihood phase 1: batched MGE convolution + operated-matrix caching — autoarray · medium · supervised · high @@ -332,14 +340,6 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-
📋 Board phase 1: the injection seam, proven on the overnight legs — pyautobrain · medium · supervised · normal - -``` -/start_dev draft/feature/pyautobrain/board_without_gh_phase1_seam.md -``` - -
-
📋 Token-light wiki index over the complete/ archive — pyautomind · medium · supervised · normal ``` @@ -1220,7 +1220,7 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-27 | filed | LensCalc NumPy Hessian step is too coarse for multi-plane tracers | | 2026-08-27 | filed | Cross-validate multi-plane ray tracing | | 2026-08-27 | filed | Board phase 2: the remaining four legs onto the seam | -| 2026-08-27 | filed | Board phase 1: the injection seam, proven on the overnight legs | +| 2026-08-27 | issued | Board phase 1: the injection seam, proven on the overnight legs | | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… |
… 10 more (40 left)