Skip to content

feat(mind): auto-merge ledger branches, so Mind's own work stops stranding - #358

Merged
Jammy2211 merged 1 commit into
mainfrom
claude/pyautomind-pr-automation-lcu0s5
Aug 27, 2026
Merged

feat(mind): auto-merge ledger branches, so Mind's own work stops stranding#358
Jammy2211 merged 1 commit into
mainfrom
claude/pyautomind-pr-automation-lcu0s5

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

The seam

PyAutoMind's own work strands on claude/**. prompt_sync.sh pushes HEAD deliberately — a cloud session must not push straight to main — but nothing downstream moves the branch on: no workflow so much as looks at a claude/** push, because lifecycle_drift, dashboard_refresh, firewall_gate and spawn_drift all trigger on push: main or pull_request only.

So a filed prompt, a task moved to complete/, a regenerated dashboard all wait for a human to write an explicit "merge that branch" prompt, and the dashboard renders a stale backlog until they do.

The last twelve merges into main say what is actually stranding: draft/, active/, complete/, the root registry files, dashboard.{md,html}. It is the organism's own bookkeeping, its drift checks are already automated, and review adds nothing to a moved prompt file. Review adds everything to a changed script — so draw that line and merge only the ledger side of it.

What lands

scripts/ledger_merge.py — the gate, as a script rather than workflow YAML so it is testable and a session can predict the verdict before it pushes. Default deny: an unclassified path (a new root file, a new top-level folder) is code.

Ledger — merged automatically Code — always a human
draft/**, active/**, complete/** scripts/, tests/, .github/, skills/, policy/, docs/
active.md, planned.md, parked.md, condemned.md, epics.md, ideas.md, autonomy_log.md repos.yaml, README.md, AGENTS.md, REFERENCE.md, …
dashboard.md, dashboard.html anything unclassified

Two exceptions inside the ledger dirs: a dot-path anywhere, and a file pytest would collect (conftest.py, test_*.py, *_test.py) — inert prompt assets like draft/bug/autofit/*_assets/run_once.py ride along, a file CI would execute does not.

.github/workflows/mind_ledger_merge.yml — on push to claude/**:

  1. Classify the diff against main.
  2. lifecycle.py check blocks — structural drift (a prompt in active/ with no active.md entry) heals nowhere, and merging it would put the contradiction on main. Stale renders do not block: complete/index.md, the registry contents blocks and the dashboard all self-heal on main.
  3. Merge. An open PR is merged through the PR so it records as MERGED; a branch without one gets a direct merge commit, retried on the current tip of main (the same convergence the self-heal loops use). A conflict stops the run and leaves the branch alone.
  4. Delete the branch, on the proof branch_sweep.yml uses — main must actually contain the head sha, so a failed or partial merge never loses a branch. A PR merge usually self-deletes; this covers the direct path, which is what let 188 provably-spent branches pile up.
  5. Dispatch dashboard_refresh.yml + lifecycle_drift.yml on main — a GITHUB_TOKEN push triggers no workflows, so the heals must be asked for explicitly (the lesson dashboard_refresh.yml already carries from 2026-08-21).

workflow_dispatch runs the same gate in audit mode by default, so a manual look never merges by accident.

Not a review bypass

Review adds nothing to a moved prompt file; it adds everything to a changed script. The gate encodes precisely that distinction and nothing else. The workflow and the gate script are both on the code side of their own line — neither can auto-merge a change to itself, and this PR classifies as code, which is why it is a PR.

Validation

267 tests (-n auto), ruff clean, lifecycle check / registry_toc --check / repos_sync --check clean, spawn.py rule matches.

11 new tests, and they are the refusals: default deny, traversal behind a ledger prefix, dot-paths, pytest-collectable files, and every code home the repo actually has. One caught a fail-open in the first draft — a stdin of "\n" is one empty string, which is a truthy list, and was read as "0 ledger paths, exit 0".

spawn.py DROPs the workflow: it merges to main and checks out a sibling Brain for the dashboard render — dashboard_refresh.yml's rule-9c reasoning, twice over. An adopter re-adds it once they have a Brain and have decided which of their own paths are ledger.

Noted, not touched

Two pre-existing drifts on main, unrelated to this diff: spawn.py reports .claude/hooks/session-start.sh and .claude/settings.json UNMATCHED (so the weekly spawn_drift cron is failing), and repos_sync.py --check flags PyAutoMemory's session-start hook as differing from policy/session_start_hook.sh.


Generated by Claude Code

…nding

PyAutoMind's own work strands on `claude/**`. `prompt_sync.sh` pushes HEAD
deliberately — a cloud session must not push straight to main — but nothing
downstream moves the branch on: no workflow so much as *looks* at a `claude/**`
push, because lifecycle_drift, dashboard_refresh, firewall_gate and spawn_drift
all trigger on `push: main` or `pull_request` only. So a filed prompt, a task
moved to `complete/`, a regenerated dashboard all wait for a human to write an
explicit "merge that branch" prompt, and the dashboard renders a stale backlog
until they do.

The last twelve merges into main say what is actually stranding: `draft/`,
`active/`, `complete/`, the root registry files, `dashboard.{md,html}`. It is
the organism's own bookkeeping, its drift checks are already automated, and
review adds nothing to a moved prompt file. Review adds everything to a changed
script — so draw that line and merge only the ledger side of it.

- scripts/ledger_merge.py — the gate, as a script rather than workflow YAML so
  it is testable and a session can predict the verdict before pushing. DEFAULT
  DENY: an unclassified path (a new root file, a new top-level folder) is code.
  Two exceptions inside the ledger dirs: a dot-path anywhere, and a file pytest
  would collect — inert prompt assets ride along, a file CI would execute does
  not.
- .github/workflows/mind_ledger_merge.yml — on push to `claude/**`: classify,
  block on `lifecycle.py check` (structural drift heals nowhere), merge, delete
  the branch, then dispatch the self-healing legs on main (a GITHUB_TOKEN push
  triggers no workflows, so stale renders must be asked for). An open PR is
  merged THROUGH the PR so it records as MERGED; a branch without one gets a
  direct merge commit and is deleted on the proof branch_sweep.yml uses — main
  must actually contain the head sha. Manual dispatch defaults to audit.
- The workflow and the gate are both on the code side of their own line, so
  neither can auto-merge a change to itself. This branch classifies as code.
- Tests are the refusals: default deny, traversal, dot-paths, collectable
  files, every code home the repo has. One caught a fail-open — a stdin of
  "\n" is one empty string, a truthy list, and read as "0 ledger paths, exit 0".
- spawn.py DROPs the workflow (it merges to main and checks out a sibling
  Brain: dashboard_refresh.yml's rule-9c reasoning, twice over).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmGdNA97ajAyLJ4jemvki8
@Jammy2211
Jammy2211 merged commit f43db19 into main Aug 27, 2026
3 checks passed
@Jammy2211
Jammy2211 deleted the claude/pyautomind-pr-automation-lcu0s5 branch August 27, 2026 19:11
Jammy2211 pushed a commit that referenced this pull request Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants