Skip to content

feat(sweep): enumerate the org for repo settings, not the body map #291

Closed

Description

@Jammy2211

Overview

PyAutoBrain#290 shipped repo_settings.yml, a weekly sweep that turns on
"Automatically delete head branches" per repo, and removed /prm's
remote-delete step. That much works. But the sweep picks its targets from
PyAutoMind/repos.yaml, so it cannot see a repo until someone registers it —
and org repos are created by hand on github.com, with no gh repo create path
in the organism to hook. Nine live org repos are absent from the body map
entirely and ten more are excluded by category, so 27 of 44 are swept.

This switches the sweep to enumerate the organisation live from the GitHub API,
which closes the new-repo hole, the unregistered-repo hole and the category hole
at once — and removes the need for a category boundary on this consumer at all.

Plan

  • PyAutoBrain#290 shipped the sweep and removed /prm's remote-delete step. That half is done and is not reopened here.
  • The sweep reads repos.yaml, so a repo is invisible until registered — and nothing in the organism creates org repos, so there is no birth-time hook to add.
  • Nine live org repos are absent from the body map; ten more are excluded by category. 27 of 44 org repos are swept.
  • Switch to live org enumeration (non-archived repos under the workflow's own owner), union any body-map repo whose owner is not the org.
  • Split the failure policy: an in-org repo refusing the PATCH is an error; a personal-account repo is a warning, since the org PAT's admin scope there is unverified by design.
  • Retire the now-unused --include-self-sweeping flag, whose only consumer is the line being replaced.
  • Leave the assistants' start-new-project.md alone: it creates a visiting scientist's own repo under their own account.
Detailed implementation plan

Affected Repositories

  • PyAutoBrain (primary)

Branch Survey

Repository Current Branch Dirty?
./PyAutoBrain main clean

Suggested branch: feature/repo-settings-org-enumeration

Implementation Steps

1. .github/workflows/repo_settings.yml

  • Add ORG: ${{ github.repository_owner }} to the step env: alongside GH_TOKEN, rather than interpolating ${{ }} inline in the run: block.
  • Replace the single targets= line (currently line 84) with two lists:
    in_org=$(gh api --paginate "orgs/$ORG/repos?per_page=100" \
               --jq '.[] | select(.archived == false) | .full_name')
    outside=$(python3 bin/branch_sweep_targets.py --outside-owner "$ORG" .mind/repos.yaml)
  • Retarget the refuse-to-continue guard: an empty in_org is an error, never a clean run.
  • Failure-policy split in the loop: in-org PATCH refusal or unreadable setting keeps failed=1; out-of-org refusal emits ::warning:: only. Today every refusal sets failed=1, which would make the weekly schedule permanently red once personal-account repos are included.
  • Add a Scope/Owner column to the $GITHUB_STEP_SUMMARY table so the split is visible in the run.
  • Rewrite the header comment: the "27 repos" count and the "a repo added to the body map after today is corrected on the next run" sentence both become false. Replace with the real reason enumeration is live — a new repo is not in the body map at the moment it is created.

2. bin/branch_sweep_targets.py

  • Add --outside-owner <owner> → body-map slugs whose owner part != <owner>. Deliberately no category filter: the org enumeration has no category boundary, so its complement must not have one; filtering would yield nothing, since the only two foreign repos are categories admin and assistant.
  • Remove --include-self-sweeping and the include_self_sweeping parameter. Sole consumer verified to be repo_settings.yml:84. An unused widening flag on a boundary whose other modes delete refs is a trap.
  • Do not touch targets() or SWEEPABLE_CATEGORIES — they still serve branch_sweep_all.yml:98 and branch_archive.yml:110, where the action is deleting refs and the narrow boundary is correct.
  • Update the module docstring: drop the --include-self-sweeping paragraph, add the --outside-owner rationale.

3. tests/test_branch_sweep_targets.py

  • Delete test_include_self_sweeping_keeps_the_organs_that_sweep_themselves with the flag.
  • Repoint test_cli_flag_matches_the_function at --outside-owner.
  • Add: foreign-owner slugs yielded regardless of category; an all-in-org body map yields empty; CLI/function parity for the new flag.
  • Keep the fail-closed tests as they are (unknown flag → exit 2, missing body map → exit 1, unknown/missing category excluded).
  • Fixtures keep invented repo names — this file is .py under a framework organ and the tenant firewall scans it.

4. Stale prose

  • skills/repo_cleanup/SKILL.md:133 — "body-map-wide" becomes org-wide.
  • skills/repo_cleanup/reference.md:164 — one sentence noting the settings sweep no longer uses this module's category boundary.
  • skills/prm/reference.md:311 and skills/GITHUB_ACCESS.md:81 say only that the setting is kept on per repo — still true, unchanged.

Key Files

  • .github/workflows/repo_settings.yml — the sweep; target derivation and failure policy
  • bin/branch_sweep_targets.py — body-map derivation; gains --outside-owner, loses --include-self-sweeping
  • tests/test_branch_sweep_targets.py — the boundary's test surface
  • skills/repo_cleanup/{SKILL.md,reference.md} — prose that goes stale

Trade-offs / risks

  • Reverses part of PR#290, merged hours earlier. That PR added a test asserting the category boundary must not widen. This task does not widen it — it bypasses it for one consumer that enumerates the org instead — but the --include-self-sweeping flag it introduced is retired. The PR body must say so and why.
  • Org enumeration sweeps repos nobody classified (.github, PyAutoLogo, both -template repos, the typo repo autolens_workspace_developer-). Flipping a reversible boolean there is harmless, but it is a real widening.
  • Main risk: PAT_PYAUTOLABS may lack admin on some org repo a personal token can reach. Unknown until the workflow runs — it never has.

Testing

  • python3 bin/branch_sweep_targets.py --outside-owner PyAutoLabs ../PyAutoMind/repos.yaml → exactly Jammy2211/admin_jammy and Jammy2211/euclid_assistant.
  • python3 bin/branch_sweep_targets.py ../PyAutoMind/repos.yaml | wc -l → still 25 (measured before the change; the plain derivation must not move).
  • pytest tests/ -q → baseline measured at 542 passed (4m25s), minus the one removed test, plus the new ones.
  • bash bin/check_skill_line_counts.sh; YAML parse of the workflow; repos_sync.py --check --only "tenant firewall (organ code)".
  • Post-merge: dispatch the workflow in audit mode first (it has never run) — expect ~44 in-org + 2 out-of-org rows, all "already on", green. Only then dispatch apply.

Original Prompt

Click to expand starting prompt

The repo-settings sweep should enumerate the org, not the body map

Type: feature
Target: pyautobrain
Repos:

  • PyAutoBrain
    Difficulty: small
    Autonomy: supervised
    Priority: normal
    Status: formalised
    Filed: 2026-08-26

The original request (verbatim)

I want to make it so that when a branch merges via PR, on all repos, its auto
deleted. Can we do that here or do I need to manually go to each repo on
Github.com and do it?

and, once told PyAutoBrain#290 had already shipped most of it:

make it so new repos do inherit this, we should of also just made it so prm
doesnt try to delete anymore, I think the mobile chat pushed but chec

What is already done — do not redo it

PyAutoBrain#290 (merged 5ac5586, 2026-08-26, from a mobile session) shipped:

  • .github/workflows/repo_settings.yml — a weekly + dispatchable sweep that
    PATCHes delete_branch_on_merge=true, targets derived from the body map.
  • bin/branch_sweep_targets.py --include-self-sweeping — the widened organ
    boundary that sweep uses, plus tests/test_branch_sweep_targets.py.
  • /prm deletes no remote branch on any surface (skills/prm/prm.md:43,
    step 5.6). The second half of the request above is therefore already
    satisfied
    — do not reopen it.

Separately, all 46 live repos (44 PyAutoLabs/* + Jammy2211/admin_jammy +
Jammy2211/euclid_assistant) were PATCHed to true by hand in the filing
session and verified by re-reading the API. This task is about durability for
repos the sweep cannot currently see — not about today's state.

The hole

The sweep derives its targets from PyAutoMind/repos.yaml. Two consequences:

  1. A new repo is invisible until someone registers it. Org repos are created
    by hand on github.com — there is no gh repo create path in the organism for
    them (checked: the four assistants' start-new-project.md create a visiting
    scientist's own
    private project repo under their account; the clone
    conductor has no create call at all, clone/DESIGN.md:148 still lists repo
    creation as an open v0 question; /spawn force-syncs template repos that
    already exist). So birth-time is not a hook we have, and registration is the
    only thing standing between a new repo and the sweep.
  2. Nine live org repos are absent from the body map and so are never swept:
    .github, PyAutoLogo, PyAutoMemory-template, PyAutoMind-template,
    PyAutoProject, autolens_jax_joss, autoproject_workspace,
    autoproject_workspace_test, and the typo repo
    autolens_workspace_developer-.

A third, smaller gap: the category boundary excludes assistant (5),
pipeline (1), project (3) and admin (1), so 10 of the 37 registered
repos are skipped too — 27 of 37 swept. Those exclusions are correct for the
branch sweep, whose action is deleting refs, and PR#290 added a test
asserting they must not widen. They do not transfer to flipping a reversible
boolean on repos that demonstrably merge PRs (autolens_assistant#115,
autogalaxy_assistant#19, autocti_assistant#26 all merged this week).

Decision (human, at filing time)

Enumerate the org live. Targets become every non-archived repo under
${{ github.repository_owner }} read from the API, union any body-map repo
whose owner is not the org. This closes all three gaps at once and needs no
category boundary at all. The owner comes from the GitHub context, so no
instance fact enters organ code and the tenant firewall stays satisfied.

Do not touch the assistants' start-new-project.md. That repo belongs to
the visiting scientist, under their own account, and the skill presents it as a
backup/collaboration surface that may not use PRs. Imposing our merge policy on
it is our convention leaking into someone else's workspace.

Notes for the implementer

  • --include-self-sweeping has exactly one consumer, repo_settings.yml:84.
    Org enumeration removes that consumer, so the flag becomes dead code on a
    safety-relevant boundary — retire it with its two tests rather than leaving it
    loaded. This reverses part of a PR merged hours earlier; say so in the PR body.
  • The plain derivation still has two consumers (branch_sweep_all.yml:98,
    branch_archive.yml:110) and its narrow category boundary must not move.
  • Out-of-org repos are the user's own (Jammy2211/*); PAT_PYAUTOLABS admin
    scope there is unverified. A PATCH refusal on those must warn, not fail —
    the job currently sets failed=1 on any refusal and would go red every week.
  • Stale claims to update once the source changes: the workflow's own header
    comment ("27 repos", body-map framing), skills/repo_cleanup/SKILL.md:133
    ("body-map-wide"), and the derivation paragraph at
    skills/repo_cleanup/reference.md:164 if it describes the settings consumer.
  • The workflow has never actually run. Dispatching it after merge is the smoke
    test: with every repo already true, apply should report all-already-on.

Activity

  1. Jammy2211 commented on Aug 26, 2026

    @Jammy2211
    ContributorAuthor

    Library PR created — #292

    feature/repo-settings-org-enumeration @ dcce212, 5 files.

    Scope changed during implementation. The filed prompt's "Gap 2" (patch the gh repo create call sites so new repos inherit the setting at birth) turned out not to exist: the four assistants' start-new-project.md create a visiting scientist's own repo under their account, the clone conductor has no create call (clone/DESIGN.md:148 — still an open v0 question), and /spawn syncs template repos that already exist. There is no org repo-creation path to hook, which is why live org enumeration is the fix rather than a birth-time PATCH.

    Verification — 548 passed (baseline measured at 542; 542 − 1 removed + 7 new reconciles exactly). Plain derivation measured before and after at 25 slugs, so the branch sweep's boundary provably did not move. The workflow's run: block was extracted and control-tested against a stubbed gh across 7 scenarios, since it has never executed; live org enumeration returns 44 non-archived repos. Tenant firewall OK and proven non-vacuous with a deliberate leak probe.

    Heart was RED at ship time — release validation FAILED (integrate:fail), standing since 2026-08-23 and unrelated to this diff. Shipped on an explicit human override, not the corrective-PR exception.

    Workspace impact: none (option iii). This touches a CI workflow, a derivation script, its tests and skill prose — no PyAuto library API, so no workspace script imports anything that changed and /smoke_test has nothing to exercise here.

    Before merge: dispatch repo_settings.yml in audit mode first — it has never run.

  2. Jammy2211 commented on Aug 26, 2026

    @Jammy2211
    ContributorAuthor

    Shipped — #292 merged

    Merge commit 475d292e0, CI green on both matrix legs (pytest (3.12), pytest (3.13)) at dcce212.

    The change validated itself on its own merge: GitHub auto-deleted feature/repo-settings-org-enumeration the moment #292 landed — confirmed by git ls-remote returning nothing for that ref. That is delete_branch_on_merge doing exactly what #290 and this PR set out to make automatic, with no session credential involved.

    What shipped

    • repo_settings.yml now enumerates the organisation live (non-archived repos under the workflow's own owner), union the out-of-org complement from --outside-owner. A repo created by hand on github.com is covered the run after it exists, rather than waiting on someone to register it in repos.yaml.
    • Scope-aware failure policy: in-org refusal is an error, out-of-org a warning — without which the weekly unattended run would have gone red permanently the first time it touched a personal-account repo.
    • --include-self-sweeping retired (org enumeration removed its only consumer); a regression test asserts a stale caller fails rather than silently falling back to the narrow set.
    • targets() / SWEEPABLE_CATEGORIES untouched — the branch sweep's boundary is unchanged at 25 slugs, measured before and after.

    Still to do, and it needs a human: the workflow has never executed. Dispatch it in mode: audit first — expect 44 in-org + 2 out-of-org rows, all "already on". Only then apply. Any refused (needs admin) row is PAT_PYAUTOLABS scope, which is a decision rather than a retry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions