Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
125 changes: 124 additions & 1 deletion .claude/hooks/session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,26 @@ set -euo pipefail

VENV="${PYAUTO_SESSION_VENV:-$HOME/.pyauto/session-py312}"
BASE_DEPS=(pytest PyYAML)
EXTRAS_FILE="${CLAUDE_PROJECT_DIR:-$PWD}/.claude/session-python.txt"

# Which checkout is this copy of the hook installed in?
#
# NOT $CLAUDE_PROJECT_DIR. That is the session's project directory, which equals
# the repo only when the session holds exactly ONE repo. A session scoped to
# several organs clones them side by side under the project directory
# (/home/user/PyAutoMind, /home/user/PyAutoBrain, ...), and then
# $CLAUDE_PROJECT_DIR is their parent — so reading session-python.txt from it
# found nothing, silently, in exactly the sessions that hold the most repos.
#
# Derive it from where this script actually is, in both the installed and the
# canonical location.
HOOK_SELF="$(readlink -f "$0")"
case "$(dirname "$HOOK_SELF")" in
*/.claude/hooks) REPO_DIR="$(cd "$(dirname "$HOOK_SELF")/../.." && pwd)" ;;
*/policy) REPO_DIR="$(cd "$(dirname "$HOOK_SELF")/.." && pwd)" ;;
*) REPO_DIR="${CLAUDE_PROJECT_DIR:-$PWD}" ;;
esac
WORKSPACE_ROOT="$(dirname "$REPO_DIR")"
EXTRAS_FILE="$REPO_DIR/.claude/session-python.txt"

# stderr, not stdout: a SessionStart hook's stdout is fed to the agent as
# session context.
Expand Down Expand Up @@ -173,6 +192,107 @@ retool_uv_tools() {
done
}

# 4. Honest git history.
#
# A remote session clones shallow. `git merge-base --is-ancestor` then LIES
# across the graft boundary: it reports "not an ancestor" for a commit whose
# ancestry is simply not in the clone, and the ship/close-out skills act on that
# answer. A completion record already logged this the hard way
# (complete/2026/08/status-sh-repos-missing-source.md, "environment note").
#
# These repos are small (single-digit MB of .git), so unshallowing costs a
# couple of seconds once per container and removes a whole class of wrong
# answer. Bounded and non-fatal: a slow or blocked network leaves a shallow
# clone and a warning, never a failed session start.
ensure_full_clone() {
local repo
for repo in "$WORKSPACE_ROOT"/*/; do
[ -e "${repo}.git/shallow" ] || continue
log "unshallowing $(basename "$repo") (shallow clones make ancestry checks lie)"
if timeout 120 git -C "$repo" fetch --unshallow --quiet 2>/dev/null \
|| timeout 120 git -C "$repo" fetch --depth=2147483647 --quiet 2>/dev/null; then
log " $(basename "$repo"): full history ($(git -C "$repo" rev-list --count HEAD 2>/dev/null) commits)"
else
log " WARNING: $(basename "$repo") is still shallow — run 'git fetch --unshallow' before trusting any ancestry check"
fi
done
}

# 5. Make the NEXT session in this container start correctly.
#
# Claude Code registers project hooks from the session's project directory. In a
# one-repo session that IS the repo, and `<repo>/.claude/settings.json` is found.
# In a session holding several organs the project directory is their parent,
# which is not a repo and has no `.claude/` — so none of the per-repo hooks are
# registered and none of this script runs. That is why a multi-repo session came
# up on the container's Python 3.11: not a broken hook, an unreachable one.
#
# A repo cannot ship a file into its own parent, so install it at run time. The
# settings we write fan out to every sibling repo's own hook, so the layout
# stays "each repo owns its hook" and this file stays generated-from-one-source.
# It takes effect on the next session start in this container.
install_workspace_settings() {
# Only in the multi-repo layout: when the project dir IS this repo, Claude
# Code already found the repo's own settings and there is nothing to add.
[ -n "${CLAUDE_PROJECT_DIR:-}" ] || return 0
[ "$(readlink -f "$CLAUDE_PROJECT_DIR")" != "$(readlink -f "$REPO_DIR")" ] || return 0

local settings="$CLAUDE_PROJECT_DIR/.claude/settings.json"
local fanout="$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
[ -w "$CLAUDE_PROJECT_DIR" ] || { log "WARNING: $CLAUDE_PROJECT_DIR not writable; multi-repo sessions will keep skipping the hook"; return 0; }

mkdir -p "$(dirname "$fanout")"
cat >"$fanout" <<'FANOUT'
#!/usr/bin/env bash
# GENERATED at session start by a PyAuto repo's own session-start hook.
# Runs every sibling repo's hook, because the workspace root is not a repo and
# therefore has no hook of its own. Each repo's hook is idempotent, so the
# second and later ones cost ~0.2s.
set -u
ROOT="$(cd "$(dirname "$(readlink -f "$0")")/../.." && pwd)"
for repo in "$ROOT"/*/; do
hook="${repo}.claude/hooks/session-start.sh"
[ -x "$hook" ] || continue
CLAUDE_PROJECT_DIR="${repo%/}" "$hook" || \
printf '[session-start] WARNING: %s failed\n' "$hook" >&2
done
FANOUT
chmod 0755 "$fanout"

if [ ! -f "$settings" ]; then
cat >"$settings" <<'SETTINGS'
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
}
]
}
]
}
}
SETTINGS
log "installed $settings — multi-repo sessions in this container now run the hook"
fi
}

# Git history and hook reachability have nothing to do with the interpreter, so
# they run first and unconditionally: a container where the 3.12 venv cannot be
# built still wants honest ancestry and a hook that fires next session.
ensure_full_clone
install_workspace_settings

# PYAUTO_SESSION_SKIP_PYTHON=1 stops here — the seam the hook's own tests use to
# exercise the legs above without building an interpreter.
if [ "${PYAUTO_SESSION_SKIP_PYTHON:-}" = "1" ]; then
log "PYAUTO_SESSION_SKIP_PYTHON=1 — leaving the interpreter alone"
exit 0
fi

if ensure_venv; then
ensure_repo_extras
point_system_default "$(readlink -f "$VENV/bin/python")"
Expand All @@ -184,6 +304,9 @@ if ensure_venv; then
echo "export PYAUTO_SESSION_PY312=\"$VENV\""
echo "export VIRTUAL_ENV=\"$VENV\""
echo "export PATH=\"$VENV/bin:\$PATH\""
# The workspace root, so the Brain's shell/python resolvers agree
# with the session instead of each re-deriving it.
echo "export PYAUTO_ROOT=\"$WORKSPACE_ROOT\""
} >>"$CLAUDE_ENV_FILE"
fi
log "default python is now $("$VENV/bin/python" -V 2>&1) ($VENV/bin)"
Expand Down
21 changes: 11 additions & 10 deletions dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
</head>
<body>
<header class="hero"><span class="orb"><svg class="mark" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="24" cy="24" r="20.4"/><path d="M20.2,37.6 L20.2,33.6 C20.2,32.4 19.4,31.6 18.4,30.8 C16.8,29.6 15.8,28.2 15.6,26.6 C15.5,25.6 14.9,25.2 13.9,24.9 C12.6,24.5 12.3,23.7 13.0,22.7 C13.8,21.6 14.5,20.6 14.6,19.4 C15.0,15.4 18.2,12.2 22.4,11.8 C27.2,11.3 31.6,14.6 32.3,19.2 C32.8,22.4 31.6,25.0 30.6,27.0 C29.9,28.4 29.6,29.6 29.6,31.2 L29.6,37.6"/><circle cx="24.6" cy="19.6" r="4.4"/><path d="M22.5,19.7 L24.0,21.3 L26.8,18.1"/><path d="M34.2,16.5 L38.8,16.5 M35.2,21.0 L39.4,21.0 M33.6,25.5 L37.2,25.5"/><g fill="currentColor" stroke="none"><circle cx="40.1" cy="16.5" r="1.4"/><circle cx="40.7" cy="21.0" r="1.4"/><circle cx="38.5" cy="25.5" r="1.4"/></g></svg></span><h1>PyAuto<b>Mind</b><span class="kind">Dashboard</span></h1><div class="rule"></div><p class="tag">Intent. Priority. Flow.</p></header><p class="lede">Every task the Mind is holding. Tap a task's 📋 and its <code>/start_dev</code> command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. <a href="#recent">Recent</a> is the same work by date — what has been happening rather than what to do next.</p>
<ul class="stats"><li><b>1</b><span>In flight</span></li><li><b>3</b><span>Parked</span></li><li><b>5</b><span>Planned</span></li><li><b>133</b><span>Backlog</span></li></ul>
<ul class="stats"><li><b>1</b><span>In flight</span></li><li><b>3</b><span>Parked</span></li><li><b>5</b><span>Planned</span></li><li><b>134</b><span>Backlog</span></li></ul>
<div class="fresh"><p><b>Last updated 2026-08-26.</b> This page is generated from <code>active/</code>, <code>draft/</code> and the registry files, so it is only as current as they are. <code>dashboard_refresh.yml</code> re-renders it on every push to <code>main</code> — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to <code>complete/</code> keeps rendering here as pickable backlog. Reconciling those is the refresh below.</p>
<div class="task"><button class="copy" data-cmd="Bring the PyAutoMind dashboard up to date. Work in the PyAutoMind checkout:

Expand Down Expand Up @@ -250,9 +250,9 @@ <h2>Planned <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/blob
<div class="task"><button class="copy" data-cmd="/route start the planned PyAutoMind task latent-nan-guard-honest-run — its record is in planned.md" aria-label="Copy the Claude command">📋</button><p><b>latent-nan-guard-honest-run</b><span class="facets"> — planned 2026-07-22</span></p></div>
</details>
<h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree/main/draft">markdown version</a></h2>
<p class="muted">133 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.</p>
<p class="muted">134 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.</p>
<details>
<summary>feature — 27</summary>
<summary>feature — 28</summary>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autoarray/numba_cpu_likelihood_mge_convolution_and_caching.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autoarray/numba_cpu_likelihood_mge_convolution_and_caching.md">Numba CPU likelihood phase 1: batched MGE convolution + operated-matrix caching</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autoarray</span><span class="pill n">medium</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autoarray/rectangular_bilinear_rtu_mesh_split.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autoarray/rectangular_bilinear_rtu_mesh_split.md">Rectangular mesh split: Bilinear (fast CPU default) vs RTU (advanced/GPU)</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autoarray</span><span class="pill n">medium</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autoarray/numba_cpu_likelihood_kernel_cdf_fast_path.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autoarray/numba_cpu_likelihood_kernel_cdf_fast_path.md">Numba CPU likelihood phase 2: kernel-CDF numba fast path (the 49-88% lever)</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autoarray</span><span class="pill y">large</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
Expand All @@ -269,6 +269,7 @@ <h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autofit_assistant/remote_mcp_deployment_tiers.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autofit_assistant/remote_mcp_deployment_tiers.md">Remote-MCP deployment tiers (2 + 3) for the results-inspector server</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autofit_assistant</span><span class="pill y">large</span><span class="pill r">human-required</span><span class="pill n">normal</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autolens_profiling/search_settings_estimation_infrastructure.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autolens_profiling/search_settings_estimation_infrastructure.md">Search settings-estimation + profiling infrastructure (n_starts / batch_size / n_batch)</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autolens_profiling</span><span class="pill y">large</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autolens_workspace/oversampled_psf_dataset_adoption.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autolens_workspace/oversampled_psf_dataset_adoption.md">Adopt oversampled PSFs in the start-here dataset chain (option a)</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autolens_workspace</span><span class="pill y">large</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/pyautobrain/board_without_gh.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/pyautobrain/board_without_gh.md">The Brain board should work in a session that has no <code>gh</code></a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">pyautobrain</span><span class="pill y">large</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autoarray/rectangular_multi_submesh.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autoarray/rectangular_multi_submesh.md">Follow-up to <code>rectangular_adapt_cdf.md</code> (issue #322) and Path A</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autoarray</span><span class="pill r">too-large</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autogalaxy/piemass_potential.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autogalaxy/piemass_potential.md"><code>PIEMass.potential_2d_from</code>: implement the missing lensing potential</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autogalaxy</span><span class="pill r">too-large</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autolens_jax_joss/autolens_jax_joss_benchmark_repo.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autolens_jax_joss/autolens_jax_joss_benchmark_repo.md">autolens_jax_joss benchmark repo + real-data start_here pairing</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autolens_jax_joss</span><span class="pill r">too-large</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
Expand Down Expand Up @@ -396,6 +397,12 @@ <h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree
<td class="pick"><button class="copy" data-cmd="/start_dev draft/bug/workspaces/gradient_pixelization_pin_residual_drift.md" aria-label="Copy the Claude command">📋</button></td>
</tr>
<tr>
<td class="when">2026-08-26</td>
<td class="what">filed</td>
<td><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/pyautobrain/board_without_gh.md">The Brain board should work in a session that has no <code>gh</code></a></td>
<td class="pick"><button class="copy" data-cmd="/start_dev draft/feature/pyautobrain/board_without_gh.md" aria-label="Copy the Claude command">📋</button></td>
</tr>
<tr>
<td class="when">2026-08-24</td>
<td class="what">filed</td>
<td><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/maintenance/ci/wiki_currency_check_version_gate.md">wiki-currency's --check-version gate rots on every library main merge</a></td>
Expand Down Expand Up @@ -443,7 +450,7 @@ <h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree
<td><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/maintenance/workspaces/pynufft_removal_downstream_residue.md">pynufft removal: unswept downstream residue (1 hard break + stale…</a></td>
<td class="pick"><button class="copy" data-cmd="/start_dev draft/maintenance/workspaces/pynufft_removal_downstream_residue.md" aria-label="Copy the Claude command">📋</button></td>
</tr>
<tr>
<tr hidden>
<td class="when">2026-08-23</td>
<td class="what">filed</td>
<td><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/maintenance/workspaces/pynufft_removal_downstream_residue_phase_3_ci_install_docs.md">Phase 3: stop installing pynufft in Hands/Heart CI and PyAutoCTI…</a></td>
Expand Down Expand Up @@ -683,12 +690,6 @@ <h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree
<td><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/research/autolens_profiling/cluster_gradient_search_benchmark.md">Cluster-scale gradient-search benchmark (Prodigy vs Nautilus…</a></td>
<td class="pick"><button class="copy" data-cmd="/start_dev draft/research/autolens_profiling/cluster_gradient_search_benchmark.md" aria-label="Copy the Claude command">📋</button></td>
</tr>
<tr hidden>
<td class="when">2026-07-30</td>
<td class="what">filed</td>
<td><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/docs/workspaces/plot_coverage_followups.md">plot coverage — follow-ups deferred from plot-coverage-gaps</a></td>
<td class="pick"><button class="copy" data-cmd="/start_dev draft/docs/workspaces/plot_coverage_followups.md" aria-label="Copy the Claude command">📋</button></td>
</tr>
</table>
<button class="more" data-page="10">… 10 more (40 left)</button>
<h2>Epics <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/epics.md">markdown version</a></h2>
Expand Down
Loading
Loading