Construire un pont entre un CAS C++ et Julia avec un agent IA
Université de Poitiers - IUT de Poitiers Châtellerault Niort · Département GEII
2026-07-02
1. Poser le décor Contexte pédagogique, Giac, l’état des lieux avant.
2. Giac.jl en action Installation, premiers pas, calcul symbolique.
3. L’architecture GIAC_jll, C-shim, CxxWrap, structure du paquet.
4. La méthode Développement piloté par spécification avec un agent IA.
5. Honnêteté Ce qui fonctionne, ce qui ne fonctionne pas encore et ce qui n’a pas initialement bien fonctionné.
6. La suite Feuille de route, projets connexes…
Merci à Pierre Navaro (CNRS, IRMAR Rennes, Groupe Calcul) pour cette invitation au Café Julia du Groupe Calcul.
Merci à Bernard Parisse (Université Grenoble Alpes, Institut Fourier), créateur et mainteneur de Giac depuis 25 ans, qui a accepté que je présente ce travail aujourd’hui.
« j’ai le sentiment que [Julia] est un excellent choix de langage pour un jeune développeur ! Il me semble donc que Sébastien est la personne qu’il vous faut dans le cadre d’un Café Julia »
— Bernard Parisse, email du 4 mai 2026
Sébastien Celles

Les étudiants en BUT GEII doivent analyser symboliquement des circuits électriques linéaires :
Ils n’y arrivent plus à la main. Pas par paresse — la manipulation algébrique a reculé au lycée.
Je ne peux pas changer le lycée. Je peux leur donner un outil.
C’est ce qui m’a mené à vouloir un CAS1 solide en Julia.
Pour aujourd’hui :
| Terme | Définition |
|---|---|
| CAS | Computer Algebra System — système de calcul formel |
| CLI | Command Line Interface — interface en ligne de commande |
| FFI | Foreign Function Interface — pont entre langages |
| JLL | Julia Language Linking — paquet Julia fournissant un binaire pré-compilé |
| CxxWrap | Bibliothèque Julia pour créer des bindings C++ |
| PIMPL | Pointer to IMPLementation — pattern C++ d’isolation |
| SDD | Spec-Driven Development — développement piloté par spécification |
| EARS | Easy Approach to Requirements Syntax (Mavin et al., RequirementsEngineering’09) |
C--)
Giac.jl — installer Giac à la mainPour appeler Giac depuis Python, Julia, R, etc., il faut d’abord installer la bibliothèque C++ système.
| Système | Commande typique | Statut |
|---|---|---|
| Debian / Ubuntu | sudo apt install libgiac-dev |
OK, version variable selon la distrib |
| Windows | installateur Xcas, ou MSYS2 + compilation | piégeux |
…
Avertissement
Trois soucis pour qui veut écrire un wrapper :
Pkg.addC’est exactement ce que GIAC_jll va supprimer plus loin — l’utilisateur n’aura plus à se soucier de rien.
Le 8 février 2026, j’ouvre une issue chez HaraldHofstaetter/Giac.jl #4:
« Future of Giac.jl? (2026) »
Le repo original (~10 ans) est dormant. Bernard Parisse a son propre fork. Aucun n’est vraiment utilisable « simplement » aujourd’hui.
J’y joins un plan généré par IA (Claude Opus 4.6) avec :
GIAC_jll via BinaryBuilderGiac.jl moderneNote
Bernard Parisse me contacte le lendemain. Le projet démarre.
ou
julia> ]
(@v1.12) pkg> add Giac
Astuce
Zero-install : pas de compilation locale.
GIAC_jll télécharge le binaire pré-compilé pour la plateforme (Linux x86_64/aarch64, macOS x86_64/aarch64, Windows).
~10 secondes la première fois.
using Giac.Commands: diff, integrate, limit, series
@giac_var x
diff(sin(x^2), x) # 2*x*cos(x^2)
diff(x^4, x, 2) # 12*x^2 (dérivée seconde)
integrate(x*exp(x), x) # (x-1)*exp(x) (par parties)
integrate(sin(x), x, 0, π) # 2 (intégrale définie)
limit(sin(x)/x, x, 0) # 1
limit((1 + 1/x)^x, x, Inf) # exp(1)
series(exp(x), x, 0, 4) # 1+x+x²/2+x³/6+x⁴/24+O(x⁵)Une matrice 2×2 à coefficients purement symboliques :
Important
Tout reste symbolique de bout en bout — déterminant, trace, inverse, … : aucune approximation numérique.
using Giac.Commands: laplace, ilaplace, partfrac
@giac_var s t
# Réponse impulsionnelle d'un système d'ordre 2
H = 1 / (s^2 + 2*s + 5)
ilaplace(H * 1/s, s, t) # GiacExpr: 1/5-1/5*cos(2*t)*exp(-t)-1/10*sin(2*t)*exp(-t)
# Décomposition en éléments simples (avant transformation inverse de Laplace ILT à la main)
partfrac(1/(s^2 - 1), s). # GiacExpr: 1/2/(s-1)-1/2/(s+1)
H = 10 / (s * (2*s + 1))
partfrac(H) # GiacExpr: 10/s-10/(s+1/2)
# Transformée de Laplace
laplace(exp(-2*t)*sin(3*t), t, s) # GiacExpr: 3/(s^2+4*s+13)Symbolics.jl est excellent dans son couloir (SciML, ModelingToolkit). Mais pour mon usage CAS, il manque des choses :
Astuce
Conversion par parcours d’arbre, pas par round-trip de chaîne. Les BigInt passent par GMP en binaire. π, ℯ, i sont préservés.
Pas une compétition — des forces complémentaires.
ifactor pour les CTFMon usage personnel, hors enseignement :
60 chiffres, semi-premier RSA-style, factorisé en quelques secondes.
Primes.jl n’a pas l’algorithme — issue #159.
On retrouve ici les 2 facteurs premiers :
65065544729509880110227237436797203382511716094137942550450PR Giac.jl#29 — extension à dépendance faible GiacMCPExt qui expose Giac.jl à tout client MCP-aware (Claude Desktop, Claude Code, Cursor) via ModelContextProtocol.jl.
Deux outils MCP exposés :
giac_eval — évalue toute expression Xcas/Giac (les ~2200 commandes accessibles via une seule chaîne). Erreurs Julia renvoyées en CallToolResult(isError=true) → la session MCP reste vivante.giac_search — recherche par mot-clé sur le catalogue (matrix, laplace, …) avec fallback préfixe-puis-sous-chaîne.Philosophie : un seul outil eval, pas 2200. Le LLM connaît déjà la syntaxe Xcas — Giac est le moteur d’exécution, le LLM est le décideur.
serverInfo.version reflète pkgversion(Giac) → le client voit la version live.
❯ claude mcp add-json --scope user "giac-cas" '{
"command": "julia",
"args": ["--startup-file=no",
"--project=/home/scelles/.julia/environments/mcp-giac",
"-e", "using Giac, ModelContextProtocol; start!(giac_mcp_server())"]
}'
Added stdio MCP server giac-cas to user config
❯ claude mcp list
giac-cas: julia --startup-file=no ... - ✓ Connected❯ Utilise le serveur MCP giac-cas pour factoriser x^4 - 1
● giac-cas → giac_eval(expr: "factor(x^4-1)")
Evaluate any expression using the Giac/Xcas computer algebra system
(~2200 functions). Syntax follows Xcas conventions.
Do you want to proceed?
1. Yes
2. Yes, and don't ask again for giac-cas - giac_eval commands [...]
3. No
● factor(x^4 - 1) = (x-1)(x+1)(x²+1)
✻ Worked for 8s
❯ Utilise le serveur MCP giac-cas pour factoriser
632459103267572196107100983820469021721602147490918660274601
● giac-cas → giac_eval(expr:
"ifactor(632459103267572196107100983820469021721602147490918660274601)")
→ 650655447295098801102272374367 × 972033825117160941379425504503
✻ Cogitated for 7s
60 chiffres, deux facteurs premiers de 30 chiffres chacun.
La boucle se referme : un agent IA a aidé à construire Giac.jl (SDD, Spec-Kit) — et Giac.jl devient à son tour un outil pour les agents IA. Giac, le CAS, ne change pas : c’est l’écosystème autour qui s’étend.
📝 Transcript complet et plan d’implémentation : talk/raw/drafts/Feature Giac.jl - intégration serveur MCP.md
Pkg.add┌─────────────────────────────────────────────────────────┐
│ s-celles/giac (fork C++, branche dev) GPL-3+ │ src
│ Modernisation Meson · 22 features · CI 3 plateformes │
└─────────────────────────────────────────────────────────┘
│ Yggdrasil (BinaryBuilder)
▼
┌─────────────────────────────────────────────────────────┐
│ GIAC_jll GPL-3+ │ bin
│ libgiac.{so,dylib,dll} + icas + share/giac/aide_cas │
└─────────────────────────────────────────────────────────┘
│ dépendance binaire
▼
┌─────────────────────────────────────────────────────────┐
│ libgiac-julia-wrapper (C++17) GPL-3+ │ src
│ CxxWrap + PIMPL · Firewall headers · jlcxx::Module │
└─────────────────────────────────────────────────────────┘
│ Yggdrasil (BinaryBuilder)
▼
┌─────────────────────────────────────────────────────────┐
│ libgiac_julia_jll GPL-3+ │ bin
│ Pont C++/Julia pré-compilé, chargé par CxxWrap │
└─────────────────────────────────────────────────────────┘
│ dépendance Julia
▼
┌─────────────────────────────────────────────────────────┐
│ Giac.jl (Julia pur) GPL-3+ │ src
│ @giac_var · API idiomatique · pont Symbolics │
└─────────────────────────────────────────────────────────┘
│
▼
`using Giac`
3 sources éditées + 2 JLLs auto-générés via Yggdrasil = 5 niveaux.
s-celles/giac — build Meson en 3 jours16-19 mars 2026 : 67 commits, 6 specs Spec-Kit.
| # | Spec | Effet |
|---|---|---|
| 001 | meson-build-system | Migration autotools → Meson |
| 002 | decouple-icas-fltk | CLI sans GUI |
| 003 | remove-autotools | Suppression de ~83 fichiers |
| 004 | micropython-submodule | Vendored → submodule git |
| 005 | extract-xcas | GUI extrait dans s-celles/xcas |
| 006 | cleanup-repo | Nettoyage final |
Note
Travail amont : ma PR #11 chez geogebra/giac ouverte le 9 février 2026 — « Add Meson build system for all platforms ». J’avais 5 semaines d’“expertise” Meson sur Giac avant d’attaquer le fork.
CMake aurait été le choix « par défaut ». J’ai préféré Meson pour trois raisons :
meson.build est non Turing-complet, syntaxe inspirée de Python pour la lisibilité. CMake, lui, a dérivé en quasi-langage.Pas une guerre de religion. Juste : sur ce projet, avec ces outils, Meson m’a fait gagner du temps.
PR #13101 (autotools) : un mois de combat (pas à temps plein quand même), plateforme par plateforme.
| Plateforme | Symptôme | Patch tenté |
|---|---|---|
| macOS | symboles LAPACK non-gardés dans vecteur.o |
-framework Accelerate |
| macOS | -lintl manquant à l’édition de liens |
GettextRuntime_jll |
| FreeBSD / Apple | libtool n’arrive plus à relier libxcas.la |
bypass make install |
| Windows / MinGW | que des .a statiques, pas de DLL |
-no-undefined |
| FreeBSD / Apple | pas d’artefact OpenBLAS32_jll |
retrait |
→ recette de 173 lignes de hacks par plateforme.
Astuce
PR #13324 : on retire autotools, on pointe sur le fork s-celles/giac (Meson). Recette = ~60 lignes, ~50 s par binaire, 18 plateformes :

Exemple : Buildkite #27644 — toutes vertes.
Aide humaine :
GIAC_jll dans Yggdrasil — la recette (partielle)# Extrait de Yggdrasil/G/GIAC/build_tarballs.jl
name = "GIAC"
version = v"2.0.1"
sources = [
GitSource("https://github.com/s-celles/giac.git", "194ff510...") # branche dev, build Meson
]
script = raw"""
cd ${WORKSPACE}/srcdir/giac*
meson setup build --cross-file="${MESON_TARGET_TOOLCHAIN}" \
-Dgui=disabled -Dpari=disabled -Dntl=disabled \
-Dgsl=disabled -Dlapack=disabled # ... 22 options
meson compile -C build -j${nproc}
meson install -C build
"""
products = [# ...] # products exposés côté Julia
dependencies = [
Dependency("Gettext_jll"), Dependency("GMP_jll"),
Dependency("MPFR_jll"), Dependency("Readline_jll"),
]
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies;
clang_use_lld=false, preferred_gcc_version=v"8", julia_compat="1.10")GIAC_jll dans Yggdrasil2 PRs Yggdrasil mergées :
Products du JLL — et l’astuce icasLe JLL expose trois artefacts côté Julia :
Astuce
icas exposé comme exécutable : on peut lancer le REPL Giac d’origine depuis Julia, sans installation système.
→ utile pour diagnostiquer un bug : si icas produit le bon résultat mais que Giac.jl ne le fait pas, le problème est dans le wrapper. Si icas se trompe lui aussi, c’est en amont.
Documentation : s-celles.github.io/Giac.jl/dev/icas.
libgiac-julia-wrapper — CxxWrap + PIMPL┌──────────────────────────────────────────────────────┐
│ Giac.jl (Julia) │
│ using CxxWrap; @wrapmodule │
├──────────────────────────────────────────────────────┤
│ giac_wrapper.cpp (C++17, inclut jlcxx) │
│ • Bindings CxxWrap │
│ • NE DOIT PAS inclure de header Giac │
│ • N'inclut que giac_impl.h │
├──────────────────────────────────────────────────────┤
│ giac_impl.cpp (C++14) │
│ • Inclut <giac.h>, <input_lexer.h> │
│ • API Giac : gen(), eval(), at_XXX... │
└──────────────────────────────────────────────────────┘
PIMPL = Pointer to IMPLementation. Le pattern qui permet d’isoler les conflits de headers entre Giac et CxxWrap. Julia ne voit qu’un pointeur opaque.
libgiac_julia_jll — la matriceCréation d’une recette Yggdrasil pour le wrapper C++.
Le wrapper C++ se lie à libcxxwrap_julia → dépend de l’ABI Julia → recompilé pour chaque mineure.
Interface Buildkite pour libgiac_julia_jll
→ ~60 builds en parallèle, temps total d’exécution : un peu plus de 7 minutes, ~50 s chacun. Buildkite #28291.
Conséquence pour Giac.jl : un seul Pkg.add("Giac") choisit automatiquement le bon binaire pour la plateforme et la version Julia de l’utilisateur. Côté utilisateur, c’est invisible. Côté mainteneur, c’est ~60 builds à valider à chaque release.
« On passe plus de temps à décrire ce qu’on veut que ce qu’on fait. »
| Dev classique | SDD avec agent | |
|---|---|---|
| Lignes de code | 10 000 | 10 000 |
| Lignes de spec | ~1 000 | ~63 000 |
| Ratio | 1:10 | 6:1 |
Pourquoi ça marche :
| Outil | Rôle |
|---|---|
| Claude Code CLI | Agent IA en CLI (Anthropic) |
| GitHub Spec-Kit | Workflow SDD outillé |
/speckit-constitution Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync. (project)
/speckit-specify Create or update the feature specification from a natural language feature description. (project)
/speckit-plan Execute the implementation planning workflow using the plan template to generate design artifacts. (project)
/speckit-tasks Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts. (project)
/speckit-implement Execute the implementation plan by processing and executing all tasks defined in tasks.md (project)
| Outil | Rôle |
|---|---|
| EARS | Spécification amont en langage contraint, (en français parfois en anglais pour me faire un peu moins consommer de tokens) |
Mon flux : EARS (humain + IA + humain) → Découpage en une roadmap (ROADMAP.md) de features à livrer (IA + humain) → Spec-Kit (agent, anglais) → code.
L’agent IA est un outil en CLI : il vit dans un terminal. Or une tâche agentique (/speckit-implement, un build) peut tourner longtemps.
Je lance l’agent dans une session GNU screen — un multiplexeur de terminal —, sur un serveur Linux distant que j’atteins en SSH — depuis différents lieux via différentes machines (PC fixe, PC portable), et même depuis mon téléphone Android (Termux + clé SSH). Le serveur est mon « bureau virtuel » — l’agent y tourne en continu, accessible de partout.
ssh serveur # n'importe quel poste — auth par clé SSH
screen -S agent # créer une session nommée « agent »
claude # … on lance l'agent à l'intérieur
# Ctrl-a puis d → détache : l'agent CONTINUE
screen -ls # lister les sessions actives
screen -r agent # rattacher (depuis le même poste)
screen -dr agent # détacher de force ailleurs + rattacher ICIscreen -dr — la commande clé du multi-poste. Oublié de détacher à la maison ? Depuis le téléphone portable, screen -dr agent arrache la session de l’ancien terminal et me la rend ici. Un seul agent, joignable de partout — et qui a continué d’avancer pendant que j’étais déconnecté.
screen rend l’agent joignable en continu. La tentation serait de le laisser tourner en autonomie totale, sans jamais valider une action.
Le réglage existe : un drapeau supprime toutes les demandes de permission. Son nom dit tout — il contient le mot dangerously (--dangerously-skip-permissions).
Je ne l’active pas. Même détaché, l’agent reste derrière ses permissions : il s’arrête et m’attend dès qu’une action sort de l’allowlist (commande shell non listée, git push, écriture hors périmètre).
Mon choix assumé : je préfère baby-sitter l’agent — me rattacher, lire ce qu’il propose, valider — plutôt que lui accorder trop de droits. screen me sert la continuité entre postes, pas l’autonomie sans contrôle. Ce qui tourne sans moi se limite exactement à l’allowlist de settings.json, volontairement étroite.
EARS = Easy Approach to Requirements Syntax
Repris par Kiro — l’IDE agentique d’Amazon (AWS) sorti en 2025 — qui utilise EARS comme format de spec en amont de son workflow agentique.
Pas (vraiment) intégré à Spec-Kit — c’est dommage (j’y reviens juste après).
Avant que Spec-Kit prenne le relais, j’écris la spec fondatrice en français, en EARS.
Extrait de specs/ears/Giac.jl - EARS - introspection.md (v1.1, 449 lignes) :
REQ-C10 [Ubiquitaire] La classe Gen shall exposer une méthode
is_integer()retournanttruesi et seulement si l’instance est de type_INT_ou_ZINT.
5 patterns EARS :
Ubiquitaire (Ubiquitous),
Piloté par des événements (Event-driven),
Piloté par l’état (State-driven),
Fonctionnalité optionnelle (Optional feature),
Comportement non souhaité (Unwanted behavior)
Plus d’informations : Wikipedia (FR/EN)
Pourquoi en français ? Parce que c’est ma langue de pensée d’enseignant. Spec-Kit prend ensuite le relais en anglais pour exécuter.
Upstream : j’ai ouvert spec-kit#1356 pour proposer EARS comme intégration officielle (template, lint dans /speckit-clarify, ou commande /speckit-ears).
Une CLI Python (uv tool install specify-cli) qui :
specify init).claude/commands/.specify/templates/constitution.md dans .specify/memory/Documentation détaillée : github.com/github/spec-kit · synthèse interactive sur DeepWiki.
Spec-Kit Workflow
Exemple: - issue spec-kit #1377 - Gist privé
/speckit-specify ne produit pas de code. Elle traduit un prompt en spec exécutable — un document structuré, lisible par un humain comme par un agent.
Cette phase répond à une seule question : est-ce qu’on est d’accord sur ce qu’on construit ?
3 sous-commandes :
/speckit-specify — prompt → spec.md
NNN-feature-name (ex. 067-build-function-tier3)specs/NNN-feature-name/spec.md à partir de .specify/templates/spec-template.md/speckit-clarify — combler les trous
spec.md, pose ≤ 5 questions ciblées sur les zones sous-spécifiées## Clarifications datée/speckit-checklist (optionnel) — porte de qualité
Invariant bloquant : tant qu’il reste un marqueur [NEEDS CLARIFICATION: …] dans spec.md, /speckit-plan refuse de tourner.
spec.mdLe template spec-template.md impose 7 sections, dans cet ordre :
| Section | Rôle |
|---|---|
| User Scenarios & Testing | User stories priorisées P1/P2/P3, chacune indépendamment testable (une story = un MVP (Minimum Viable Product) autonome) |
| Acceptance Scenarios | Format Given / When / Then — base des tests d’acceptation |
| Edge Cases | Conditions limites explicites (π, types mixtes, variable libre…) |
| Functional Requirements | FR-001, FR-002… au style EARS (MUST, SHALL) |
| Key Entities | Domaine métier, sans techno |
| Success Criteria | SC-001… métriques mesurables, technology-agnostic |
| Assumptions | Hypothèses prises par défaut quand le prompt ne tranche pas |
Règle d’or du template : pas de techno dans la spec. Sur Giac.jl, ça veut dire : pas de CxxWrap, pas de Symbolics.build_function, pas de GIAC_jll. On parle utilisateur et comportement, pas implémentation — l’implémentation arrivera dans plan.md (phase 2).
Pourquoi cette phase compte : une mauvaise spec produit un bon plan d’une mauvaise feature. 80 % de l’effort est dans le dialogue humain — clarifier les priorités, débusquer les hypothèses tacites — pas dans la rédaction.
s-celles/giac |
wrapper |
Giac.jl |
|
|---|---|---|---|
| Langage | C++ | C++ + Julia | Julia |
| Specs Spec-Kit | 6 | 4 | 65 |
| Lignes de spec | ~4 000 | ~5 700 | ~52 700 |
| EARS amont | — | 2 fichiers (FR) | — |
| Période | 16-19 mars | fév-avril | fév-mai |
| Stratégie | modernisation | fondations | itération |
Total : 75 specs, ~63 500 lignes de spécifications, 3 Constitutions adaptées.
CLAUDE.md- GitHub handle: s-celles
- Name: Sébastien Celles
- Disclose AI usage but never include "Generated with Claude Code" or "Co-Authored-By: Claude" in commit messages or documentation.
- Do NOT modify .gitignore (if necessary ask in !!! CAPITAL LETTERS !!!)
- Never try to git add CLAUDE.md file or specs/ .claude/ .specify/ folders
- Use conventional commit format (feat:, fix:, refactor:, ...)
- Project MUST adhere to Semantic Versioning
- Run tests before committing code changes
- Build doc (without warning) before committing
- All notable changes MUST be in CHANGELOG.md (Keep a Changelog format)
- Use Julia development skill
- Always document new features in docs/
- Always write tests before writing code (TDD)
## Active Technologies (auto-généré par update-agent-context.sh)
- 001-giac-jl-package: Julia 1.10 LTS + libgiac_jll...
- ... (88 lignes au total)15 règles humaines + section auto-générée par Spec-Kit.
Un skill Claude Code = un dossier .claude/skills/<nom>/SKILL.md chargé à la demande quand le contexte y correspond (fichier .jl, Project.toml, prompt parlant de Julia…).
Mon julia-development/SKILL.md (adapté de celui de @seabbs, 389 lignes) — extrait :
---
name: julia-development
description: Expert guidance for Julia package development following
SciML standards, patterns, and Julia ecosystem best practices
---
## Development Workflow
**IMPORTANT**: Always use `Pkg.update()` to update packages.
Never edit `Project.toml` directly for version updates.
### Testing
- Use `TestItemRunner` with `@testitem` syntax for modular testing
- Package-level tests in `test/package/` for quality (Aqua, DocTest, formatting)
### Enum Convention (MANDATORY for finite value sets)
Enums MUST be used for finite value sets. Each enum MUST: ...Effet concret : pas besoin de redire « écris du Julia idiomatique » à chaque session. L’agent commence par un test, pose les weak deps correctement (le FR-003 du Tier 3 vu plus haut !), et reste stylistiquement cohérent à travers les 65 features.
Skill vs CLAUDE.md : le skill = expertise de domaine (Julia ici), chargée à la demande. CLAUDE.md = règles du projet (toujours chargées). Complémentaires.
Exemple de SKILL.md complet :
.gitignore “classique” d’un projet JuliaSource : Github Julia.gitignore
# Files generated by invoking Julia with --code-coverage
*.jl.cov
*.jl.*.cov
# Files generated by invoking Julia with --track-allocation
*.jl.mem
# System-specific files and directories generated by the BinaryProvider and BinDeps packages
# They contain absolute paths specific to the host computer, and so should not be committed
deps/deps.jl
deps/build.log
deps/downloads/
deps/usr/
deps/src/
# Build artifacts for creating documentation generated by the Documenter package
docs/build/
docs/site/
# File generated by Pkg, the package manager, based on a corresponding Project.toml
# It records a fixed state of all packages used by the project. As such, it should not be
# committed for packages, but should be committed for applications that require a static
# environment.
Manifest*.toml
# File generated by the Preferences package to store local preferences
LocalPreferences.toml
JuliaLocalPreferences.toml
.gitignore allow-list — défense par défautPourquoi ? Avec un agent qui crée 20 fichiers de scratch par session (logs, brouillons, .specify/, CLAUDE.md, etc.), une allow-list garantit que rien ne fuit accidentellement dans un commit.
C’est l’inverse du .gitignore traditionnel. Défense par défaut.
.specify/memory/constitution.md — 6 principes pour Giac.jl :
Chaque plan.md contient une Constitution Check table : si un principe ne passe pas, on doit l’argumenter ou retravailler le plan.
Important
C’est ce qui empêche un agent de dériver sur 65 features.
Le point de départ — Tier 1, déjà livré en v0.13.0 : build_function(expr, vars...) retourne une fonction Julia qui passe par la FFI Giac à chaque appel.
Ce qu’on veut — Tier 3 : un mode opt-in backend=:symbolics qui transforme l’expression en fonction Julia native via Symbolics.build_function. Cible : boucles serrées.
Prompt initial : « I need a build_function function — see issue #17 for tier 3 » → /speckit-specify produit :
Extrait textuel — FR-003 : Tier 3 MUST be a weak-dependency extension — no strong dep on Symbolics may be added.
tasks.md éclate la spec en 25 tâches ordonnées, regroupées en 5 phases :
backend = :giac. Par défaut, comportement inchangé.→ branche → PR mergée → CHANGELOG.md → tag v0.14.0.
Durée totale : ~3-4 heures de session interactive humain + agent.
1. CLAUDE.md grossit indéfiniment Auto-enrichi par feature. À 100 specs → 200+ lignes. Compromis à trouver entre mémoire institutionnelle et dilution du contexte.
2. Dette de synchronisation upstream Bernard Parisse refuse actuellement Meson et Git. Mon fork doit (devra?) suivre ses releases manuellement (~quelques heures par version).
3. Coût de la rigueur ~63 000 lignes de spec écrites par moi + l’agent. Sans IA, ce volume serait inatteignable. Sans humain, l’agent dériverait.
Tout est sur GitHub dès le commit 1. Pas de garage privé puis grande révélation.
Question ouverte : est-ce que chaque commit IA-generated doit être marqué ? Je n’ai pas fait ça systématiquement car je ne souhaite pas que mes repositories se transforment en musée publicitaire pour l’agent. Mais je suis preneur de retours sur ce point.
Les retours du choix :
Sans visibilité publique, aucune de ces collaborations n’aurait eu lieu.
Le plan AI initial recommandait MIT pour Giac.jl :
« seulement du `ccall`, pas de code GPL »
Avertissement
Cet argument ne tient pas dans mon cas. CxxWrap reflète les types C++ dans Julia — ce n’est pas une frontière C-ABI propre.
J’ai corrigé : GPL-3-or-later sur toute la chaîne. Aligné sur Singular.jl (le précédent OSCAR).
Leçon : l’IA a fait gagner des semaines sur l’architecture. L’humain a rattrapé la nuance de licence. Exactement la collaboration recherchée.
Bernard Parisse maintient Giac depuis 25 ans :
Note
Mon fork ne demande rien à l’amont. Si l’écosystème Meson l’intéresse un jour, le travail existe. Sinon, mon fork vit sa vie sans le gêner.
C’est de la diplomatie technique.
Je suis devenu, de fait, le mainteneur Yggdrasil/Julia de Giac :
upstream Giac → s-celles/giac → GIAC_jll
libgiac-julia-wrapper
libgiac_julia_jll
Giac.jl
Workflow par release upstream : ~quelques heures pour propager une nouvelle version sur les 5 maillons.
Ce n’est pas un projet « livré, terminé ». C’est un poste de mainteneur pont.
Participation à JuliaCon ou pas, autant être franc sur ce qui m’a parfois ralenti :
Symbolics.jl#59 (couverture vs SymPy), #249 (partfrac), #1770 (Laplace) : ouvertes depuis parfois des années, toutes structurantes pour un usage CAS.Constat fait en contributeur reconnaissant, pas en client mécontent. La communauté m’a aidé (@parisseb, @giordano, @fingolfin, @ViralBShah, @jverzani et d’autres). Mais ces aides reposent sur la disponibilité de quelques individus — pas sur des process.
C’est précisément pour ça que rendre le travail visible tôt (issues, PRs publiques, drafts) est important : on multiplie les chances qu’une des personnes-clés croise le projet au bon moment.
Cohérence : si je questionne ma dépendance à Julia, je dois questionner ma dépendance à l’agent IA.
Je délègue
Ça m’inquiète
Souveraineté numérique ≠ autonomie cognitive. Sov. = où sont mes données. Autonomie cog. = ai-je accès à la meilleure intelligence frontière ? Fable, c’est un splinternet cognitif.
Ma piste : les specs EARS, CLAUDE.md, Constitution sont la couche d’abstraction qui rend le projet portable d’un modèle à l’autre. Tester avec Mistral/Qwen/Kimi = vérifier que la méthode tient, pas un plan B qui restitue la productivité.
Tout ce qui précède = un seul agent. Deux pistes non industrialisées sur Giac.jl :
1. Subagents (intra-session)
Délégation avec contexte isolé :
Explore, Plan — natifs.claude/agents/Utilisés ponctuellement, pas systématiquement.
2. Multi-agent (inter-session)
Plusieurs claude coordonnés par Git :
Pas testé. Vraie question : coordonner les specs.
Prospective, pas blocage. À tester sur CASSys.jl.
Mon objectif final, qui n’est pas le sujet aujourd’hui :
Vision
Construit sur
Giac.jl est l’infrastructure dont j’avais besoin pour ce projet. Aujourd’hui c’est elle que je vous ai présentée.
ErrorPropagation.jl — origine du projetDeuxième projet, v0.10.0 (préparation de release stable). Repo non publié — hébergé en privé sur ma propre instance Forgejo auto-hébergée, pas même sur GitHub. Pas encore enregistré au General registry.
Inspiration : JuliaPhysics/Measurements.jl#142 — issue ouverte de longue date demandant la propagation symbolique d’incertitudes dans Measurements.jl (qui n’opère qu’en numérique), jamais implémentée.
L’objectif : produire résultat ET incertitude propagée en closed-form, à partir d’expressions purement symboliques Symbolics.jl — pas une approximation Monte-Carlo, pas un calcul numérique sur un point d’opération donné.
Ancrage normatif : implémentation (la plus proche possible) de JCGM 100:2008 (GUM) — Guide to the Expression of Uncertainty in Measurement — la référence métrologique internationale.
ErrorPropagation.jl — capacités du packageCe que fait le package (toutes ces capacités à partir de Symbolics.jl seul, qui est la seule hard-dep) :
uncertainty_budget(m, vars, sigmas) rend la table contribution-par-source (JCGM §5.1.3 + layout EA-4/02 §7.3)Symbolics.CTarget) — formule typographiée pour rapport, fonction C compilable pour firmwareModelingToolkit.jl — propagation à travers une EDO via propagate_odeGiac.jl (weak-dep) — simplification symbolique avancée activée par using Giac (ex. -tan(x) + sin(x)/cos(x) → 0)Cible métier : analyse de sensibilité analytique (GUM §5.1.6), construction du budget métrologique (GUM §5.1.3 / EA-4/02 §7.3), optimisation de protocole de mesure, génération de code embarqué.
ErrorPropagation.jl — Giac.jl en dépendance faibleStatut technique :
Symbolics.jlGiac.jl dans le fichier ext/ErrorPropagationGiacExt.jl, activée automatiquement quand Giac est chargéTestItemRunner.jlCe que ça veut dire : Giac.jl est utilisé comme dépendance optionnelle (weak-dep) par un autre package, qui l’active pour simplifier symboliquement les expressions d’incertitude. La méthodo SDD se transpose d’un domaine (CAS) à un autre (métrologie) sans rien changer au flux.
Mettre des outils scientifiques en open-source ne supprime pas les questions juridiques — ça les déplace.
1. Copyleft GPL-3+ (effet héréditaire) — toute la chaîne (Giac, fork, wrapper, JLLs, Giac.jl) est sous GPL-3-or-later. Pour un laboratoire CNRS pur recherche, transparent ; pour une thèse CIFRE ou un partenariat industriel produisant du code propriétaire, le copyleft se propage à tout code dérivé — et ça change la donne.
2. Code écrit avec un agent IA — provenance et copyright — qui est l’auteur, vis-à-vis de la GPL ? Anthropic ? Moi ? La jurisprudence est (il me semble) inexistante. Je co-signe certaines specs « S. Celles / Claude » en clair — ce n’est pas une assurance juridique, c’est de la transparence sur la provenance.
Les deux points ci-dessus relèvent de qui détient quoi sur le code — la première préoccupation, me semble-t-il, pour un auditoire académique amené à signer des conventions ou à publier.
3. Le disclaimer métrologie d’ErrorPropagation.jl — « paraphrase la méthodologie de JCGM 100:2008 sans être GUM-conformant au sens accréditation ». Sans validation logicielle indépendante (ISO/IEC 17025 §6.4.7), aucun usage en certificat d’étalonnage ni en contexte safety-critical. Ce n’est pas une formalité, c’est un risque réel pour qui couperait le coin.
4. Mainteneur unique — je suis seul à maintenir le fork Giac, le wrapper, les 2 JLLs et Giac.jl. Si je m’arrête demain, la chaîne s’arrête avec moi. Le build in public (tout est sur GitHub depuis le commit 1) réduit le risque — quelqu’un peut reprendre la suite — sans le supprimer.
Aucun de ces quatre points n’est un deal-breaker en recherche académique. Les passer sous silence dans une présentation publique serait malhonnête — d’où ces deux slides.
Côté Giac :
Côté écosystème Julia :
Côté méthodologie :
Et bien sûr : Pierre Navaro (@pnavaro) et le Groupe Calcul CNRS pour cette invitation.
🎤 Slides + sources de cette présentation : github.com/s-celles/Giac.jl_cafe_julia_grp_calcul_cnrs
✉️ sebastien.celles@univ-poitiers.fr

Toutes les briques sont publiques sur GitHub. Toutes les contributions bienvenues.
Giac.jl n’est pas un cas isolé : sur décembre 2025 → juillet 2026, la même méthode (specs EARS, CLAUDE.md, TDD, build in public) a produit une série de projets dans des domaines très différents — calcul formel, sérialisation, géométrie dynamique, théorie des nombres, infrastructure as code, reverse engineering matériel…
Tour d’horizon — pour chaque projet : un résumé succinct, le dépôt et, quand il existe, le lien de documentation ou de l’application.
Symbolics.jl. 📦 github.com/s-celles/MathJSON.jl · 📚 s-celles.github.io/MathJSON.jlPluto.jl, bâti sur MathJSON. 📦 github.com/s-celles/PlutoMathInput.jltlienart/JSXGraph.jl, désormais sous l’org JuliaJSXGraph. 📦 github.com/JuliaJSXGraph/JSXGraph.jl · 📚 juliajsxgraph.github.io/JSXGraph.jlPARI_jll. 📦 github.com/s-celles/LibPARI.jl · 📚 s-celles.github.io/LibPARI.jl047d:00f2, puce Synaptics) → pilote natif libfprint sans blob propriétaire (modèle goodixmoc) : session TLS-1.2-like, pipeline de capture et match on-chip (0x99) validés runtime sur matériel réel. Driver pas encore mergé upstream. ⚙️ Méthode différente du SDD : 2 agents en tandem — l’un sous Windows (capture/analyse du pilote de référence), l’autre sous Linux NixOS (implémentation et tests du driver natif). 📝 IT : libfprint #590 · write-up dans gist s-celles · DT : gist s-cellesOndrejSlamecka/Capnp.jl. 📦 github.com/s-celles/Capnp.jl (branche 004-capnp-latest)gRPCServer.jl. 📦 github.com/s-celles/Pulumi.jlOTwin.jl = jumeau cyber-physique (simulation de procédé + détection d’attaque) en Julia/SciML. Noms de travail provisoires. 📦 github.com/s-celles/OTwin.jl - à l’état d’ébaucheMême flux SDD, du calcul symbolique à la cybersécurité OT en passant par le reverse engineering matériel : la méthode se transpose d’un domaine à l’autre sans rien changer.

Café Julia CNRS · Giac.jl · Sébastien Celles