tempo di lettura: 10 minutes

foundry/— l’area funzionale che ospita il codice del mio workspace — contiene 18 progetti. Otto sono open source sotto licenza Apache 2.0. Uno solo è closed source — il SaaS Edster. Per mesi, questi nove progetti hanno coabitato nella stessa cartella, separati solo da…​ niente. Il loro stato pubblico/privato era un’informazione nella mia testa, non nel sistema di file.

E poi ho voluto collegare un RAG. E lì, tutto è crollato.

L’incidente che stava per succedere

Nel aprile 2026 ho iniziato a implementare il RAG pgvector per slider-gradle. Il principio: indicizzare tutti i repository di`foundry/, produrre dei embeddings, e iniettarli nel contesto del LLM affinché ne abbia una percezione" del fascicolo`foundry/.

Il pipeline era semplice :

val repos = fileTree(rootDir) {
    include("**/*.adoc", "**/*.kts", "**/*.kt", "**/*.json", "**/*.yml")
}
val chunks = repos.map { chunk(it) }
val embeddings = chunks.map { embed(it) }
pgvector.insert(embeddings)

Semplice. Efficace. Epericoloso.

Perché questo`fileTree`non fa la differenza tra`plantuml-gradle/` (Apache 2.0, pubblico) e`edster/`(closed source, private). Divora tutto. E se un giorno pubblico questi embeddings — su un dashboard, in una risposta LLM, in un dataset di addestramento — il codice proprietario di Edster trapelato.

Il problema non è che un LLM legga codice closed source. Il problema, è che questo codice finisca in un embedding vettoriale pubblico — irreversibile, non cancellabile, non controllabile

La Vera Domanda

Non è "come impedire al LLM di trapelare il codice ?". C’è "come Rendere la fuga strutturalmente impossibile?

La risposta non è un prompt. La risposta è di dividere la cartella in due.

La Soluzione: OSS/ e CSS/

Prima :

foundry/
├── plantuml-gradle/       ← public
├── bakery-gradle/         ← public
├── magic-stick/           ← public
├── edster/                ← PRIVÉ
├── slider-gradle/         ← public
└── ...                    ← mélange invisible

Dopo:

foundry/
├── OSS/                   ← tout est Apache 2.0
│   ├── plantuml-gradle/
│   ├── bakery-gradle/
│   ├── magic-stick/
│   ├── slider-gradle/
│   └── ...
└── CSS/                   ← tout est closed source / private
    └── edster/

Le `fileTree`diventa :

val ossRepos = fileTree(File(rootDir, "OSS")) {
    include("**/*.adoc", "**/*.kts", "**/*.kt", "**/*.json", "**/*.yml")
}
val cssRepos = fileTree(File(rootDir, "CSS")) {
    include("**/*.adoc", "**/*.kts", "**/*.kt", "**/*.json", "**/*.yml")
}

// Embeddings publics — OSS seulement
pgvectorPublic.insert(ossRepos.map { chunk(it) }.map { embed(it) })

// Dataset fine-tuning privé — CSS seulement
fineTuningDataset.insert(cssRepos.map { chunk(it) })  // jamais publié

Il RAG pubblico non vede mai`CSS/`. Il codice closed source alimenta un dataset senza fine-tuning — un modello interno che non esce mai da casa mia. La separazione è meccanica, non dichiarativa.

Perché "OSS" e "CSS" e Non "Public" e "Private

La scelta degli acronimi OSS (Open Source Software) e CSS (Closed Source Software) è deliberato :

  • Pubblico"/"Privato" descrive lavisibilità(ciò che GitHub vede)

  • OSS"/"CSS" descrive lanaturadel codice (ciò che il LLM deve sapere)

La visibilità GitHub è un metadato del repository remoto. La natura del codice è una proprietà del contenuto. Il LLM non ha accesso all’API GitHub — ma ha accesso al sistema di file.CSS/`gli dice "attenzione, questo codice non è sotto licenza libero" senza bisogno di leggere un`LICENSE`o di parsare un`package.json.

Il nome della cartella è il metadato più robusto che Lei possa dare a un LLM. Non può essere parsata male, ignorata o interpretata male. Il LLM vede`CSS/edster/`nel percorso del file — lo sa.

L’Albero Completo — Quattro Zone, Non Tre

La granulazione OSS/CSS completa l’ontologia a tre zone. La zona `foundry/`passa da 1 a 2 sottozone funzionali :

L’organisation de foundry/ en deux sous-zones OSS et CSS

| Zone | GDPR | Indicizzazione RAG pubblica | fine-tuning del dataset | | Radice | Livello 0 — Intimo | ✗ | ✗ | (empty)configuration/| Livello 1 — Limitato | ✗ | ✗ | (No output)office/| Livello 2 — Collaborativo | ✓ Filtrato | ✓ Filtrato | (no output)foundry/private/| Livello 3 — Aperto condizionale | ✗ | ✓ Privato | (No output)foundry/public/| Livello 4 — Pubblico nativo | ✓ Libero | ✓ Pubblico |

Il caso particolare: cheroliv.com

Mentre ero lì, ho corretto un’altra incoerenza. Il mio sito cheroliv.com`viveva in`foundry/`come un progetto software a tutti gli effetti — con il proprio build Gradle, il proprio CI, il proprio governance.agents/`.

Ma gli articoli del blog non sono codice. Sono dati editoriali** di Cerchio 2 — allo stesso titolo degli inquadramenti di`office/pilotage/` o le formazioni di`office/formations/`.

AVANT                            APRÈS
foundry/                office/
  cheroliv.com/                    sites/
    site/jbake/content/blog/         cheroliv.com/
      2026/0117_....adoc               2026/0117_....adoc

Cosa cambia :

  • Gli articoli sono in`office/`→ riservatezza di Cerchio 2

  • Il compito`publishSite`diventa una capability di`engine`

via`bakery-gradle`— il plugin riceve un`FileTree`, non lo sa che gli articoli vengano da`office/`

  • Un solo CI, una sola governance — zero duplicazione

  • Il classificatore Vision/Opinion (Regola 2bis) si applica automaticamente :

gli articoli in`office/`sono filtrati prima della pubblicazione

bakery-gradle non gliene frega niente

E quello è bello. Il plugin`bakery-gradle`non è stato modificato. Riceve una directory di contenuto AsciiDoc, genera HTML, push sui GitHub Pages. Che questo directory si chiami`site/jbake/content/` ou office/sites/cheroliv/# Gradle Integration JBake can be integrated into Gradle builds using the JBake Gradle plugin or by calling the JBake CLI directly:

----
tasks.register<JavaExec>("bake") {
    mainClass.set("org.jbake.launcher.Main")
    classpath = configurations["jbake"]
    args = listOf(projectDir.absolutePath, "$buildDir/jbake")
}
----

— il plugin non se ne frega.

[source,kotlin]
----
// engine/build.gradle.kts
task("publishBlog") {
    doLast {
        val articles = fileTree("../../office/sites/cheroliv/2026/")
        bakery.generate(articles)  // ← bakery ne sait pas d'où ça vient
    }
}
----

Il contratto di interfaccia è una attività Gradle. Non è un'API REST. Non è un webhook. Un compito — tipizzato, testabile, eseguibile in locale e nella CI.

== Impatto sulla governance dell'agente

La granularizzazione OSS/CSS modifica la governance degli agenti su tre punti:

1. *RAG public* : il perimetro di indicizzazione passa da`foundry/**`

à `foundry/public/**`+`office/Vision/**`Il codice sorgente chiuso è escluso dall'indice per configurazione dell'attività, non per prompt.

1. *Knowledge Graph* : graphify-gradle produce due grafi :

*`office/graph.json`(public) — relazioni tra artefatti OSS + office/Vision *`configuration/graph_private.json`(gitignored, privato) — relazioni tra artefatti CSS, mai pubblicato

1. *Fine-tuning del dataset* :`codebase-gradle`ha ora due fonti:

*`OSS/`→ datasets pubblici (modelli comunitari, benchmarks) *`CSS/`→ datasets privati (modello interno, fine-tuning proprietario)

== Cosa si guadagna (e cosa si perde)

|===
|Fronte (cartella piatta) |Dopo (OSS/CSS + office/sites) |`ls foundry/`→ misto pubblico/privato |`ls foundry/public/`tutto è pubblicabile |RAG indicizza tutto senza discriminazione |RAG indicizza`OSS/`+`office/Vision`esclusivamente |Impossibile sapere se un progetto è open source |Il percorso`OSS/` ou `CSS/`il detto |cheroliv.com ha la propria CI, la propria governance |La CI e la governance sono condivise nel motore |Gli articoli del blog sono in un repository 'code' |Gli articoli del blog sono in`office/sites/`— dati, non codice |Rischio di fuga di codice sorgente chiuso in embeddings pubblici |Strutturalmente impossibile —`CSS/` hors scope RAG
|===

L'unico costo: uno`git mv`globale sui 17 depositi OSS + un refactoring del `build.gradle.kts` de `engine`. È una migrazione a freddo — nessun dato in volo, nessun utente interessato

== Conclusione: il Segnale Fisico batte il Segnale Logico

Ciò che ho fatto questo pomeriggio è stato sostituire una convenzione invisibile da una separazione visibile nella zona`foundry/`. Prima, dovevi *sapere* che`edster/`era closed source. Ora, tu lo *vedi* — la cartella si chiama`CSS/`.

È lo stesso principio delle autorizzazioni Unix, delle VLAN di rete, o le compartimenti tagliafuoco in un database. La sicurezza non deve essere un'informazione che devi ricordare. Deve essere una proprietà **fisica**del sistema.

La granularizzazione OSS/CSS è la traduzione di questo principio nel settore della governance LLM. Il LLM non ha bisogno di sapere che Edster è confidenziale. Ha bisogno di essere incapace di indicizzarlo.

E questo è esattamente quello che`CSS/`garantisce.

== Riferimenti

* xref:0118_matrice_gouvernance_llm_zone_acces_workspace_post.adoc[Articolo 0117 — La matrice di governance LLM]
* xref:0114_gouvernance_cercles_confiance_ontologie_spatiale_alignement_llm_post.adoc[Articolo 0114 — I Cerchi di Fiducia]
* xref:0116_compartimentage_epistemique_automatise_llm_post.adoc[Articolo 0116 — Il Compartimentaggio Epistemico Automatizzato]
* xref:0108_gouvernance_agent_opencode_eager_lazy_post.adoc[Articolo 0108 — Gestire un agente IA con AsciiDoc]

Articoli correlati