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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ the `/<verb>` slash commands.
| `eyes` | The perceptive function — the organism's sense of its own appearance: survey/review a visualization workspace's figure surface, critiques route to intake/start_dev — EyesSurvey/EyesReviewSurface (never renders, never edits) | `bin/pyauto-brain eyes` |
| `profiling` | The proprioceptive function — the organism's sense of its own effort: campaign/ingest/triage plans over the autolens_profiling workspace — ProfilingDecision | `bin/pyauto-brain profiling` |
| `hygiene` | The maintenance function — the organism's sense of its own upkeep: code-quality debt (dev-loop cost + tidiness), delegating fixes — HygieneDecision | `bin/pyauto-brain hygiene` |
| `clone` | The Mitosis Agent: partition the reference assistant, analyze the domain, emit the CloneDecision; --apply --mode lightweight-seed delegates the seed birth to Build | `bin/pyauto-brain clone` |
| `clone` | The Mitosis Agent: partition the reference assistant, analyze the domain, emit the CloneDecision; --apply --mode lightweight-seed delegates the seed birth to Build; 'clone sync' patches the reference's generic-file diff onto the born siblings (dry run by default; rejected hunks are a human decision) | `bin/pyauto-brain clone` |
| `build` | Coordinate execution: consult the vitals faculty, then delegate to PyAutoHands | `bin/pyauto-brain build` |
| `release` | Release door → the Build Agent release mode (single gate); 'release rehearse'/'release validate' drive release validation; 'release nightly' is the scheduled-nightly driver | `bin/pyauto-brain release` |
| `health` | The organism's clinician: run the health loop with a human, dispatch by dispatch, toward green | `bin/pyauto-brain health` |
Expand Down
61 changes: 60 additions & 1 deletion agents/conductors/clone/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ bin/pyauto-brain clone <library> --workspace <repo> [--reference <repo>] [--json
# give birth (the mode is the human's answer to the clone-mode question):
bin/pyauto-brain clone <library> --workspace <repo> --apply --mode lightweight-seed
# ... add --no-push to build the seed tree in scratch without creating a repo
# keep the already-born siblings from drifting (dry run by default):
bin/pyauto-brain clone sync [--reference <repo>] [--target <repo>]... \
[--since <rev>] [--until <rev>] [--apply] [--json]
```

What analyze does: domain analysis (library public API via `ast`, workspace
Expand All @@ -39,13 +42,69 @@ instead of `main`, so paired PRs can both be green before the ordered merge
(PyAutoBrain#186); new cells inherit this from the reference's
`clone-boundary.yml`.

## `sync` — the second mode (drift, not birth)

Birth copies the reference **once**. Nothing re-synced afterwards, so the four
copies of `skills/start-new-project.md` and `wiki/project/*` grew four distinct
hashes and the `autofit_assistant` copy diverged by ~343 lines. `sync` closes
that loop for the files the boundary already calls **generic** — and only
those; a domain file the reference changed never crosses.

**It is not an overwrite.** It takes the *reference's own diff* over a commit
range, restricted to that reference's `generic` pattern set, rewrites the names
in it for the target (`name_substitutions` — the one rename table birth and
sync share), and applies it with GNU `patch`. Per file, per sibling it reports
one of:

| result | meaning |
|--------|---------|
| `applied` / `created` | the hunks fit; on `--apply` the file is written |
| `already-applied` | the sibling already carries the change |
| `rejected` | one or more hunks no longer fit — **listed by number**; on `--apply` they land as a `.rej` file |
| `absent` | the reference changed a file this sibling does not have |
| `skipped` | the reference *added* a file the sibling already has — compare by hand |
| `unsupported` | a rename/delete in the reference — do it by hand |

### The rename table (`name_substitutions`)

One table, used by **both** birth and sync, so a generic file reads the same
whichever route brought it into a sibling. Most specific first: the assistant
name, the `al_` → `ac_` skill prefix (word-anchored), the library repo name,
the **UPPERCASE** package form, the lowercase package, then the **domain noun**
(`DOMAIN_NOUNS`: lensing / galaxy / CTI / model-fitting, each with its longer
phrase, word-anchored so `microlensing` survives).

The last two rules were missing at the first births, and both are still visible
in `autocti_assistant` (PyAutoBrain#315): its generated project scaffold points
at `$AUTOLENS_ASSISTANT`, and its profile template heads its first section
"Lensing background". Neither is reachable by the identity rules — `AUTOLENS`
is not `autolens`, and "lensing" is not a package at all. A target whose
package is not in `DOMAIN_NOUNS` gets **no** domain rule and a printed warning;
a science's own noun is never guessed.

Apply is **per hunk**, the way `patch` has always worked: what fits lands, what
does not is written out for a human. A sibling's domain adaptation outranks the
reference's prose, and a conflict is never resolved silently — that judgement
is the human's, which is the whole reason this mode is a patch and not a copy.

**Dry run is the default.** `--apply` writes. Exit `1` means at least one file
had rejected hunks (in either mode), so a caller can gate on it.

**"Since the sibling's last sync"** is read from the sibling's *own* history:
a sync commit carries the trailer `Clone-sync: <reference>@<sha>`, and the next
run diffs the reference from there. No state file, and the pointer travels with
the commit that consumed the patch. The first sync of a sibling therefore needs
an explicit `--since <rev>`; without one the run reports that and exits 1
rather than guessing a range.

Hard rules (from DESIGN.md): never writes repos/files/GitHub state itself
(birth is Build's — this agent hands over a plan); never copies domain content
across domains; never modifies the reference; never embeds PyAutoMemory content
in a public assistant; a newborn is born private and is not flipped public or
announced before its Heart validation legs pass
(`PyAutoHeart/docs/newborn_validation.md`).

Exit codes: `0` decision · `4` inputs unresolvable (incl. an unclassified
Exit codes: `0` decision / clean sync · `1` a sync completed with rejected
hunks · `4` inputs unresolvable (incl. an unclassified
boundary, or Build's birth failing) · `5` bad usage (`--apply` without
`--mode lightweight-seed` — the v2 modes are refused here).
Loading
Loading