OSS/CSS Granularization — Why I Split foundry/ in Two
Published on 03 May 2026
- The Accident Waiting to Happen
- The Solution: OSS/ and CSS/
- Why "OSS" and "CSS" and Not "Public" and "Private"
- The Special Case: cheroliv.com
- OSS/CSS granularization modifies agent governance on three points:
- Before (flat folder)
- What I did this afternoon was replace an invisible convention
- Article 0117 — The LLM Governance Matrix
-
reading time: 10 minutes
foundry/— the functional area that hosts the code of my workspace — contains 18 projects. Eight are open source under Apache 2.0. Only one is closed source — the Edster SaaS. For months, these nine projects cohabited in the same folder, separated only by… nothing. Their public/private status was information in my head, not in the file system.
And then I wanted to hook up a RAG. And that’s when everything collapsed.
The Accident Waiting to Happen
In April 2026, I started implementing pgvector RAG for slider-gradle. The principle: index all repositories in`foundry/`, produce embeddings, and inject them into the LLM’s context so that it has a "perception" of the`foundry/`folder.
The pipeline was simple:
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)
Simple. Effective. Anddangerous.
Because this`fileTree`does not differentiate between`plantuml-gradle/` (Apache 2.0, public) and`edster/`(closed source, private). It swallows everything. And if one day I publish these embeddings — on a dashboard, in an LLM response, in a training dataset — Edster’s proprietary code leaks.
|
The problem isn’t an LLM reading closed source code. The problem is that this code ends up in a public vector embedding — irreversible, non-deletable, non-auditable. |
The Real Question
It’s not "how to prevent the LLM from leaking code?". It’s "how to make the leak structurally impossible?"
The answer is not a prompt. The answer is to split the folder in two.
The Solution: OSS/ and CSS/
Before:
foundry/
├── plantuml-gradle/ ← public
├── bakery-gradle/ ← public
├── magic-stick/ ← public
├── edster/ ← PRIVÉ
├── slider-gradle/ ← public
└── ... ← mélange invisible
After:
foundry/
├── OSS/ ← tout est Apache 2.0
│ ├── plantuml-gradle/
│ ├── bakery-gradle/
│ ├── magic-stick/
│ ├── slider-gradle/
│ └── ...
└── CSS/ ← tout est closed source / private
└── edster/
Le `fileTree`becomes:
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é
|
The public RAG never sees`CSS/`. The closed source code feeds a private fine-tuning dataset — an internal model that never leaves my house. The separation is mechanical, not declarative. |
Why "OSS" and "CSS" and Not "Public" and "Private"
The choice of acronyms OSS (Open Source Software) and CSS (Closed Source Software) is deliberate:
-
"Public"/"Private" describes thevisibility(what GitHub sees)
-
"OSS"/"CSS" describes thenatureof the code (what the LLM needs to know)
GitHub visibility is metadata of the remote repo. The nature of the code is a property of the content. The LLM does not have access to the GitHub API — but it has access to the file system.CSS/`tells it "careful, this code is not under a free license" without needing to read a`LICENSE`or parse a`package.json.
|
The folder name is the most robust metadata you can give to an LLM. It cannot be misparsed, ignored, or misinterpreted. The LLM sees`CSS/edster/`in the file path — it knows. |
The Complete Tree — Four Zones, Not Three
OSS/CSS granularization completes the three-zone ontology. The `foundry/`zone goes from 1 to 2 functional sub-zones:
| Zone | GDPR | Public RAG Indexing | Fine-tuning Dataset | | Root | Level 0 — Intimate | ✗ | ✗ | |configuration/| Level 1 — Restricted | ✗ | ✗ | |office/| Level 2 — Collaborative | ✓ Filtered | ✓ Filtered | |foundry/private/| Level 3 — Conditional Open | ✗ | ✓ Private | |foundry/public/| Level 4 — Native Public | ✓ Free | ✓ Public |
The Special Case: cheroliv.com
While I was at it, I corrected another inconsistency. My site cheroliv.com`lived in`foundry/`as a full-fledged software project — with its own Gradle build, its own CI, its own governance ..agents/`But blog posts are not code. They are editorial data
of Circle 2 — just like the framings of or the training of`office/pilotage/` .`office/formations/`What changes:
AVANT APRÈS
foundry/ office/
cheroliv.com/ sites/
site/jbake/content/blog/ cheroliv.com/
2026/0117_....adoc 2026/0117_....adoc
Articles are in
-
→ Circle 2 confidentiality`office/`The
-
task becomes a capability of`publishSite`via`engine`
— the plugin receives an`bakery-gradle`, it doesn’t know`FileTree`that the articles come from One single CI, one single governance — zero duplication`office/`
-
The Vision/Opinion classifier (Rule 2bis) applies automatically:
-
articles in
are filtered before publication`office/`bakery-gradle Doesn’t Care
And that’s the beauty of it. The
plugin was not modified.bakery-gradle`It receives an AsciiDoc content directory, it generates HTML, it pushes to GitHub Pages. Whether this directory is called — the plugin doesn’t care.`site/jbake/content/ ou `office/sites/cheroliv/`The interface contract is a Gradle task. Not a REST API. Not a webhook.
// 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
}
}
A task — typed, testable, executable locally and in the CI. Impact on Agent Governance
OSS/CSS granularization modifies agent governance on three points:
Public RAG : the indexing perimeter changes from
-
+
foundry/**
à foundry/public/. Closed source code`office/Vision/`is excluded from the index by task configuration, not by prompt. Knowledge Graph : graphify-gradle produces two graphs:
-
*
(public) — relations between OSS artifacts + office/Vision`office/graph.json`* (gitignored, private) — relations between`configuration/graph_private.json`CSS artifacts, never published Fine-tuning Dataset :
-
now has two sources:`codebase-gradle`*
→ public datasets (community models, benchmarks)OSS/* → private datasets (internal model, proprietary fine-tuning)`CSS/`What is Gained (And What is Lost)
Before (flat folder)
After (OSS/CSS + office/sites) |
→ public/private mix |
|
`ls foundry/public/`RAG indexes everything without discrimination |
RAG indexes |
+`OSS/`exclusively`office/Vision`Impossible to know if a project is open source |
The path |
says it`OSS/` ou `CSS/`cheroliv.com has its own CI, its own governance |
CI and governance are shared in engine |
Blog posts are in a "code" repository |
Blog posts are in |
— data, not code`office/sites/`Risk of closed source code leaking into public embeddings |
Structurally impossible — |
out of RAG scope`CSS/`The only cost: a |
global across 17 OSS repos + a refactor of the`git mv`. It’s a cold migration — build.gradle.kts de `engine`no data in flight, no users impacted. Conclusion: Physical Signal Beats Software Signal
What I did this afternoon was replace an invisible convention
with a visible separation in the zone. Before, you had to`foundry/`know that was closed source. Now, you see it —`edster/`the folder is called .`CSS/`It’s the same principle as Unix permissions, network VLANs, or
firewall compartments in a database. Security should not be information you have to remember. It must be a physical property of the system.OSS/CSS granularization is the translation of this principle into the domain
of LLM governance. The LLM doesn’t need to know that Edster is confidential. It needs to be incapable of indexing it. And that is exactly what
guarantees.`CSS/`References