|
| 1 | +--- |
| 2 | +name: change-propagation |
| 3 | +description: Propagate an adapted copy of a reference file or directory across several targets — monorepo libs and/or external repos — in parallel, then file one draft PR per repo. Use when the user wants to roll a piece of boilerplate or config out to many places at once. Not for a single-target edit (just edit it) and not when the change needs bespoke per-target design rather than adaptation of a shared reference. |
| 4 | +--- |
| 5 | + |
| 6 | +# Change propagation |
| 7 | + |
| 8 | +Take a **reference** — an existing file or small directory that already |
| 9 | +embodies the thing you want everywhere — and propagate an **adapted** version |
| 10 | +of it into a set of **targets**, in parallel, then file pull requests. The |
| 11 | +targets are a mix of monorepo libs (`libs/<pkg>/`) and external GitHub repos. |
| 12 | +Adaptation is the point: each target's existing files are reconciled with the |
| 13 | +reference and the source's own identity (package name, `python_versions.json`, |
| 14 | +paths, version) is rewritten, not necessarily copied verbatim. |
| 15 | + |
| 16 | +You are the **lead**. You resolve the reference and the target list, fan out |
| 17 | +one `_propagate_target` worker per target, converge their proposals into |
| 18 | +per-repo branches, gate everything on one explicit approval, and file the |
| 19 | +PRs. |
| 20 | + |
| 21 | +## Process |
| 22 | + |
| 23 | +You **MUST** complete these in order. Track them with `TaskCreate`. |
| 24 | + |
| 25 | +### 1. Parse and confirm the input |
| 26 | + |
| 27 | +Read `$ARGUMENTS` as natural language naming a **reference source** and a |
| 28 | +**target list**. Resolve and classify, then **echo your parse back and wait |
| 29 | +for confirmation before spawning anyone** — this is a cheap gate that catches |
| 30 | +a misread reference or target before any work fans out. |
| 31 | + |
| 32 | +- **Reference source** → a concrete file set. If it's a directory, list the |
| 33 | + files you'll carry. Identify the **source package/repo** so workers know |
| 34 | + what is source-specific versus the generalizable boilerplate. |
| 35 | +- **Target list** → classify each entry: |
| 36 | + - a monorepo lib by name or path (`config-tree`, `libs/engine`) → |
| 37 | + `monorepo` substrate; |
| 38 | + - an `owner/repo` (`ihmeuw/vivarium`) → `external` substrate. |
| 39 | +- **Per-target source material.** If adapting a target needs more than the |
| 40 | + shared reference — e.g. each target's *own* pre-migration file, recovered |
| 41 | + from an archived external repo or older git history — resolve that **here**, |
| 42 | + while you (the lead) still have GitHub/network access. Workers don't; they |
| 43 | + only ever see what you put in their brief (see step 2). |
| 44 | +- **Optional MIC key.** If the user names a motivating ticket (`MIC-####`), |
| 45 | + use it for branch names and PR linkage. If not, offer to file one. |
| 46 | + |
| 47 | +### 2. Fan out one worker per target |
| 48 | + |
| 49 | +Spawn one `_propagate_target` worker per target, **in parallel**. Brief each |
| 50 | +worker with: its single `target`, the `substrate`, the `reference_files` |
| 51 | +(path + content), the `source_package` note, the `intent`, and — when the |
| 52 | +adaptation needs material specific to that target — the `target_basis` you |
| 53 | +gathered in step 1. Then: |
| 54 | + |
| 55 | +- **Monorepo workers** each run in their own **isolated git worktree** — spawn |
| 56 | + them with the Agent tool's `isolation: "worktree"`, so the worker's working |
| 57 | + directory *is* a fresh checkout, cut from the same base you'll branch the |
| 58 | + integration branch from. They adapt into `libs/<pkg>/` (or a shared root |
| 59 | + file) and verify proportionate to the change — full `make check` for code, a |
| 60 | + parse + targeted-validity check for metadata-only edits (see the worker's |
| 61 | + step 4) — leaving the result in the worktree for you to integrate directly. |
| 62 | +- **External workers** each work in a **local clone** of the target repo that |
| 63 | + you provision — it's outside the monorepo, but the worker still gets a real |
| 64 | + checkout to edit in and to run the repo's checks against when an env is |
| 65 | + available. |
| 66 | + |
| 67 | +### 3. Collect and converge |
| 68 | + |
| 69 | +Each worker returns `{target, substrate, status, proposed files, verification, |
| 70 | +conflicts, notes}`. Account for **every** target — a dead or garbled worker |
| 71 | +is reported as *not propagated*. **Don't trust a worker's self-report |
| 72 | +blindly** — sanity-check what it actually produced (the right file changed, the |
| 73 | +content is plausible and complete, nothing truncated or dropped) before you |
| 74 | +integrate it; a worker can report `adapted` while having mangled its own |
| 75 | +output. For a correctness-critical propagation, an independent verification |
| 76 | +pass per target — a second agent checking each adapted file against the intent |
| 77 | +— is worth the cost. Then converge by substrate: one PR for the monorepo |
| 78 | +(integrate every monorepo worker's worktree onto a single `vivarium-suite` |
| 79 | +branch — their subtrees are disjoint, so they union cleanly), and one PR for |
| 80 | +each external repo. For **small, uniform, disjoint** edits — one field added to |
| 81 | +each `pyproject.toml`, say — you needn't juggle N worktrees: a worker can |
| 82 | +return its adapted content or a diff and you apply them onto one branch |
| 83 | +yourself. Reserve full per-worktree integration for substantial, multi-file |
| 84 | +per-target changes. |
| 85 | + |
| 86 | +Sort targets into what you'll file versus what you'll hold: |
| 87 | + |
| 88 | +- `adapted` → goes into the PR set. |
| 89 | +- `no-op` (already matches the reference) → **no PR**; report as already |
| 90 | + current. Never open an empty PR. |
| 91 | +- `conflict` → **held by default**; surface the divergence for a human call. |
| 92 | + The user can include it (apply anyway), drop it, or resolve it manually. |
| 93 | +- `failed` / `make check` failure → **held by default**; surface it. |
| 94 | + Distinguish an *adaptation-caused* failure (the change is bad) from a |
| 95 | + *pre-existing* one (the lib was already broken — the user may want to |
| 96 | + propagate anyway). One bad target never blocks the rest. |
| 97 | + |
| 98 | +A reference may also include a **shared root file** (root `pyproject.toml` |
| 99 | +lint config, `CLAUDE.md`, a root workflow). Delegate it to its own worker like |
| 100 | +any other target; integrate its worktree onto the same `vivarium-suite` branch |
| 101 | +alongside the per-lib changes. |
| 102 | + |
| 103 | +### 4. Present the plan and gate on one bulk approval |
| 104 | + |
| 105 | +Show the consolidated plan in one place: per target, its status and proposed |
| 106 | +change (diffs are fine to render); the held set (conflicts, failures, no-ops) |
| 107 | +with reasons; and the PR set you intend to file (one `vivarium-suite` PR + |
| 108 | +one per external repo, each a **draft**). Then take **one bulk approval** to proceed. |
| 109 | + |
| 110 | +### 5. (after approval) File the PRs |
| 111 | + |
| 112 | +Hand all PR mechanics to `/viv:team-conventions` — branch naming, the PR |
| 113 | +template, draft status, and the `#vivarium_dev` review flag live there. File |
| 114 | +one `vivarium-suite` PR for the integrated monorepo branch and one PR per |
| 115 | +external repo. |
| 116 | + |
| 117 | +### 6. Report |
| 118 | + |
| 119 | +List, per target: the filed PR (key/URL) or the reason it was held (conflict, |
| 120 | +failure, no-op, worker death). Confirm every target from step 1 is accounted |
| 121 | +for. Stop — committing further, merging, and un-drafting are the user's call. |
0 commit comments