---
title: "Meta repository e agenti AI: mappa del lavoro o versioni fissate?"
source: "https://www.matteovincenti.com/blog/meta-repository-agenti-ai"
language: it
author: "Matteo Vincenti"
published: 2026-10-21
updated: 2026-10-23
category: "AI"
tags: ["Metarepository", "Git", "Agenti AI", "Context engineering"]
---

# Meta repository e agenti AI: mappa del lavoro o versioni fissate?

Quando basta una mappa dei progetti e quando servono submodule: setup completo, manifest, istruzioni per agenti e un esempio Git eseguibile.

![Quattro archivi separati, ciascuno con le proprie schede, collegati da una struttura comune: repository indipendenti coordinati dal metarepo.](https://cdn.sanity.io/images/xrqkwry7/production/32978c5b4b495b21308be7acfc4b0ac9c44d7c30-1734x907.png?w=1400&fit=max&auto=format)

Categoria: AI · Tag: Metarepository, Git, Agenti AI, Context engineering

Il backend, l’applicazione web, il sito pubblico e l’infrastruttura del prodotto su cui lavoro vivono in quattro repository. Volevo conservarli separati: hanno dipendenze, istruzioni e cicli di rilascio diversi. Mi serviva però un posto dal quale capire come stavano insieme, anche quando affidavo una modifica a un agente AI.

Un **meta repository**, o metarepo, raccoglie il coordinamento di più repository: documentazione comune, configurazione del workspace, istruzioni e collegamenti ai progetti. Il codice applicativo rimane nei componenti, con storie e remoti indipendenti. Il pattern precede gli agenti AI: [Burke Libbey lo descriveva già nel 2017](https://notes.burke.libbey.me/metarepo/), proponendo una gerarchia dei progetti e il coordinamento delle loro revisioni.

## Serve una mappa o una combinazione di versioni?

Prima del setup c’è una scelta che cambia il lavoro di manutenzione.

Se serve **orientarsi fra i progetti**, il coordinamento può partire da un README con repository, responsabilità e istruzioni da leggere. Per esempio: l’API definisce il contratto degli ordini, l’applicazione lo consuma, l’infrastruttura descrive dove eseguirli. Le posizioni dei checkout stanno in configurazione; ogni progetto continua a scegliere le proprie versioni. È un ingresso comune al lavoro, sufficiente quando la composizione del prodotto è già gestita dai rilasci di servizi o pacchetti.

Se serve **ritrovare la stessa coppia di sorgenti**, bisogna conservare anche le revisioni selezionate. Con i submodule, Git colloca i componenti in sottodirectory del repository padre e registra per ciascuno un riferimento a un commit, chiamato gitlink. Lo SHA identifica quel commit. Un clone ricorsivo può così recuperare l’API e il client scelti, anziché i rispettivi branch nel momento in cui qualcuno li scarica.

Ho scelto questa seconda strada. Il costo è diventato evidente lavorandoci: un commit nel componente, poi un altro nel metarepo per aggiornarne il riferimento. Il codice era già cambiato; chi clonava il padre riceveva ancora la versione precedente. Da qui la domanda che mi interessa oggi: **quali combinazioni vale la pena conservare insieme, e chi se ne occupa?**

Un riferimento fisso è utile per ricostruire una regressione o una coppia verificata. Se il padre deve inseguire ogni commit solo per tenerci nello stesso workspace, abbiamo aggiunto una responsabilità di versionamento al bisogno iniziale di orientarci. Il setup seguente serve a chi vuole conservare quelle revisioni; una mappa dei progetti può fermarsi prima.

In un monorepo i sorgenti appartengono alla stessa storia Git e una modifica può cambiare API e client nello stesso commit. Nei submodule le storie restano separate: il commit del padre registra la composizione. È questa la differenza da valutare prima di copiare la struttura.

Per vedere il meccanismo in azione c’è un [esempio Git eseguibile](https://cdn.sanity.io/files/xrqkwry7/production/759e93277c44183a99d8125ea19ae5edb43d5b12.mjs?dl=metarepo-pin-flow.mjs). Due piccoli moduli JavaScript mostrano una coppia incompatibile, un rebase che cambia lo SHA, il rifiuto di un riferimento non pubblicato e un clone finale della coppia registrata. Scarica il file e leggilo prima di eseguirlo. Servono Node.js 22 e Git 2.28 o successivo, su macOS o Linux:

```bash
node metarepo-pin-flow.mjs
```

La fixture crea repository temporanei e remoti locali, poi li rimuove. Il controllo riguarda i due moduli; il setup che segue ricostruisce i sorgenti, mentre dipendenze, servizi e ambiente restano descritti nei componenti.

## Le alternative che avevo considerato

Nella decisione iniziale avevo confrontato queste possibilità:

_Le alternative che avevo considerato_

| Alternativa valutata | Che cosa avrei ottenuto | Perché ho scelto diversamente |
| --- | --- | --- |
| Monorepo | Codice dei componenti nella stessa storia Git, con commit che possono cambiarli insieme. | I progetti esistevano già separati. Volevo mantenerne i confini di accesso e i cicli di rilascio, senza riunire le storie. |
| Repository separati, senza metarepo | Ogni progetto continua per conto proprio, senza un altro repository da aggiornare. | Mi mancava un ingresso comune per documenti e agenti; avrei dovuto decidere altrove dove conservare le combinazioni di versioni. |
| Strumenti a manifest: [meta](https://github.com/mateodelnorte/meta) e [Repo](https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md) | Una mappa dei repository e comandi per gestirli insieme; Repo può specificare anche le revisioni. | Avrei aggiunto un programma da installare per chi lavora sul progetto, agenti e CI compresi. Per iniziare mi bastava Git. |
| [git-meta](https://github.com/twosigma/git-meta/blob/master/doc/user-guide.md), di Two Sigma | Operazioni sopra i submodule per lavorare con un’interfaccia simile a quella di un monorepo. | Per la dimensione del workspace non avevo bisogno di quel livello aggiuntivo. |

Ho scelto i submodule perché conservavano l’indipendenza dei repository usando Git già disponibile. Per il mio punto di partenza il costo di un altro strumento non era giustificato.

## Setup con submodule: collegare due repository esistenti

Per seguire il setup servono Git 2.28 o successivo, con nome ed email già configurati, e una shell Bash o Zsh. Parti da due repository remoti, API e web, ciascuno con almeno un commit pubblicato sul branch predefinito. Crea anche un terzo repository remoto vuoto per il coordinamento, scegliendo `main` come branch predefinito nell’hosting. È il branch che creeremo nel padre. Il tuo account deve poter clonare i componenti e pubblicare sul padre.

Sostituisci i tre segnaposto con i rispettivi URL. Esegui da una directory nella quale non esiste ancora `coordination`:

```bash
API_URL='<API_REPOSITORY_URL>'
WEB_URL='<WEB_REPOSITORY_URL>'
META_URL='<META_REPOSITORY_URL>'

mkdir coordination
cd coordination
git init --initial-branch=main
git remote add origin "$META_URL"
git submodule add --name api "$API_URL" components/api
git submodule add --name web "$WEB_URL" components/web
```

Git ha clonato i componenti, creato `.gitmodules` con nomi, URL e percorsi e preparato nell’indice del padre i due gitlink da salvare con il primo commit.

Ora creiamo i file di coordinamento. Al termine avremo questa struttura:

```txt
coordination/
├── .gitmodules
├── workspace.manifest.json
├── AGENTS.md
├── CLAUDE.md
├── tasks/
│   └── orders-pagination.md
└── components/
    ├── api/                    # independent Git repository
    └── web/                    # independent Git repository
```

Crea `workspace.manifest.json` nella root di `coordination`, con questo contenuto:

```json
{
  "schemaVersion": 1,
  "projects": [
    {
      "id": "api",
      "submodule": "api",
      "role": "HTTP API and request contracts",
      "dependsOn": []
    },
    {
      "id": "web",
      "submodule": "web",
      "role": "Web application and API consumer",
      "dependsOn": [
        "api"
      ]
    }
  ]
}
```

Questo JSON è il formato dell’esempio: lo leggono la persona o l’agente che ricevono il task. Descrive ruoli e dipendenze; non installa né avvia i progetti. Il campo `submodule` richiama il nome che abbiamo assegnato con `--name`. Il percorso resta in `.gitmodules` e si legge così:

```bash
git config -f .gitmodules --get submodule.api.path
```

L’ultimo comando legge il percorso associato al nome `api`. È il collegamento che deve fare anche chi legge il manifest. Nel setup mostrato, spostare il submodule con `git mv` aggiorna il percorso in `.gitmodules`; il JSON conserva il nome.

`dependsOn` rende esplicito che il client usa l’API. Lo script o la persona che legge il manifest deve poi scegliere i controlli pertinenti: quella relazione non descrive da sola tutto il contratto tra i due.

Gli SHA fissati rimangono nei gitlink. Ripeterli a mano nel JSON richiederebbe di aggiornarli in due posti. Nel risultato di un test registreremo invece quali SHA sono stati effettivamente provati.

## Le regole comuni avevano iniziato a divergere

Nel mio workspace erano comparse copie diverse delle istruzioni condivise. Alcune procedure generiche contenevano ancora la sigla del team o assumevano un percorso dei progetti, anche se quel percorso era già dichiarato nel manifest. In un componente le regole locali erano finite nella sezione che avrebbe dovuto essere comune.

Correggere una regola significa anche capire quali copie aggiornare e quali differenze sono intenzionali. Il tooling del workspace ha una sorgente condivisa e una modalità di confronto senza scrittura. Prima di distribuire un cambiamento, voglio poter vedere quelle differenze.

Per il setup crea `AGENTS.md` nella root del padre, con questo ingresso comune:

```markdown
# Cross-repository work

Read workspace.manifest.json and the selected task before editing.
For submodules, resolve each manifest submodule name to its path in .gitmodules.
Use project IDs in instructions.
Before changing a component, read its applicable agent instructions and build configuration.
Keep component commands and runtime requirements with that component.
Report conflicting project rules before choosing how to proceed.

Record the exact revisions and the checks actually run in the task.
Leave unexecuted checks pending. Do not reset or clean another task's checkout.
```

Crea anche `CLAUDE.md` nella stessa root, con la sola riga `@AGENTS.md`. È un modo per condividere l’ingresso con Claude Code, come documentato da [Anthropic](https://code.claude.com/docs/en/memory#share-one-file-with-other-coding-tools). Le versioni correnti supportano anche `AGENTS.md` direttamente, secondo le condizioni indicate nella stessa pagina.

Per Codex conta la [catena di istruzioni dalla root del progetto alla directory di lavoro](https://learn.chatgpt.com/docs/agent-configuration/agents-md). Avviare una sessione in un checkout indipendente può quindi escludere l’ingresso del coordinamento. Nella consegna del task specificherei il meta repository da leggere e i componenti interessati; prima di modificare i file, chiederei all’agente di indicare le istruzioni applicabili che ha trovato.

Se distribuisci copie delle regole, il confronto deve distinguere la parte comune dagli adattamenti locali. Controlla anche le rimozioni: una regola tolta dalla sorgente può sopravvivere in una vecchia copia. Questo merita una review, soprattutto quando riguarda permessi o comandi.

## Un task che si possa riprendere

Consideriamo una modifica alla ricerca degli ordini: l’interfaccia deve caricare una pagina alla volta. L’API attuale restituisce un array; la nuova risposta contiene `items` e `nextCursor`. Cambiare la forma della risposta sullo stesso endpoint romperebbe il client precedente.

Crea la directory `tasks` e il file `tasks/orders-pagination.md`. Questo task di esempio conserva la decisione che evita quell’errore e le informazioni per riprendere il lavoro:

```markdown
# Orders pagination

Projects: api, web
Status: planned

Contract:
- GET /orders keeps returning the existing array.
- GET /v2/orders returns {items, nextCursor}.
- The web application moves to /v2/orders after that API is available.

Checks:
- Old client against updated API: pending.
- Updated client against the new endpoint: pending.
- Last page and invalid cursor: pending.

For each executed check, record:
- API and web commit SHAs, with source files matching those commits.
- Exact command, result and retained output location.

Next action: implement the compatible API endpoint, then update the client.
Release order: API first, web second.
Integration owner: assign before starting.
```

Per un’attività piccola questo può essere un solo file. Separerei piano, stato e handoff quando hanno davvero destinatari o tempi di aggiornamento diversi. Una decisione destinata a durare oltre il task può diventare un ADR, un Architecture Decision Record: il task la richiama, senza copiarne la motivazione a ogni ripartenza.

La [ricerca Anthropic sugli agenti che lavorano su più sessioni](https://www.anthropic.com/research/effective-harnesses-for-long-running-agents) usa artefatti di avanzamento e storia Git per aiutare la sessione successiva a orientarsi. Nel metarepo aggiungerei una domanda: quali revisioni dei componenti riguardano questi risultati? Se uno SHA cambia, il vecchio esito rimane riferito alla combinazione precedente.

È lo stesso problema affrontato nell’articolo sulla [verifica del lavoro degli agenti](https://www.matteovincenti.com/blog/allucinazioni-ai-verificare-agenti): uno stato «completato» deve avere un riscontro. Qui il riscontro riguarda anche il confine tra repository.

## Salvare il primo workspace e riclonarlo

A questo punto nella root devono esserci il manifest, i due file di istruzioni e il task appena creati. Sempre dalla root di `coordination`, salva anche la configurazione scritta da Git e i riferimenti ai componenti:

```bash
git add -- .gitmodules components/api components/web \
  workspace.manifest.json AGENTS.md CLAUDE.md tasks/orders-pagination.md
git diff --cached --submodule=log
git commit -m "Set up coordinated API and web workspace"
git push --recurse-submodules=check -u origin main
```

Ora fai la prova da una directory esterna al checkout, usando per `META_URL` lo stesso remoto del padre:

```bash
META_URL='<META_REPOSITORY_URL>'
git clone --recurse-submodules "$META_URL" coordination-check
git -C coordination-check submodule status
git -C coordination-check status --short
```

`submodule status` deve mostrare una riga per API e una per web. Nei due componenti appena ricostruiti non devono comparire i prefissi `-` (non inizializzato), `+` (commit diverso da quello nell’indice del padre) o `U` (conflitto). `status --short` del padre deve restare vuoto.

Se hai clonato senza `--recurse-submodules`, puoi completare quel clone con:

```bash
git -C coordination-check submodule update --init --recursive
```

È il flusso spiegato in [Pro Git](https://git-scm.com/book/en/v2/Git-Tools-Submodules). Hai ricostruito i sorgenti e i documenti, non ancora installato le dipendenze o avviato i servizi. Questi passaggi restano nei README dei componenti: il padre non introduce un’installazione comune per stack diversi.

Per il primo task con un agente, apri il clone del coordinamento e dagli una consegna esplicita:

```txt
Read AGENTS.md in this coordination repository, then workspace.manifest.json
and tasks/orders-pagination.md. Resolve api and web through .gitmodules.
Read each component's applicable instructions and build configuration.
Before editing, explain the contract, current revisions and planned files.
Work in the relevant component, then record checks and the next action in the task.
```

Il manifest gli dice quali progetti esistono; le istruzioni locali spiegano come lavorarci. Da qui l’agente può proporre la modifica nel repository giusto, invece di indovinare comandi o percorsi.

## Perché il riferimento rimane indietro

Anche impostando `branch = main` in `.gitmodules`, il riferimento resta fissato finché non lo aggiorni. Quella configurazione indica il branch per operazioni come `update --remote`, come spiega il [manuale dei submodule](https://git-scm.com/docs/git-submodule).

Puoi vedere la differenza dalla root del meta repository. In questo esempio l’API si trova in `components/api`:

```bash
git ls-tree HEAD -- components/api
git -C components/api rev-parse HEAD
```

Il primo comando legge il riferimento nell’ultimo commit del padre; il secondo legge il commit attualmente aperto nel componente. Se hai appena committato nell’API, possono essere diversi. Con `git diff --submodule=log` esamini i commit che separano il checkout del componente dal riferimento nell’indice del padre.

Per chi sta sviluppando questa differenza può essere del tutto intenzionale. Per chi deve riprodurre un errore, invece, sapere quale delle due revisioni è stata provata diventa essenziale. Il nome del branch da solo non basta: domani potrebbe indicare un altro commit.

## Quando aggiornerei i riferimenti

Per una modifica che coinvolge API e interfaccia, raggrupperei l’aggiornamento dei pin dopo i merge dei componenti. I commit intermedi resterebbero nei rispettivi repository; il padre registrerebbe la combinazione finale che abbiamo verificato.

Questo criterio cambia se la CI del padre deve controllare ogni singolo merge. In quel caso aggiornare spesso i pin ha uno scopo preciso: far emergere subito una possibile incompatibilità. Raggruppare tutto alla fine la farebbe scoprire più tardi.

## Registrare la composizione dopo i merge

Il commit del padre va preparato con gli SHA risultanti dai merge, disponibili nei remoti dei componenti. Una PR unita con rebase può avere identificativi diversi da quelli del branch su cui hai lavorato.

Per preparare la composizione userei un clone di integrazione separato. Parti da una directory esterna ai checkout, nella quale non esiste ancora `integration`. Nel blocco sostituisci `META_REPOSITORY_URL` con l’URL del padre e i due segnaposto `PUBLISHED_FINAL_*_COMMIT_SHA` con gli SHA completi dei commit finali, già pubblicati nei remoti dei componenti dopo i merge. I percorsi seguono `.gitmodules`, nella struttura di esempio:

```bash
META_URL='<META_REPOSITORY_URL>'
API_SHA='<PUBLISHED_FINAL_API_COMMIT_SHA>'
WEB_SHA='<PUBLISHED_FINAL_WEB_COMMIT_SHA>'

git clone --recurse-submodules "$META_URL" integration
git -C integration switch -c integration/orders-pagination

git -C integration/components/api fetch origin
git -C integration/components/api switch --detach "$API_SHA"
git -C integration/components/web fetch origin
git -C integration/components/web switch --detach "$WEB_SHA"

git -C integration/components/api status --short
git -C integration/components/web status --short
```

Controlla lo stato dei sorgenti prima e dopo il bootstrap e le prove previste dal task. Devono corrispondere agli SHA selezionati. Se per far passare un test servono modifiche o nuovi sorgenti non committati, ferma la registrazione: integra quelle modifiche nei componenti e ripeti le prove sulle nuove revisioni finali. Non fare reset del lavoro locale per ottenere un checkout pulito.

Aggiorna `tasks/orders-pagination.md` con revisioni, esiti e riferimenti ai risultati conservati. Includilo nella stessa PR dei gitlink e rileggi l’intero diff:

```bash
git -C integration add -- components/api components/web tasks/orders-pagination.md
git -C integration diff --cached --submodule=log
git -C integration commit -m "Record verified orders pagination composition"
git -C integration push --recurse-submodules=check -u origin integration/orders-pagination
```

La modalità `check`, descritta in [Pro Git](https://git-scm.com/book/en/v2/Git-Tools-Submodules#_publishing_submodules), rifiuta il push se i nuovi commit dei submodule non risultano pubblicati nei remote conosciuti. Il clone della CI dovrà comunque avere accesso a quei repository.

Nel clone di integrazione il `HEAD` detached è intenzionale: stiamo provando commit precisi. Per sviluppare in parallelo, userei branch e worktree dei singoli componenti. Il manuale [git-worktree](https://git-scm.com/docs/git-worktree#_bugs) segnala ancora supporto incompleto dei submodule e sconsiglia più checkout del superproject. Eviterei inoltre aggiornamenti ricorsivi nel checkout su cui sta lavorando un’altra sessione.

## Quando cambierei scelta

Oggi aggiungerei al confronto anche [west](https://docs.zephyrproject.org/latest/develop/west/manifest.html#freezing-manifests): può produrre un manifest con revisioni risolte a SHA usando `west manifest --freeze`. Il comando usa i riferimenti `manifest-rev` gestiti da west, non fotografa il lavoro locale del programmatore. È una possibilità emersa nell’approfondimento di questo articolo, da conoscere prima di costruire un altro gestore di manifest.

Se ciò che il client consuma è una libreria rilasciata, valuterei versioni di pacchetti e lockfile del consumer come confine del coordinamento. Se invece quasi ogni funzionalità richiede modifiche simultanee agli stessi componenti, riesaminerei il monorepo: a forza di imitarlo con script e convenzioni si può accumulare lavoro di coordinamento senza poter committare insieme le modifiche correlate.

Per decidere se tenere i pin, partirei dal loro prossimo utilizzatore: chi deve recuperare questa coppia di revisioni, per quale controllo? Se la risposta è concreta, il commit del padre ha uno scopo e un responsabile. Se cerchiamo soltanto un ingresso al workspace, possiamo mantenere mappa e istruzioni senza aggiornare una composizione a ogni modifica.
