Skip to content

feat(clone): a sync mode, and the shared rename table birth and sync now both use - #316

Merged
Jammy2211 merged 2 commits into
mainfrom
feature/science-project-memory
Aug 29, 2026
Merged

feat(clone): a sync mode, and the shared rename table birth and sync now both use#316
Jammy2211 merged 2 commits into
mainfrom
feature/science-project-memory

Conversation

@Jammy2211

Copy link
Copy Markdown
Contributor

Summary

A fresh agent chat pointed at a science project folder should work out where the
project got to, how to resume, and what its history is — by default. This is the
organ-side half of that: a sync mode on the clone conductor, so the change
lands in the sibling assistants from the reference's own diff rather than as four
hand edits.

clone sync takes the reference assistant's diff over a commit range, restricts
it to that reference's generic pattern set (reusing REFERENCE_PROFILES /
match_any — not a second list), rewrites the names for the target, and applies
it with GNU patch, reporting per file applied / created / already-applied
/ rejected (hunks #n) / absent / skipped / unsupported. Dry run by
default; --apply writes and leaves .rej files; exit 1 when anything was
rejected. "Since the sibling's last sync" is read from the sibling's own history
via a Clone-sync: <reference>@<sha> commit trailer — no state file.

The second commit closes the seam that made birth and sync disagree: birth and
sync each built their own rename list, and birth's was missing two rules.
name_substitutions() is now the one table both use, carrying the UPPERCASE
package rule birth omitted plus DOMAIN_NOUNS / DOMAIN_ALIASES (the science's
own noun, word-anchored so microlensing survives and lensing-fluent keeps its
tail). A target package absent from the table gets no domain rule and a
printed warning — a science's own noun is never guessed.

Commits on this branch:

API Changes

New capability, nothing removed or renamed: bin/pyauto-brain clone sync is a
new CLI mode, and the clone conductor gains a shared name_substitutions()
rename table that both birth (apply_seed) and sync now read instead of each
building its own. Existing clone behaviour is unchanged apart from birth
picking up the two rules it was missing. See full details below.

Test Plan

  • pytest tests/ -q634 passed (627 before this branch's second commit; tests/test_clone_sync.py 9 → 16 tests)
  • Tenant firewall check OK (its allowlist entry for the new test file is already on PyAutoMind main, 2b764e48)
  • Exercised end to end on live siblings: clone sync --since ee306ac --target autofit_assistant --apply reproduced its dry run exactly (1 applied · 2 created · 4 rejected)
  • Dry run writes nothing; rejected hunks are reported, never auto-resolved
Full API Changes (for automation & release notes)

Added

  • bin/pyauto-brain clone sync [--since <rev>] [--target <assistant>] [--apply] — replay the reference assistant's generic-file diff onto a born sibling as a reviewable three-way patch; dry run by default, exit 1 on any rejected hunk.
  • agents/conductors/clone/_clone.py::name_substitutions() — the single source of the birth/sync rename table (package names incl. UPPERCASE env-var form, DOMAIN_NOUNS, DOMAIN_ALIASES).
  • agents/conductors/clone/_clone.py::sync_substitutions() — sync's view of that table.
  • Clone-sync: <reference>@<sha> commit trailer — how a sibling records the point it last synced from.

Changed Behaviour

  • apply_seed now serialises name_substitutions() for PyAutoHands' clone_seed instead of building its own list, so a birth picks up the UPPERCASE package rule and the domain-noun rules it previously omitted.
  • A target package absent from the domain table gets no domain rule plus a printed warning, rather than a guessed noun.

Migration

  • None. Existing clone invocations are unaffected; sync is additive.

Closes #315.

Generated by the PyAutoLabs agent workflow.

Jammy2211 and others added 2 commits August 28, 2026 18:44
…onto the born siblings

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. The conductor
listed those files as _SHARED_GENERIC but had only a birth mode.

`clone sync` closes that loop for the generic set, and ONLY that set:

- it takes the REFERENCE's own diff over a commit range, restricted to that
  reference's `generic` patterns (reusing REFERENCE_PROFILES/match_any, not a
  second list), rewrites the names in it for each sibling, and applies it with
  GNU patch;
- it is not an overwrite. A hunk the sibling has adapted away is REJECTED and
  reported by number — on --apply it lands as a .rej for a human. A sibling's
  domain adaptation outranks the reference's prose, and a conflict is a human
  decision, never a silent resolution;
- per file it reports applied / created / already-applied / rejected / absent /
  skipped / unsupported, so the report says what a human must look at;
- dry run is the default; --apply writes; exit 1 means something was rejected.

"Since the sibling's last sync" is read from the sibling's own history via a
`Clone-sync: <reference>@<sha>` commit trailer — no state file, and the pointer
travels with the commit that consumed the patch. A first sync needs an explicit
--since rather than a guessed range.

The substitutions add the UPPERCASE rule birth omits, which is why a sibling
born from autolens_assistant still names $AUTOLENS_ASSISTANT in its generated
project scaffold; synced lines are right even where the old ones are not.

tests/test_clone_sync.py: 9 hermetic tests over a temp workspace of three fake
assistants, one of which has adapted the prose the reference changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZceRRca9F5ojAtjpRp3me
Birth and sync each built their own rename list, and birth's was missing two
rules. Both gaps were still visible in a sibling cell: its generated project
scaffold pointed at the REFERENCE's `$..._ASSISTANT` variable, and its profile
template still headed its first section with the reference's domain noun
("Lensing background", in a CTI cell). Neither is reachable by the identity
rules — the uppercase form is not the package, and a domain noun is not a
package at all.

- `name_substitutions()` is now the ONE table, used by `apply_seed` (which
  serialises it for PyAutoHands' `clone_seed`) and by `sync_substitutions`. A
  file that lands in a sibling reads the same whichever route brought it.
- It carries the UPPERCASE package rule (birth omitted it) and, new,
  `DOMAIN_NOUNS`: the science's own noun and its longer phrase, per package.
- `DOMAIN_ALIASES` handles the qualified compounds first, because
  "strong-lensing" -> "strong-CTI" is a phrase in no science; the qualifier
  belongs to the reference's domain, so the compound resolves to the target's
  bare noun. Word-anchored throughout, so `microlensing` survives and
  `lensing-fluent` keeps its own tail.
- A target package absent from `DOMAIN_NOUNS` gets NO domain rule and a printed
  warning at birth. A science's own noun is never guessed.

Sync only ever touches the lines in the patch, so these rules correct newly
synced lines; they do not retro-fix a line an earlier birth got wrong — one of
the new tests pins exactly that, so nobody expects a silent rewrite.

tests/test_clone_sync.py: 9 -> 16 (domain-noun rename, the compound rule, the
word anchor, the never-guess rule, birth/sync table identity, an end-to-end
sync that lands both closed, and the no-retro-fix pin).

Checks: `pytest tests/ -q` 634 passed (627 before); tenant firewall OK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZceRRca9F5ojAtjpRp3me
@Jammy2211
Jammy2211 merged commit 8f5f769 into main Aug 29, 2026
2 checks passed
@Jammy2211
Jammy2211 deleted the feature/science-project-memory branch August 29, 2026 01:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: science-project memory — a fresh chat resumes a project by default

1 participant