From 07f7e825be84e6eba6febe9794b51dde60896f3a Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Fri, 10 Jul 2026 08:51:31 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20tenant-firewall=20check=20in=20repos=5F?= =?UTF-8?q?sync;=20LICENSE;=20README=20=E2=86=92=20REFERENCE.md=20split=20?= =?UTF-8?q?(PyAutoScientist=20Phase=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- LICENSE | 21 ++ README.md | 535 +++--------------------------------------- REFERENCE.md | 459 ++++++++++++++++++++++++++++++++++++ ideas.md | 4 + scripts/repos_sync.py | 145 ++++++++++++ 5 files changed, 659 insertions(+), 505 deletions(-) create mode 100644 LICENSE create mode 100644 REFERENCE.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..7fe7e656 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Jammy2211 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 910b037c..5056acbb 100644 --- a/README.md +++ b/README.md @@ -1,507 +1,32 @@ # PyAutoMind -**The Mind of the PyAuto organism — its ideas, intent, goals and priorities.** - -The PyAuto ecosystem is evolving into a software organism, and this repository -is its **Mind**: the place where the organism's *ideas, intentions, goals, -priorities and future direction* are captured. Although it began life as a -prompt repository, it has grown into the home of everything the organism *wants -to become* — ideas, prompts, active and completed work, planning, priorities, -workflow state and project direction. - -It is still the **starting point of the PyAuto workflow**. Every piece of work -in the ecosystem starts here as a markdown file describing an intent in **plain -English**. You write what you want — that's it. An AI agent (or a human) picks -it up and turns it into a tracked GitHub issue, a feature branch, and a merged -pull request. - -No template to fill in, no special syntax. **If you can describe the change -in a GitHub issue, you can drive the workflow.** - -The Mind decides *what* the organism wants to become; the Brain -([PyAutoBrain](https://github.com/PyAutoLabs/PyAutoBrain)) -decides *how* to achieve it. See [The PyAuto organism](#the-pyauto-organism) below. - -## The PyAuto organism - -The PyAuto ecosystem is organised as a software **organism**, with each -repository playing the role of an organ: - -``` - Mind → Brain → Hands → Heart - ideas reasoning execution health - intent & planning & delivery & readiness - goals - priorities ↘ ↙ - Memory - accumulated knowledge -``` - -| Organ | Repository | Role | -|-------|------------|------| -| **Mind** | **PyAutoMind** (this repo) | What the organism *wants to become*: ideas, intent, goals, priorities, future work. | -| **Brain** | [PyAutoBrain](https://github.com/PyAutoLabs/PyAutoBrain) | *How* to achieve those goals: reasoning, planning, routing work. | -| **Hands** | [PyAutoBuild](https://github.com/PyAutoLabs/PyAutoBuild) *(the "Hands")* | Execution and delivery: building, testing, releasing. | -| **Heart** | [PyAutoHeart](https://github.com/PyAutoLabs/PyAutoHeart) | Health and release-readiness: monitoring, checks, the "is it safe to ship?" gate. | -| **Memory** | [PyAutoMemory](https://github.com/PyAutoLabs/PyAutoMemory) | Accumulated knowledge: literature summaries, wikis, scientific and project knowledge. | - -**Why this repository is the Mind.** Work in PyAuto begins as *intent* — an -idea, a goal, a priority — long before it becomes code. This repository is where -that intent lives and is shaped: raw ideas in `ideas.md`, scoped intentions as -prompt files, the priorities that order them, and the workflow state that tracks -what the organism is currently pursuing. It does not reason about *how* to build -something (that is the Brain) or carry the work out (that is the Hands); it holds -the organism's **wants and direction**. Everything downstream — planning, -execution, health checks — flows from the intent captured here. - ---- - -## What a prompt looks like - -Here's a real prompt — the contents of `autoarray/psf_oversampling.md` — that -became a tracked task. This is the level and style of detail to aim for in -your own GitHub issue: free-form prose, with `@RepoName/path/to/file.py` -references so the tooling knows which repo and files to target. No boilerplate. - -````markdown -A point spread function is used to blur images via 2d convolution. - -This blurring occurs predominantly in the package @PyAutoArray/autoarray/operators/convolver.py. - -The source code currently requires PSF blurring to occur at the same resolution (pixel scale) as the -image, meaning the PSF is always the same resolution as the image. - -However, for modeling, convolution can be performed at a higher resolution than the image, which allows for more accurate -blurring and modeling of the image. This requires us to have an oversampled PSF, which is a PSF that has a higher -resolution than the image. - -For modeling, where images are generated PSF blurring happens in @PyAutoGalaxy/autogalaxy/operate/image.py. - -Modeling can always evaluate images using a hgiher resolition grid, blurring them with the PSF at high -resolution and then downsample to the observed image resolution. Oversampling is implemented in -@PyAutoArray/autoarray/operators/over_sampling. - -Note that over sampling often uses an adaptive sub-szie, which means that 2D covnolution with a PSF is not -well defined. for now, we will assume adaptive over sampling is not used. - -I want us to be able to append the Convolver class with a convolve_over_sample_size integer, which specifies the over sample size of the PSF. -This will allow us to perform convolution at a higher resolution than the image, which will improve the accuracy of the blurring and modeling of the image. -For example, if convolve_over_sample_size is 2, then the PSF will be oversampled by a factor of 2, meaning it will have a resolution that is 2 times higher than the image. - -This, in turn, means out imaging object @PyAutoArray/autoarray/dataset/imaging/dataset.py will need to be -extended to include the convolve_over_sample_size_lp and convolve_over_sample_size_pixelization attributes, which will -specify the over sample size of the PSF for the lensing and pixelization operations, respectively. - -class Imaging(AbstractDataset): - def __init__( - self, - data: Array2D, - noise_map: Optional[Array2D] = None, - psf: Optional[Convolver] = None, - psf_setup_state: bool = False, - noise_covariance_matrix: Optional[np.ndarray] = None, - over_sample_size_lp: Union[int, Array2D] = 4, - over_sample_size_pixelization: Union[int, Array2D] = 4, - use_normalized_psf: Optional[bool] = True, - check_noise_map: bool = True, - sparse_operator: Optional[ImagingSparseOperator] = None, - ): - - -Also,read through the @PyAutoArray/autoarray/inversion/inversion/imaging package, and parents, to see -how PSF convolution enters this. I think we can get it to work in PyAutoArray/autoarray/inversion/inversion/imaging/mapping.py, -and will leave work in PyAutoArray/autoarray/inversion/inversion/imaging/sparse.py to future work. - -This is a complex task, therefore I think we should extend @autolens_workspace_test/scripts/imaging/convolution.py -with a numerical test. - -We should then build on this test in a separate test file using a simple over sampled PSF, to get a numerical -result we can test the source code against. - -@autolens_workspace/scripts/imaging/simulator.py is a good example we can build on to show how to use -over sampled PSFs in a real simulation. We can extend this script to show how to use over sampled PSFs in a real simulation. - -Come up with a plan to implement over sampled PSFs. -```` - -That prompt becomes a GitHub issue, gets routed to the affected repos -(`PyAutoArray`, `PyAutoGalaxy`, the autolens workspaces), and lands as PRs -against each. Typos, half-finished thoughts, and "I think we should…" are -fine — write naturally, the AI fills in the rest. - ---- - -## How a prompt flows through the workflow - -``` - idea ── you write it in ideas.md - │ - ▼ - draft prompt ── you write a markdown file under //.md - │ - ▼ - /start_dev ── reads the prompt, audits the code, drafts an issue, - │ creates the GitHub issue, registers the task in - │ active.md, moves the prompt to issued/ - ▼ - active.md entry ── the task is now tracked across machines and sessions - │ - ▼ - /start_library ── creates a worktree, branch, opens dev environment - or (or workspace variant — chosen automatically) - /start_workspace - │ - ▼ - development ── code, tests, run smoke tests, commit - │ - ▼ - /ship_library ── runs tests, opens PR, waits for merge - or - /ship_workspace - │ - ▼ - PR merged ── post-merge cleanup deletes the worktree, removes the - │ active.md entry, appends a summary to complete.md - ▼ - done -``` - -The slash commands above are skills hosted across the organism (Brain, Heart) but -all read/write Mind's registry via workspace-root-anchored paths. One operates -over the registry without starting work: - -- `/health status` — dashboard of `active.md`, `planned.md`, `complete.md` - (a PyAutoHeart status view, reached through the single `/health` door). - Continuity across execution environments needs no special step — any - environment reads `active.md` and resumes an in-flight task. - ---- - -## Repository layout - -``` -PyAutoMind/ -├── README.md ← this file -├── .gitignore -│ -├── active.md ← tasks currently in progress (one ## section per task) -├── complete.md ← finished tasks (most recent first) -├── ideas.md ← raw incubating ideas, no structure required -├── parked.md ← started/scoped but not in flight (stashes, orphan worktrees, deferred) -├── planned.md ← issued tasks blocked from starting (created on demand) - -├── queue.md ← processing queue for /register_and_iterate -│ -│ PROMPTS — organised by WORK TYPE (first folder), then TARGET (second folder). -│ See "Prompt taxonomy" below and ROUTING.md. -├── feature/ ← new user-facing or scientific capabilities -│ ├── autoarray/ autofit/ autogalaxy/ autolens/ autolens_assistant/ … -│ ├── workspaces/ ← any *_workspace repo -│ ├── pyautobrain/ ← prompts that implement PyAutoBrain agents -│ ├── jax_substructure/ weak/ cluster/ ← numbered topic series (kept together) -├── bug/ ← incorrect behaviour, crashes, regressions -│ ├── autofit/ autogalaxy/ autolens/ autoarray/ priors/ … -├── refactor/ ← internal restructuring, no intended behaviour change -├── docs/ ← documentation, tutorials, notebooks, examples -├── test/ ← test coverage, smoke tests, validation scripts -├── release/ ← packaging, versions, deployment, release readiness -├── maintenance/ ← dependency updates, hygiene, cleanup, small tech debt -├── research/ ← exploratory scientific / algorithmic investigation -├── experiment/ ← prototypes, spikes, proof-of-concept work -├── triage/ ← classification still unclear; needs manual review -│ -│ LIFECYCLE / META — not work-types; keep their own names. -├── issued/ ← prompts that have been routed via /start_dev -│ └── autolens_workspace_developer/ ← per-target subdirs preserved -│ -├── z_features/ ← multi-task epic trackers (one tracker → many sub-prompts) -│ └── complete/ ← archived trackers (all sub-prompts shipped) -│ -├── z_vault/ ← deferred prompts (z_ prefix sorts last in listings) - -│ -├── autoprompt/ ← prompts about THIS repo's own infrastructure (meta) -│ -├── scripts/ -│ ├── status.sh ← prompt inventory helper -│ └── prompt_sync.sh ← commit/push helpers sourced by skills -│ -└── skills/ ← Mind-owned skills + the ownership audit - ├── OWNERSHIP.md ← where every workflow skill lives, and why - └── create_issue/ ← convert a prompt into a tracked GitHub issue -``` - -`PyAutoMind/skills/` now holds **only** the Mind-owned `create_issue` skill (plus -`OWNERSHIP.md`). The development-workflow skills were re-homed to the organs that -own them — **PyAutoBrain** (`start_dev`, `start_dev_for_user`, `plan_branches`, -`start_library`, `start_workspace`, `ship_library`, `ship_workspace`, -`register_and_iterate`, `health` [the single health door, with `check` sweep, -`status` dashboard, and `full` release-run legs]), **PyAutoHeart** -(`worktree_status`, and the health-leg procedures `health_sweep/`, -`pyauto-status/`, and `pyauto-status-full/` that `/health` drives), and -**autolens_profiling** (`profile_likelihood`). The -`handoff` skill was retired (PyAutoBrain runs uniformly across execution -environments — see `OWNERSHIP.md`). General PyAuto tooling (release prep, -dependency audits, smoke tests, lint sweeps) lives in `admin_jammy/skills/`. - -`scripts/prompt_sync.sh` is sourced by skills that mutate registry files -(`active.md`, `complete.md`, etc.) to commit and push back to origin. It -replaces the previous `admin_jammy/software/admin_sync.sh` which operated on -`admin_jammy/prompt/`. - ---- - -## Prompt taxonomy - -PyAutoMind organises **intent by the kind of thinking required; PyAutoBrain uses -that structure to choose the right reasoning agent.** - -Prompts live at `//.md`: - -- The **first folder** answers *what kind of thinking or agent is needed?* — the - work type. -- The **second folder** answers *what domain or repository is affected?* — the - target repo (`autoarray`, `autofit`, `autogalaxy`, `autolens`, - `autolens_assistant`, `pyautobrain`, …), a workspace bucket (`workspaces`), or - a topic series (`jax_substructure`, `weak`, `cluster`, `priors`). - -### Work types → PyAutoBrain agents - -| Folder | Holds | Future PyAutoBrain agent | -|--------|-------|--------------------------| -| `feature/` | new user-facing or scientific capabilities | feature planner | -| `bug/` | incorrect behaviour, crashes, regressions | debugger | -| `refactor/` | internal restructuring, no intended behaviour change | refactor architect | -| `docs/` | documentation, tutorials, notebooks, examples | documentation agent | -| `test/` | test coverage, smoke tests, validation scripts | test engineer | -| `release/` | packaging, versions, deployment, release readiness | release engineer | -| `maintenance/` | dependency updates, hygiene, cleanup, small tech debt | hygiene agent | -| `research/` | exploratory scientific / algorithmic investigation | research analyst | -| `experiment/` | prototypes, spikes, proof-of-concept work | prototype agent | - -`triage/` holds prompts whose classification is still unclear — file there with a -short note and re-home once the work type is obvious. The full mapping (and the -note that the agents themselves live in PyAutoBrain, not here) is in -[`ROUTING.md`](ROUTING.md). - -### Good prompt paths - -``` -feature/autolens/potential_corrections.md -bug/autoarray/mask_edge_case.md -refactor/autofit/result_object_cleanup.md -docs/workspaces/pixelization_tutorial.md -research/autofit/sbi_design.md -experiment/autoarray/jax_sparse_mapping.md -``` - -### Not work-types - -`issued/`, `z_features/`, `z_vault/` are **workflow lifecycle** -folders, and `autoprompt/` holds **meta** prompts about this repo's own -infrastructure. They keep their own names and are not routed by work type. - -### Migration note - -The repository previously used the target repo as the first folder -(`autoarray/foo.md`). Those prompts have moved to `/autoarray/foo.md`. -Routing always keyed off the `@RepoName` references in a prompt's body, not its -folder, so the skills accept both old and new paths during the transition — but -new prompts should use the work-type layout. - ---- - -## Conventions - -### Naming - -- Prompt filenames are lowercase `kebab_or_snake_case.md`. -- Numbered series use a leading number: `0_docs.md`, `1_simulator.md`. Skipping a - number (e.g. `feature/weak/2_*.md` not present) is fine — it usually means a - step was consolidated or deferred. -- **First folder = work type** (`feature/`, `bug/`, …); **second folder = target** - repo or domain (lowercased, no `Py` prefix): `feature/autoarray/`, - `bug/autofit/`, `refactor/autogalaxy/`. Workspace prompts go under - `/workspaces/` regardless of which workspace. See "Prompt taxonomy". - -### Prompt file format - -Free-form markdown. Strong conventions: - -- Reference repos and files with `@RepoName/path/to/file.py` (e.g. - `@PyAutoFit/autofit/non_linear/search.py`). `/start_dev` parses these to - identify the primary target repo. -- One prompt = one task = one PR (ideally). If a prompt outlines several - loosely-related changes, split before issuing. -- No frontmatter required. Title in the first line is helpful but optional. -- **Optional metadata header.** A prompt may carry a light, human-writable header - near the top so both people and PyAutoBrain can see its type/target at a glance. - This is a convention, not a schema — never required, no YAML frontmatter: - - ```markdown - # Short task title - - Type: feature - Target: PyAutoLens - Repos: - - PyAutoLens - - autolens_workspace - Difficulty: medium # small | medium | large | too-large - Autonomy: supervised # safe | supervised | human-required - Priority: normal # low | normal | high - Status: draft - ``` - - When present, `Type:` should match the work-type folder. The goal is light - structure, not bureaucracy — prompts stay free-form prose. - - The optional `Difficulty:` / `Autonomy:` / `Priority:` keys let both people and - PyAutoBrain see, at a glance, how hard a task is, whether an agent can safely - take it on, and how urgent it is. What each `Autonomy:` level *does* at every - workflow checkpoint is defined once in `PyAutoBrain/AUTONOMY.md` (the autonomy - contract); levels bind only under an explicit `--auto` launch, and `--auto` - runs append their outcome to `autonomy_log.md` (the calibration log). The **Intake (Conception) Agent** writes these - automatically when it formalises a raw idea (`/intake`), sourcing `Difficulty:` - from the shared sizing faculty the Feature Agent also uses — so the value shown - up front is the one the Feature Agent later acts on. Still a convention, not a - schema: all keys remain optional and there is **no YAML frontmatter**. - -### `active.md` schema - -Each task is an H2 section: - -```markdown -## -- issue: https://github.com///issues/ -- session: claude --resume # optional -- status: -- location: # optional, used by /handoff -- question: # optional; set when status is awaiting-input - # (checkpoint-and-continue — PyAutoBrain/AUTONOMY.md) -- heart-ack: # optional; --auto launches: the exact YELLOW - - # reason set the human acknowledged -- worktree: ~/Code/PyAutoLabs-wt/ -- repos: - - : feature/ -- summary: | - Free-form summary of progress and next steps. -``` - -### `complete.md` schema - -```markdown -## -- issue: https://github.com///issues/ -- completed: YYYY-MM-DD -- library-pr: [, ] -- workspace-pr: [, ] -- notes: | - Long-form description of what landed, gotchas, follow-ups. -``` - -### `z_features/` (multi-task epics) - -`z_features/` holds **umbrella trackers** for multi-task epics — single -markdown files listing a sequence of sub-prompts that ship as their own PRs -under `autofit/`, `autogalaxy/`, etc. The tracker itself never becomes an -issue; only its sub-prompts do. - -``` -z_features/ -├── latent_refactor.md ← tracker (lists sub-prompt links) -├── ellipse_fitting_jax.md -├── ... -└── complete/ ← archived trackers (all sub-prompts shipped) -``` - -Use this pattern when a single ask decomposes into 5+ dependent sub-tasks. -`/start_dev z_features/.md` runs in **audit-only mode** — it reports -which sub-prompts are not-yet-issued / in-flight / shipped, and offers to -move the tracker to `z_features/complete/` once everything has landed. - -**Naming convention for clean audit:** the audit derives task-name -candidates from each sub-prompt's `issued/` filename stem with `_`→`-`. For -the audit to auto-match against `complete.md` headings, **the task slug in -`active.md` / `complete.md` must equal the issued filename's stem after -that transform**. - -| Issued filename | Task slug that matches | Task slug that does NOT match | -|---|---|---| -| `issued/latent_module_autogalaxy.md` | `latent-module-autogalaxy` ✓ | `latent-autogalaxy-module` ✗ | -| `issued/latent_smoke_test.md` | `latent-smoke-test` ✓ | `smoke-test-latent` ✗ | -| `issued/latent_variables_tutorial_expand_autofit.md` | `latent-variables-tutorial-expand-autofit` ✓ | `latent-tutorial-autofit` ✗ | - -The third row is the trap — if `/start_dev` renames the prompt on move -(e.g. appends a repo suffix for disambiguation) and `active.md`'s task slug -diverges from the issued stem, the audit will report the sub-prompt as -"in flight" forever and never archive the tracker. The cure is to either: - -- Pick the task slug at `/start_dev` time to match the eventual issued - filename stem, or -- Manually archive the tracker (`mv z_features/.md z_features/complete/` - + `prompt_sync_push`) when you know it's all shipped. - ---- - -## Tracking and inspection - -### Quick inventory - -```bash -bash scripts/status.sh -``` - -Prints counts per category, lists the active and recently-completed tasks, and -flags anything in `z_vault/` that's been sitting for a while. - -### From inside Claude Code - -- `/health status` — dashboard of registry state (active, planned, recent complete; PyAutoHeart, via the `/health` door) -- `/start_dev //.md` — read a prompt and route it (PyAutoBrain) -- `/worktree_status` — cross-references registry with task worktrees (PyAutoHeart) - ---- - -## How this repo integrates with the rest - -The PyAuto workflow has three repos with distinct roles: - -| Repo | Purpose | -|------|---------| -| **PyAutoMind** (this repo) | The Mind: ideas, intent, goals, priorities, the prompt registry and prompt-coupled skills. The starting point. | -| **admin_jammy** | Personal admin notes (`euclid.md`, `grants.md`, …) and general PyAuto tooling (`software/worktree.sh`, `software/admin_sync.sh`, generic skills like `audit_docs`, `dep_audit`, `repo_cleanup`). | -| **PyAutoPaper** | Personal paper-management repo: source PDFs (gitignored) plus topical LLM wikis (`lensing_wiki/`, `smbh_wiki/`, `cti_wiki/`, `methods_wiki/`, `galaxies_wiki/`) and a reading queue (`reading-queue.md`, moved from `admin_jammy/papers.md`). | -| **`PyAuto*` libraries and `*_workspace*` repos** | Where the actual code work happens. Each task gets a feature branch + worktree under `~/Code/PyAutoLabs-wt//`. | - -Helper scripts that this repo's skills source: - -- `admin_jammy/software/worktree.sh` — task worktree management (create, remove, conflict check). -- `admin_jammy/software/admin_sync.sh` — admin_jammy/PyAutoMind sync helpers. - -These intentionally live in `admin_jammy/software/` because they're general -multi-repo tooling, not prompt-specific. The skills that need them source by -absolute path. - ---- - -## Bootstrap on a new machine - -```bash -cd ~/Code/PyAutoLabs -git clone git@github.com:PyAutoLabs/PyAutoMind.git # the Mind (this repo) -git clone git@github.com:PyAutoLabs/PyAutoBrain.git # dev-workflow skills -git clone git@github.com:PyAutoLabs/PyAutoHeart.git # status / readiness skills -git clone git@github.com:Jammy2211/admin_jammy.git # general tooling (optional) -bash PyAutoBrain/bin/install.sh # symlinks skills + commands -``` - -> **The local checkout directory must be named `PyAutoMind`.** The skills and -> scripts reference `PyAutoMind/...` paths directly — e.g. -> `source PyAutoMind/scripts/prompt_sync.sh` and `git -C PyAutoMind …` — so a -> differently-named directory breaks those commands. - -`install.sh` auto-discovers skills from every present discovery root -(`admin_jammy/skills/`, `PyAutoMind/skills/`, `PyAutoBrain/skills/`, -`PyAutoHeart/skills/`, `autolens_profiling/skills/`) and creates symlinks under -`~/.claude/skills/` and `~/.claude/commands/`. Roots that aren't checked out are -skipped. Re-run any time after pulling new skills from any of those repos. +The Mind of the PyAuto organism: every piece of work in the ecosystem starts +here, as a markdown file describing what you want in plain English. An AI +agent (or a human) picks the file up and turns it into a tracked GitHub +issue, a feature branch, and a merged pull request. No template, no special +syntax — if you can describe the change in a GitHub issue, you can drive the +workflow. + +What lives here: + +| File / folder | What it is | +|---------------|------------| +| `ideas.md` | raw incubating ideas, no structure required | +| `//*.md` | scoped prompts (`feature/`, `bug/`, `docs/`, …) | +| `active.md`, `planned.md`, `complete.md` | the task registry: in flight, queued, done | +| `issued/` | prompts that have become tracked issues | +| `repos.yaml` | the body map — the single source of repo identity | +| `scripts/` | registry sync + drift checks (`repos_sync.py`) | + +A prompt flows: idea → prompt file → `/start_dev` → GitHub issue + +`active.md` entry → worktree development → PR → `complete.md`. The registry +is shared state, so any machine or session can pick up an in-flight task. + +The schemas and conventions — prompt taxonomy, prompt file format, the +`active.md` / `complete.md` schemas, epic trackers, bootstrap on a new +machine — are in [REFERENCE.md](REFERENCE.md). How agents should operate +this repo is in [AGENTS.md](AGENTS.md). + +The organism this repo is the Mind of (Mind, Brain, Heart, Hands, Memory) is +described once in +[PyAutoBrain/ORGANISM.md](https://github.com/PyAutoLabs/PyAutoBrain/blob/main/ORGANISM.md). diff --git a/REFERENCE.md b/REFERENCE.md new file mode 100644 index 00000000..6d6ccc05 --- /dev/null +++ b/REFERENCE.md @@ -0,0 +1,459 @@ +# PyAutoMind reference + +The registry schemas, prompt conventions, and workflow detail for this repo. +Moved verbatim from `README.md` on 2026-07-10 — agent docs that point at +README sections ("Prompt taxonomy", "Prompt file format", the `active.md` / +`complete.md` schemas) resolve here, one link from the README. + +--- + +## What a prompt looks like + +Here's a real prompt — the contents of `autoarray/psf_oversampling.md` — that +became a tracked task. This is the level and style of detail to aim for in +your own GitHub issue: free-form prose, with `@RepoName/path/to/file.py` +references so the tooling knows which repo and files to target. No boilerplate. + +````markdown +A point spread function is used to blur images via 2d convolution. + +This blurring occurs predominantly in the package @PyAutoArray/autoarray/operators/convolver.py. + +The source code currently requires PSF blurring to occur at the same resolution (pixel scale) as the +image, meaning the PSF is always the same resolution as the image. + +However, for modeling, convolution can be performed at a higher resolution than the image, which allows for more accurate +blurring and modeling of the image. This requires us to have an oversampled PSF, which is a PSF that has a higher +resolution than the image. + +For modeling, where images are generated PSF blurring happens in @PyAutoGalaxy/autogalaxy/operate/image.py. + +Modeling can always evaluate images using a hgiher resolition grid, blurring them with the PSF at high +resolution and then downsample to the observed image resolution. Oversampling is implemented in +@PyAutoArray/autoarray/operators/over_sampling. + +Note that over sampling often uses an adaptive sub-szie, which means that 2D covnolution with a PSF is not +well defined. for now, we will assume adaptive over sampling is not used. + +I want us to be able to append the Convolver class with a convolve_over_sample_size integer, which specifies the over sample size of the PSF. +This will allow us to perform convolution at a higher resolution than the image, which will improve the accuracy of the blurring and modeling of the image. +For example, if convolve_over_sample_size is 2, then the PSF will be oversampled by a factor of 2, meaning it will have a resolution that is 2 times higher than the image. + +This, in turn, means out imaging object @PyAutoArray/autoarray/dataset/imaging/dataset.py will need to be +extended to include the convolve_over_sample_size_lp and convolve_over_sample_size_pixelization attributes, which will +specify the over sample size of the PSF for the lensing and pixelization operations, respectively. + +class Imaging(AbstractDataset): + def __init__( + self, + data: Array2D, + noise_map: Optional[Array2D] = None, + psf: Optional[Convolver] = None, + psf_setup_state: bool = False, + noise_covariance_matrix: Optional[np.ndarray] = None, + over_sample_size_lp: Union[int, Array2D] = 4, + over_sample_size_pixelization: Union[int, Array2D] = 4, + use_normalized_psf: Optional[bool] = True, + check_noise_map: bool = True, + sparse_operator: Optional[ImagingSparseOperator] = None, + ): + + +Also,read through the @PyAutoArray/autoarray/inversion/inversion/imaging package, and parents, to see +how PSF convolution enters this. I think we can get it to work in PyAutoArray/autoarray/inversion/inversion/imaging/mapping.py, +and will leave work in PyAutoArray/autoarray/inversion/inversion/imaging/sparse.py to future work. + +This is a complex task, therefore I think we should extend @autolens_workspace_test/scripts/imaging/convolution.py +with a numerical test. + +We should then build on this test in a separate test file using a simple over sampled PSF, to get a numerical +result we can test the source code against. + +@autolens_workspace/scripts/imaging/simulator.py is a good example we can build on to show how to use +over sampled PSFs in a real simulation. We can extend this script to show how to use over sampled PSFs in a real simulation. + +Come up with a plan to implement over sampled PSFs. +```` + +That prompt becomes a GitHub issue, gets routed to the affected repos +(`PyAutoArray`, `PyAutoGalaxy`, the autolens workspaces), and lands as PRs +against each. Typos, half-finished thoughts, and "I think we should…" are +fine — write naturally, the AI fills in the rest. + +--- + +## How a prompt flows through the workflow + +``` + idea ── you write it in ideas.md + │ + ▼ + draft prompt ── you write a markdown file under //.md + │ + ▼ + /start_dev ── reads the prompt, audits the code, drafts an issue, + │ creates the GitHub issue, registers the task in + │ active.md, moves the prompt to issued/ + ▼ + active.md entry ── the task is now tracked across machines and sessions + │ + ▼ + /start_library ── creates a worktree, branch, opens dev environment + or (or workspace variant — chosen automatically) + /start_workspace + │ + ▼ + development ── code, tests, run smoke tests, commit + │ + ▼ + /ship_library ── runs tests, opens PR, waits for merge + or + /ship_workspace + │ + ▼ + PR merged ── post-merge cleanup deletes the worktree, removes the + │ active.md entry, appends a summary to complete.md + ▼ + done +``` + +The slash commands above are skills hosted across the organism (Brain, Heart) but +all read/write Mind's registry via workspace-root-anchored paths. One operates +over the registry without starting work: + +- `/health status` — dashboard of `active.md`, `planned.md`, `complete.md` + (a PyAutoHeart status view, reached through the single `/health` door). + Continuity across execution environments needs no special step — any + environment reads `active.md` and resumes an in-flight task. + +--- + +## Repository layout + +``` +PyAutoMind/ +├── README.md ← short front page +├── REFERENCE.md ← this file (schemas + conventions) +├── .gitignore +│ +├── active.md ← tasks currently in progress (one ## section per task) +├── complete.md ← finished tasks (most recent first) +├── ideas.md ← raw incubating ideas, no structure required +├── parked.md ← started/scoped but not in flight (stashes, orphan worktrees, deferred) +├── planned.md ← issued tasks blocked from starting (created on demand) + +├── queue.md ← processing queue for /register_and_iterate +│ +│ PROMPTS — organised by WORK TYPE (first folder), then TARGET (second folder). +│ See "Prompt taxonomy" below and ROUTING.md. +├── feature/ ← new user-facing or scientific capabilities +│ ├── autoarray/ autofit/ autogalaxy/ autolens/ autolens_assistant/ … +│ ├── workspaces/ ← any *_workspace repo +│ ├── pyautobrain/ ← prompts that implement PyAutoBrain agents +│ ├── jax_substructure/ weak/ cluster/ ← numbered topic series (kept together) +├── bug/ ← incorrect behaviour, crashes, regressions +│ ├── autofit/ autogalaxy/ autolens/ autoarray/ priors/ … +├── refactor/ ← internal restructuring, no intended behaviour change +├── docs/ ← documentation, tutorials, notebooks, examples +├── test/ ← test coverage, smoke tests, validation scripts +├── release/ ← packaging, versions, deployment, release readiness +├── maintenance/ ← dependency updates, hygiene, cleanup, small tech debt +├── research/ ← exploratory scientific / algorithmic investigation +├── experiment/ ← prototypes, spikes, proof-of-concept work +├── triage/ ← classification still unclear; needs manual review +│ +│ LIFECYCLE / META — not work-types; keep their own names. +├── issued/ ← prompts that have been routed via /start_dev +│ └── autolens_workspace_developer/ ← per-target subdirs preserved +│ +├── z_features/ ← multi-task epic trackers (one tracker → many sub-prompts) +│ └── complete/ ← archived trackers (all sub-prompts shipped) +│ +├── z_vault/ ← deferred prompts (z_ prefix sorts last in listings) + +│ +├── autoprompt/ ← prompts about THIS repo's own infrastructure (meta) +│ +├── scripts/ +│ ├── status.sh ← prompt inventory helper +│ └── prompt_sync.sh ← commit/push helpers sourced by skills +│ +└── skills/ ← Mind-owned skills + the ownership audit + ├── OWNERSHIP.md ← where every workflow skill lives, and why + └── create_issue/ ← convert a prompt into a tracked GitHub issue +``` + +`PyAutoMind/skills/` now holds **only** the Mind-owned `create_issue` skill (plus +`OWNERSHIP.md`). The development-workflow skills were re-homed to the organs that +own them — **PyAutoBrain** (`start_dev`, `start_dev_for_user`, `plan_branches`, +`start_library`, `start_workspace`, `ship_library`, `ship_workspace`, +`register_and_iterate`, `health` [the single health door, with `check` sweep, +`status` dashboard, and `full` release-run legs]), **PyAutoHeart** +(`worktree_status`, and the health-leg procedures `health_sweep/`, +`pyauto-status/`, and `pyauto-status-full/` that `/health` drives), and +**autolens_profiling** (`profile_likelihood`). The +`handoff` skill was retired (PyAutoBrain runs uniformly across execution +environments — see `OWNERSHIP.md`). General PyAuto tooling (release prep, +dependency audits, smoke tests, lint sweeps) lives in `admin_jammy/skills/`. + +`scripts/prompt_sync.sh` is sourced by skills that mutate registry files +(`active.md`, `complete.md`, etc.) to commit and push back to origin. It +replaces the previous `admin_jammy/software/admin_sync.sh` which operated on +`admin_jammy/prompt/`. + +--- + +## Prompt taxonomy + +PyAutoMind organises **intent by the kind of thinking required; PyAutoBrain uses +that structure to choose the right reasoning agent.** + +Prompts live at `//.md`: + +- The **first folder** answers *what kind of thinking or agent is needed?* — the + work type. +- The **second folder** answers *what domain or repository is affected?* — the + target repo (`autoarray`, `autofit`, `autogalaxy`, `autolens`, + `autolens_assistant`, `pyautobrain`, …), a workspace bucket (`workspaces`), or + a topic series (`jax_substructure`, `weak`, `cluster`, `priors`). + +### Work types → PyAutoBrain agents + +| Folder | Holds | Future PyAutoBrain agent | +|--------|-------|--------------------------| +| `feature/` | new user-facing or scientific capabilities | feature planner | +| `bug/` | incorrect behaviour, crashes, regressions | debugger | +| `refactor/` | internal restructuring, no intended behaviour change | refactor architect | +| `docs/` | documentation, tutorials, notebooks, examples | documentation agent | +| `test/` | test coverage, smoke tests, validation scripts | test engineer | +| `release/` | packaging, versions, deployment, release readiness | release engineer | +| `maintenance/` | dependency updates, hygiene, cleanup, small tech debt | hygiene agent | +| `research/` | exploratory scientific / algorithmic investigation | research analyst | +| `experiment/` | prototypes, spikes, proof-of-concept work | prototype agent | + +`triage/` holds prompts whose classification is still unclear — file there with a +short note and re-home once the work type is obvious. The full mapping (and the +note that the agents themselves live in PyAutoBrain, not here) is in +[`ROUTING.md`](ROUTING.md). + +### Good prompt paths + +``` +feature/autolens/potential_corrections.md +bug/autoarray/mask_edge_case.md +refactor/autofit/result_object_cleanup.md +docs/workspaces/pixelization_tutorial.md +research/autofit/sbi_design.md +experiment/autoarray/jax_sparse_mapping.md +``` + +### Not work-types + +`issued/`, `z_features/`, `z_vault/` are **workflow lifecycle** +folders, and `autoprompt/` holds **meta** prompts about this repo's own +infrastructure. They keep their own names and are not routed by work type. + +### Migration note + +The repository previously used the target repo as the first folder +(`autoarray/foo.md`). Those prompts have moved to `/autoarray/foo.md`. +Routing always keyed off the `@RepoName` references in a prompt's body, not its +folder, so the skills accept both old and new paths during the transition — but +new prompts should use the work-type layout. + +--- + +## Conventions + +### Naming + +- Prompt filenames are lowercase `kebab_or_snake_case.md`. +- Numbered series use a leading number: `0_docs.md`, `1_simulator.md`. Skipping a + number (e.g. `feature/weak/2_*.md` not present) is fine — it usually means a + step was consolidated or deferred. +- **First folder = work type** (`feature/`, `bug/`, …); **second folder = target** + repo or domain (lowercased, no `Py` prefix): `feature/autoarray/`, + `bug/autofit/`, `refactor/autogalaxy/`. Workspace prompts go under + `/workspaces/` regardless of which workspace. See "Prompt taxonomy". + +### Prompt file format + +Free-form markdown. Strong conventions: + +- Reference repos and files with `@RepoName/path/to/file.py` (e.g. + `@PyAutoFit/autofit/non_linear/search.py`). `/start_dev` parses these to + identify the primary target repo. +- One prompt = one task = one PR (ideally). If a prompt outlines several + loosely-related changes, split before issuing. +- No frontmatter required. Title in the first line is helpful but optional. +- **Optional metadata header.** A prompt may carry a light, human-writable header + near the top so both people and PyAutoBrain can see its type/target at a glance. + This is a convention, not a schema — never required, no YAML frontmatter: + + ```markdown + # Short task title + + Type: feature + Target: PyAutoLens + Repos: + - PyAutoLens + - autolens_workspace + Difficulty: medium # small | medium | large | too-large + Autonomy: supervised # safe | supervised | human-required + Priority: normal # low | normal | high + Status: draft + ``` + + When present, `Type:` should match the work-type folder. The goal is light + structure, not bureaucracy — prompts stay free-form prose. + + The optional `Difficulty:` / `Autonomy:` / `Priority:` keys let both people and + PyAutoBrain see, at a glance, how hard a task is, whether an agent can safely + take it on, and how urgent it is. What each `Autonomy:` level *does* at every + workflow checkpoint is defined once in `PyAutoBrain/AUTONOMY.md` (the autonomy + contract); levels bind only under an explicit `--auto` launch, and `--auto` + runs append their outcome to `autonomy_log.md` (the calibration log). The **Intake (Conception) Agent** writes these + automatically when it formalises a raw idea (`/intake`), sourcing `Difficulty:` + from the shared sizing faculty the Feature Agent also uses — so the value shown + up front is the one the Feature Agent later acts on. Still a convention, not a + schema: all keys remain optional and there is **no YAML frontmatter**. + +### `active.md` schema + +Each task is an H2 section: + +```markdown +## +- issue: https://github.com///issues/ +- session: claude --resume # optional +- status: +- location: # optional, used by /handoff +- question: # optional; set when status is awaiting-input + # (checkpoint-and-continue — PyAutoBrain/AUTONOMY.md) +- heart-ack: # optional; --auto launches: the exact YELLOW + - # reason set the human acknowledged +- worktree: ~/Code/PyAutoLabs-wt/ +- repos: + - : feature/ +- summary: | + Free-form summary of progress and next steps. +``` + +### `complete.md` schema + +```markdown +## +- issue: https://github.com///issues/ +- completed: YYYY-MM-DD +- library-pr: [, ] +- workspace-pr: [, ] +- notes: | + Long-form description of what landed, gotchas, follow-ups. +``` + +### `z_features/` (multi-task epics) + +`z_features/` holds **umbrella trackers** for multi-task epics — single +markdown files listing a sequence of sub-prompts that ship as their own PRs +under `autofit/`, `autogalaxy/`, etc. The tracker itself never becomes an +issue; only its sub-prompts do. + +``` +z_features/ +├── latent_refactor.md ← tracker (lists sub-prompt links) +├── ellipse_fitting_jax.md +├── ... +└── complete/ ← archived trackers (all sub-prompts shipped) +``` + +Use this pattern when a single ask decomposes into 5+ dependent sub-tasks. +`/start_dev z_features/.md` runs in **audit-only mode** — it reports +which sub-prompts are not-yet-issued / in-flight / shipped, and offers to +move the tracker to `z_features/complete/` once everything has landed. + +**Naming convention for clean audit:** the audit derives task-name +candidates from each sub-prompt's `issued/` filename stem with `_`→`-`. For +the audit to auto-match against `complete.md` headings, **the task slug in +`active.md` / `complete.md` must equal the issued filename's stem after +that transform**. + +| Issued filename | Task slug that matches | Task slug that does NOT match | +|---|---|---| +| `issued/latent_module_autogalaxy.md` | `latent-module-autogalaxy` ✓ | `latent-autogalaxy-module` ✗ | +| `issued/latent_smoke_test.md` | `latent-smoke-test` ✓ | `smoke-test-latent` ✗ | +| `issued/latent_variables_tutorial_expand_autofit.md` | `latent-variables-tutorial-expand-autofit` ✓ | `latent-tutorial-autofit` ✗ | + +The third row is the trap — if `/start_dev` renames the prompt on move +(e.g. appends a repo suffix for disambiguation) and `active.md`'s task slug +diverges from the issued stem, the audit will report the sub-prompt as +"in flight" forever and never archive the tracker. The cure is to either: + +- Pick the task slug at `/start_dev` time to match the eventual issued + filename stem, or +- Manually archive the tracker (`mv z_features/.md z_features/complete/` + + `prompt_sync_push`) when you know it's all shipped. + +--- + +## Tracking and inspection + +### Quick inventory + +```bash +bash scripts/status.sh +``` + +Prints counts per category, lists the active and recently-completed tasks, and +flags anything in `z_vault/` that's been sitting for a while. + +### From inside Claude Code + +- `/health status` — dashboard of registry state (active, planned, recent complete; PyAutoHeart, via the `/health` door) +- `/start_dev //.md` — read a prompt and route it (PyAutoBrain) +- `/worktree_status` — cross-references registry with task worktrees (PyAutoHeart) + +--- + +## How this repo integrates with the rest + +The PyAuto workflow has three repos with distinct roles: + +| Repo | Purpose | +|------|---------| +| **PyAutoMind** (this repo) | The Mind: ideas, intent, goals, priorities, the prompt registry and prompt-coupled skills. The starting point. | +| **admin_jammy** | Personal admin notes (`euclid.md`, `grants.md`, …) and general PyAuto tooling (`software/worktree.sh`, `software/admin_sync.sh`, generic skills like `audit_docs`, `dep_audit`, `repo_cleanup`). | +| **PyAutoMemory** | The Memory organ: topical LLM wikis (`lensing_wiki/`, `smbh_wiki/`, `cti_wiki/`, `methods_wiki/`, `galaxies_wiki/`) and a reading queue (`reading-queue.md`, moved from `admin_jammy/papers.md`). | +| **`PyAuto*` libraries and `*_workspace*` repos** | Where the actual code work happens. Each task gets a feature branch + worktree under `~/Code/PyAutoLabs-wt//`. | + +Helper scripts that this repo's skills source: + +- `admin_jammy/software/worktree.sh` — task worktree management (create, remove, conflict check). +- `admin_jammy/software/admin_sync.sh` — admin_jammy/PyAutoMind sync helpers. + +These intentionally live in `admin_jammy/software/` because they're general +multi-repo tooling, not prompt-specific. The skills that need them source by +absolute path. + +--- + +## Bootstrap on a new machine + +```bash +cd ~/Code/PyAutoLabs +git clone git@github.com:PyAutoLabs/PyAutoMind.git # the Mind (this repo) +git clone git@github.com:PyAutoLabs/PyAutoBrain.git # dev-workflow skills +git clone git@github.com:PyAutoLabs/PyAutoHeart.git # status / readiness skills +git clone git@github.com:Jammy2211/admin_jammy.git # general tooling (optional) +bash PyAutoBrain/bin/install.sh # symlinks skills + commands +``` + +> **The local checkout directory must be named `PyAutoMind`.** The skills and +> scripts reference `PyAutoMind/...` paths directly — e.g. +> `source PyAutoMind/scripts/prompt_sync.sh` and `git -C PyAutoMind …` — so a +> differently-named directory breaks those commands. + +`install.sh` auto-discovers skills from every present discovery root +(`admin_jammy/skills/`, `PyAutoMind/skills/`, `PyAutoBrain/skills/`, +`PyAutoHeart/skills/`, `autolens_profiling/skills/`) and creates symlinks under +`~/.claude/skills/` and `~/.claude/commands/`. Roots that aren't checked out are +skipped. Re-run any time after pulling new skills from any of those repos. diff --git a/ideas.md b/ideas.md index 4c16e37c..64930ce1 100644 --- a/ideas.md +++ b/ideas.md @@ -21,3 +21,7 @@ - [from: research jax-autodiff-gradients-audit (#87) · source_plane probe] Fix `Grid2DIrregular.grid_2d_via_deflection_grid_from` xp propagation so the point-source likelihood's forward `jax.jit` works (gradients already work; the jit gap blocks fast batched sampling). - [from: research jax-autodiff-gradients-audit (#87) · interferometer probe] Interferometer MGE FD correctness test: probe is 9/9 finite but no autodiff-vs-finite-difference validation exists for the visibility-space likelihood — add `jax_grad/interferometer_mge.py` on the pattern of the imaging suite. - [from: research jax-autodiff-gradients-audit (#87) · validated-likelihood set] NUTS/HMC sampler trial on the FD-validated likelihoods (weak lensing, point-source source-plane, imaging with RectangularUniform or parametric sources) via the sampler pipeline — gradients are now certified correct end-to-end, removing the main risk that stalled gradient-based inference. +- [from: PyAutoBuild/to_do_list, evacuated 2026-07-10 by pyautoscientist-phase1] Test different numpy, scipy, scikit-learn, threadpoolctl, joblib versions. +- [from: PyAutoBuild/to_do_list] Tests using full dynesty runs with inversion, BrightnessImageNN pix, with parallel processing. +- [from: PyAutoBuild/to_do_list] Workspace tagging. +- [from: PyAutoBuild/to_do_list] Remove annoying report.log and root.log once and for all. diff --git a/scripts/repos_sync.py b/scripts/repos_sync.py index e40e5d5e..bbb7f771 100644 --- a/scripts/repos_sync.py +++ b/scripts/repos_sync.py @@ -22,6 +22,9 @@ * PyAutoBuild/pre_build.sh — run_workspace repos exist * admin_jammy/software/ensure_workspace_labels.sh — owner/name pairs match * the `origin` remote of every local checkout — manifest matches reality + * the tenant firewall — no instance fact (satellite repo name, GitHub + owner, workspace path) in Brain/Heart/Build *.py / *.sh outside the + declared config surfaces (FIREWALL_ALLOWLIST below) Exit code 0 = no drift; 1 = drift found (each mismatch printed). """ @@ -171,6 +174,147 @@ def check_labels(root, repos): return problems +# -------------------------------------------------------------------------- +# Tenant firewall +# -------------------------------------------------------------------------- +# +# The framework organs (Brain, Heart, Build) must stay adoptable as a +# config-diff fork: an adopter replaces only the declared config surfaces +# and pulls upstream cleanly. This check keeps instance facts — satellite +# repo names, GitHub owners, workspace paths — from leaking into organ code +# outside those surfaces. Skills prose (*.md) and AGENTS.md are out of scope +# by design (production prompts, never genericised). + +FIREWALL_ORGANS = ("PyAutoBrain", "PyAutoHeart", "PyAutoBuild") + +# The declared config surfaces, frozen as a per-file token baseline (seeded +# 2026-07-10 from the live mains; the §1 inventory of the PyAutoScientist +# assessment names the load-bearing ones). Semantics: a NEW instance fact in +# a listed file, or ANY instance fact in an unlisted file, is drift. Phase-3 +# config extraction shrinks this list; never grow it casually — a new entry +# means a new file an adopting fork must rewrite. +FIREWALL_ALLOWLIST = { + "PyAutoBrain/agents/_common.sh": {"PyAutoLabs"}, + "PyAutoBrain/agents/conductors/bug/_bug.py": {"PyAutoArray"}, + "PyAutoBrain/agents/conductors/bug/bug.sh": {"PyAutoLabs"}, + "PyAutoBrain/agents/conductors/health/health.sh": {"PyAutoConf"}, + "PyAutoBrain/agents/conductors/intake/_intake.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autolens_workspace"}, + "PyAutoBrain/agents/conductors/profiling/_profiling.py": {"PyAutoLabs", "autolens_profiling"}, + "PyAutoBrain/agents/conductors/profiling/profiling.sh": {"autolens_profiling"}, + "PyAutoBrain/agents/conductors/refactor/_refactor.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens"}, + "PyAutoBrain/agents/conductors/release/activity_gate.py": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoBrain/agents/conductors/release/nightly.sh": {"PyAutoLabs", "PyAutoLens"}, + "PyAutoBrain/agents/conductors/release/rehearse.sh": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens"}, + "PyAutoBrain/agents/conductors/release/validate.sh": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens"}, + "PyAutoBrain/agents/faculties/memory/_memory.py": {"autolens_assistant"}, + "PyAutoBrain/agents/faculties/memory/memory.sh": {"autolens_assistant"}, + "PyAutoBrain/agents/faculties/review/_review.py": {"PyAutoLabs"}, + "PyAutoBrain/agents/faculties/review/review.sh": {"PyAutoLabs"}, + "PyAutoBrain/agents/faculties/samplers/_samplers.py": {"PyAutoFit", "autofit_workspace_developer", "autofit_workspace_test"}, + "PyAutoBrain/agents/faculties/samplers/samplers.sh": {"PyAutoFit", "autofit_workspace_developer", "autofit_workspace_test"}, + "PyAutoBrain/agents/faculties/sizing/_sizing.py": {"autofit_workspace", "autofit_workspace_test", "autogalaxy_workspace", "autogalaxy_workspace_test", "autolens_assistant", "autolens_profiling", "autolens_workspace", "autolens_workspace_test"}, + "PyAutoBrain/bin/check_skill_line_counts.sh": {"admin_jammy", "autolens_profiling"}, + "PyAutoBrain/bin/install.sh": {"PyAutoFit", "PyAutoLabs", "admin_jammy", "autolens_profiling"}, + "PyAutoBrain/tests/test_activity_gate.py": {"HowToFit", "HowToLens", "PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoLens", "autolens_workspace"}, + "PyAutoBuild/autobuild/aggregate_results.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoBuild/autobuild/build_util.py": {"PyAutoConf"}, + "PyAutoBuild/autobuild/bump_colab_urls.sh": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoLabs", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoBuild/autobuild/create_analysis_issue.py": {"PyAutoLabs"}, + "PyAutoBuild/autobuild/generate_autofit.py": {"autofit_workspace"}, + "PyAutoBuild/autobuild/generate_release_notes.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens"}, + "PyAutoBuild/autobuild/navigator.py": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens"}, + "PyAutoBuild/autobuild/repro_command.py": {"PyAutoLabs", "autogalaxy_workspace_test"}, + "PyAutoBuild/autobuild/run_all.py": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoLabs", "autofit_workspace", "autofit_workspace_test", "autogalaxy_workspace", "autogalaxy_workspace_test", "autolens_workspace", "autolens_workspace_test", "euclid_strong_lens_modeling_pipeline"}, + "PyAutoBuild/autobuild/slow_skip_check.py": {"autofit_workspace", "autofit_workspace_test", "autogalaxy_workspace", "autolens_workspace", "autolens_workspace_test"}, + "PyAutoBuild/autobuild/tag_and_merge.sh": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens"}, + "PyAutoBuild/pre_build.sh": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens", "admin_jammy", "autofit_workspace", "autofit_workspace_developer", "autofit_workspace_test", "autogalaxy_workspace", "autogalaxy_workspace_test", "autolens_assistant", "autolens_workspace", "autolens_workspace_developer", "autolens_workspace_test", "euclid_strong_lens_modeling_pipeline"}, + "PyAutoBuild/tests/test_bump_colab_urls.py": {"Jammy2211", "PyAutoFit", "PyAutoLabs", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoBuild/tests/test_release_notes.py": {"PyAutoArray", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens"}, + "PyAutoBuild/tests/test_run_all_history.py": {"HowToLens", "autogalaxy_workspace_test", "euclid_strong_lens_modeling_pipeline"}, + "PyAutoBuild/tests/test_workspace_config_precedence.py": {"autofit_workspace", "autofit_workspace_test", "autogalaxy_workspace", "autogalaxy_workspace_test", "autolens_workspace", "autolens_workspace_test"}, + "PyAutoHeart/heart/_color.sh": {"PyAutoFit"}, + "PyAutoHeart/heart/_common.sh": {"PyAutoLabs"}, + "PyAutoHeart/heart/checks/ci_status.py": {"autolens_workspace"}, + "PyAutoHeart/heart/checks/manifest_drift.py": {"PyAutoLabs", "admin_jammy"}, + "PyAutoHeart/heart/checks/profiling_drift.py": {"PyAutoLabs", "autolens_profiling", "autolens_workspace_test"}, + "PyAutoHeart/heart/checks/script_timing.py": {"PyAutoLabs"}, + "PyAutoHeart/heart/checks/test_run.py": {"PyAutoLabs"}, + "PyAutoHeart/heart/checks/url_check.sh": {"HowToFit", "HowToGalaxy", "HowToLens", "Jammy2211", "PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoHeart/heart/checks/url_check_live.py": {"Jammy2211", "PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens", "admin_jammy", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoHeart/heart/checks/url_sweep.sh": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace", "euclid_strong_lens_modeling_pipeline"}, + "PyAutoHeart/heart/checks/verify_install.sh": {"PyAutoConf", "PyAutoLabs", "PyAutoLens", "autolens_workspace"}, + "PyAutoHeart/heart/checks/version_skew.py": {"HowToFit", "HowToGalaxy", "HowToLens", "PyAutoFit", "PyAutoGalaxy", "PyAutoLabs", "PyAutoLens", "autofit_workspace", "autogalaxy_workspace", "autolens_assistant", "autolens_workspace", "euclid_strong_lens_modeling_pipeline"}, + "PyAutoHeart/heart/checks/worktree_drift.sh": {"PyAutoLabs"}, + "PyAutoHeart/heart/dashboard.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autolens_profiling", "pyautolabs.github.io"}, + "PyAutoHeart/heart/fix.py": {"PyAutoFit", "PyAutoLabs"}, + "PyAutoHeart/heart/readiness.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autolens_profiling", "autolens_workspace_test"}, + "PyAutoHeart/heart/shell/heart_prompt.sh": {"PyAutoLabs"}, + "PyAutoHeart/heart/state.py": {"PyAutoFit"}, + "PyAutoHeart/heart/tick.sh": {"autolens_profiling"}, + "PyAutoHeart/heart/validate.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autolens_workspace", "autolens_workspace_test"}, + "PyAutoHeart/scripts/health_audit.sh": {"PyAutoLabs"}, + "PyAutoHeart/scripts/health_release.sh": {"PyAutoLabs"}, + "PyAutoHeart/scripts/health_sync.sh": {"PyAutoLabs", "admin_jammy"}, + "PyAutoHeart/tests/test_ci_status.py": {"PyAutoFit", "PyAutoLens", "autolens_workspace"}, + "PyAutoHeart/tests/test_dashboard.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autolens_workspace"}, + "PyAutoHeart/tests/test_manifest_drift.py": {"PyAutoConf", "PyAutoFit", "PyAutoLabs"}, + "PyAutoHeart/tests/test_noise.py": {"HowToFit", "autolens_workspace_test"}, + "PyAutoHeart/tests/test_readiness.py": {"HowToLens", "PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autogalaxy_workspace", "autolens_workspace", "autolens_workspace_test"}, + "PyAutoHeart/tests/test_state.py": {"PyAutoArray", "PyAutoFit"}, + "PyAutoHeart/tests/test_test_run.py": {"autofit_workspace", "autolens_workspace"}, + "PyAutoHeart/tests/test_url_check.py": {"HowToFit", "HowToGalaxy", "HowToLens", "Jammy2211", "PyAutoFit", "PyAutoLabs", "autofit_workspace", "autogalaxy_workspace", "autolens_workspace"}, + "PyAutoHeart/tests/test_validate.py": {"PyAutoArray", "PyAutoConf", "PyAutoFit", "PyAutoGalaxy", "PyAutoLens", "autolens_workspace", "autolens_workspace_test"}, + "PyAutoHeart/tests/test_verify_install_script.py": {"Jammy2211", "PyAutoLabs", "autolens_workspace"}, + "PyAutoHeart/tests/test_version_skew.py": {"HowToFit", "PyAutoFit", "PyAutoLens", "autofit_workspace", "autolens_assistant", "autolens_workspace", "autolens_workspace_test"}, +} + + +def firewall_tokens(repos): + """Instance facts to hunt for: every non-organ repo name, every GitHub + owner, and the local workspace home. Organ names are framework identity + (a fork keeps them), so they are not tokens.""" + tokens = {name for name, r in repos.items() if r["category"] != "organ"} + tokens |= {owner_of(r) for r in repos.values()} + tokens.add("/home/jammy") + return sorted(tokens, key=len, reverse=True) + + +def check_tenant_firewall(root, repos): + pattern = re.compile( + "|".join( + r"(?