Que fait c4 ?
C4 répond à "qu'est-ce que ce système, de quoi est-il fait, et qui appelle qui"
à trois niveaux de zoom. La surface est le moteur visualize : un canevas rataflow,
une section décrite par niveau, des boîtes containers pour la propriété. Lisez
agents/.agents/skills/visualize/SKILL.md pour le rendu lui-même — cette
compétence ne couvre que ce que C4 ajoute.
C4 ajoute trois choses que visualize ne fait pas :
- Une échelle de niveaux. Contexte L0, puis conteneurs L1/L1b, puis composants L2/L2b. Chaque niveau existe pour motiver le suivant, et le niveau où se trouve le lecteur est visible depuis la boîte, pas depuis une légende.
- La collecte de faits. C4 doit être complet sur qui appelle quoi et pourquoi. Les scouts lisent la base de code et produisent un inventaire d'éléments et de relations soutenu par des preuves avant que toute spécification n'existe.
- Le confinement comme une affirmation. Une boîte conteneur contient les propres parties du composant et rien d'autre. C'est une affirmation de propriété, donc elle a besoin de sources, et elle doit survivre à la vérification de la boîte par le lecteur contre le code.
N'inventez pas un élément, une relation ou une limite. Si les scouts ne peuvent pas le citer, il n'est pas dessiné.
Routage
| Demande | Utilisation |
|---|---|
| Niveaux C4 d'un composant dans une base de code réelle | cette compétence |
| Un graphe de relations à partir de faits que vous possédez déjà | visualize |
| Diagramme dans la réponse du chat | outil mermaid |
| ANSI / PNG statique | compétence mermaid |
| Page prose d'architecture ou deck | compétence artifact |
/c4 <composant> ou "donne-moi une vue C4 de X" est l'invocation. La compétence
fonctionne dans un dépôt qu'elle n'a jamais vu : les scouts sont le seul moyen
pour elle d'apprendre quoi que ce soit, donc rien ici ne dépend d'une connaissance
préalable du code.
Étape 0 — Épingler le composant
Indiquez, avant que tout scout ne s'exécute :
composant : gestionnaire de machines
racine dep : /Users/aviral/modal
ancre : machine_manager/ (un paquet, un déployable)
- Composant est une chose qu'un lecteur peut pointer dans le code : un service, un paquet, une unité déployable. Si la demande nomme un sous-système qui couvre plusieurs déployables, choisissez le déployable et dites quels voisins il attire comme externes.
- Racine dep est le point de départ des scouts. Confirmez que le checkout existe ; ne supposez pas un chemin.
- Ancre est la preuve unique la plus forte que le composant est une seule chose
(manifeste de paquet, chart Helm, module Terraform, service proto,
main.go). Si vous ne pouvez pas en nommer une, le composant est probablement deux composants — demandez à l'humain plutôt que de deviner.
Posez au plus une question de clarification (la limite du composant est la seule qui vaut la peine d'être posée). Sinon, décidez et enregistrez.
Étape 1 — Lancer les scouts
Quatre invites se trouvent dans prompts/ ; chacune est une tâche complète de
sous-agent. Lancez-les comme des appels subagent avec agent: "scout" (reconnaissance
en lecture seule — les scouts ne doivent pas modifier le dépôt), toutes les quatre en
un seul tour pour qu'elles s'exécutent en parallèle, cwd: la racine du dépôt résolue
à l'étape 0, et le nom du composant substitué à <composant> :
| Scout | Invite | Retourne |
|---|---|---|
| Scope | prompts/scope.md | inventaire des éléments : id, rôle en une ligne, points d'entrée, déployables, magasins, services externes, acteurs, chemins de fichiers |
| Relationships | prompts/relationships.md | lignes de → vers : verbe + transport, chacune avec fichier:ligne |
| Boundary | prompts/boundary.md | membres vs externes + la limite de module/déploiement qui justifie chaque appartenance |
| Levels | prompts/levels.md | affectation de niveau par élément, et quels niveaux doivent se diviser en L1/L1b (L2/L2b) |
Chaque scout retourne une preuve avec des références fichier:ligne. Une ligne sans
référence est une rumeur ; supprimez-la ou redemandez à ce scout une source.
Lancez les scouts depuis la racine du dépôt — exécutez la session là-bas (ou faites
en sorte que cwd: soit le même répertoire où se trouve déjà la session). Un scout
dont le cwd diffère de celui de la session de lancement peut mourir avant que son
processus ne démarre, et l'échec se lit comme "aucun processus pi n'est apparu dans
son panneau".
Ne sondez pas les sous-agents. Les résultats arrivent d'eux-mêmes ; continuez lorsque les quatre sont de retour. Si l'un échoue, dites lequel, et soit réessayez, soit enregistrez l'écart dans l'enregistrement de génération — ne comblez jamais le trou à partir de la mémoire.
Étape 2 — Reconcilier en un seul registre
Fusionnez les quatre retours en trois tables dans l'enregistrement de génération. C'est toute la base de faits ; la spécification ne peut contenir rien qui ne soit pas ici.
elements : id | étiquette | rôle | classe (acteur|externe|membre|magasin) | preuve
relations : id | de → vers | verbe + transport | niveau | preuve
limite : id | dedans/dehors | limite justificative | preuve
- Les conflits sont résolus en citant la source la plus forte (un manifeste ou un graph de déploiement bat un commentaire ; un site d'appel bat une docstring), et la réclamation perdante est enregistrée, pas supprimée.
- Les conflits non résolus et les relations sans source survivante sont enregistrés comme des questions ouvertes. Le silence est un défaut ; un doute visible ne l'est pas.
- Gardez un identifiant par élément et réutilisez-le entre les niveaux, de sorte qu'un
lecteur qui suit
q → regvoie les deux mêmes boîtes à L1 et L2.
Étape 3 — Que dessiner (priorisation)
Une vue C4 est un jugement sur l'importance, pas un vidage du graphe d'appels. Le registre contient toutes les relations qu'un scout a trouvées ; le diagramme contient celles dont un lecteur a besoin, et dit à voix haute ce qu'il a omis.
Une relation gagne sa place lorsqu'elle satisfait au moins un de ces critères :
| id | test | exemple |
|---|---|---|
| I1 | elle traverse la limite du composant (membre ↔ externe, externe ↔ externe) | webapi → API Latitude, machines → sshx.io |
| I2 | c'est un chemin de requête, de contrôle ou de données principal que la source mentionne (provision, battement de cœur, audit, réconciliation, démarrage) | plan de contrôle → gRPC privé, file River → workers |
| I3 | elle porte un protocole ou un qualificatif dont le lecteur a besoin : verbe RPC, transport, port, seau, intervalle, délai d'attente | register + battement de cœur toutes les 1 min, sync gcore toutes les 1 min |
| I4 | elle explique un magasin de données ou un acteur que le lecteur ne peut pas déduire des boîtes | magasin → Postgres, ops + mmctl → webapi |
| I5 | c'est une branche d'un chemin redondant ou parallèle qui doit rester comptable | machines → boot et boot → machines ; deux tirages indépendants |
Classes de suppression — une relation ne quitte le diagramme que comme l'un de ceux-ci :
| id | classe | ce que c'est |
|---|---|---|
| C1 | détail interne | entièrement à l'intérieur d'un conteneur, n'ajoute ni limite, ni protocole, ni information d'acteur (worker → magasin ligne par requête lorsque les rédacteurs du magasin sont déjà dessinés) |
| C2 | site d'appel en double | le même saut qu'une relation dessinée, ne différant que par le type de travail ou l'argument, que l'étiquette dessinée porte déjà |
| C3 | assistant | un élément dont le seul travail est de rentrer dans le même conteneur (un wrapper client entre deux boîtes dessinées) |
| C4 | branche redondante | une autre branche parallèle d'un chemin dont la comptabilité n'est pas nécessaire au lecteur (un deuxième artefact du même seau, même transport) |
Règles qui maintiennent le jugement honnête :
- Une flèche peut porter plusieurs sites d'appel lorsque l'étiquette porte leurs
verbes (
créer / terminer / réparer). C'est un pliage C2, pas une suppression. - Le franchissement de limite gagne un match nul (I1 avant I3 avant I2). Un diagramme qui laisse tomber un saut externe pour faire de la place à un interne a inversé ses propres priorités.
- En cas de doute, dessinez-le, et laissez la description du niveau dire pourquoi
il est là. Le doute n'est pas une raison de suppression ;
C1–C4le sont. - Ne supprimez jamais en silence. Chaque ligne du registre se termine comme : dessiné,
plié dans <relation dessinée>(avec le pli justifié comme C2), ouomis — C1|C2|C3|C4avec une ligne expliquant pourquoi. La carte de couverture (Étape 7) indique les quatre comptes : dessiné, plié, omis par classe, non comptabilisé. - La réduction de périmètre est une chose différente et a besoin des mots de l'appelant.
"Seulement la vue du plan de contrôle" ou "20 relations max" est une demande de
périmètre : enregistrez-la comme base (
base: périmètre de l'appelant, N relations) à côté du registre compté. La priorisation est la valeur par défaut ; un sous-ensemble est une décision que l'appelant prend, pas une que le générateur déduit.
Étape 4 — L'échelle de niveaux
La forme cible est de cinq sections, de haut en bas :
L0 · Contexte système un nœud pour le composant + ses voisins
L1 · Le composant le composant ouvert une fois, conteneur sur ses parties
L1b · <deuxième côté> la même profondeur, l'autre côté du système
L2 · À l'intérieur — <tranche A> relations entre membres, conteneur sur elles
L2b · À l'intérieur — <tranche B> la même profondeur, l'autre tranche
Voilà l'échelle : un canevas de cinq sections est le livrable. Plus de sections est un défaut à moins que la discipline de fractionnement ci-dessous soit satisfaite, avec des nombres.
Dessinez chaque niveau seulement après que celui au-dessus existe, et donnez à chacun sa description avant de passer au suivant.
| Niveau | Dessine | Comportement de la boîte |
|---|---|---|
| L0 contexte système | le composant comme UN nœud simple, plus chaque acteur et système externe autour, plus les flèches grossières entre eux | pas de conteneur, pas de focus, pas de double bordure — le but est le monde environnant, donc le composant ressemble à n'importe quelle autre boîte |
| L1 conteneurs | le composant ouvert d'un niveau : ses propres unités déployables/exécutables à l'intérieur d'un conteneur ; les externes de L0 restent à l'extérieur | boîte conteneur sur les membres |
| L1b vue latérale | la deuxième tranche optionnelle lorsqu'un seul L1 ne peut vraiment pas contenir le flux (l'extrémité de la flotte, le plan de données, le chemin par lots) — mesurée, jamais un nouveau sujet | conteneur, mêmes règles |
| L2 composants | les relations à l'intérieur d'un conteneur — les membres de L1, s'appelant maintenant les uns les autres | boîte conteneur autour d'eux |
| L2b | deuxième tranche optionnelle de la même profondeur lorsqu'un L2 ne peut pas acheminer ses relations, ou que deux histoires ont besoin de rangs séparés | conteneur, mêmes règles |
Règles de niveau qui ne sont pas négociables :
- L0 est le composant comme UN nœud simple. Une boîte pour tout le composant,
sa bordure normale, plus les acteurs et systèmes externes autour. Pas ses parties,
pas ses écouteurs, pas ses tables — ceux-ci sont le niveau suivant. Si L0 a plus
d'un nœud qui appartient à l'intérieur du composant, ce n'est pas une vue de
contexte. Le confinement à L0 prétendrait à des internes que le lecteur n'a pas
encore vus, et
focuspointerait vers un niveau qui n'existe pas encore. - L1/L1b ouvrent la boîte de L0. Chaque membre se trouve à l'intérieur du conteneur ; chaque élément qui était externe à L0 se trouve à l'extérieur. Un membre qui ne peut pas être dessiné en dehors de la boîte englobante obtient sa propre entrée de conteneur (voir Contiguïté) — jamais une boîte bricolée.
- L2/L2b restent à l'intérieur d'une boîte. Les deux extrémités de chaque relation L2 sont des membres du conteneur. Si une relation quitte la boîte, elle appartient à L1/L1b.
- Un niveau se décrit lui-même. Le titre est court (
L0 · Contexte système) ; la description indique ce qui est à l'intérieur de la boîte, ce qui est à l'extérieur, et comment il se rapporte au niveau voisin. Visez 2–3 lignes encapsulées. - Le mouvement n'est pas une propriété C4. Les niveaux 0–1b sont statiques
(
points: 0). Un niveau interne dont les relations sont des flux en direct peut porter exactement un point par route (flux: oui, points: 1) ; enregistrez le choix. N'animez jamais une flèche externe pour décorer l'échelle.
Discipline de fractionnement — un niveau est une PROFONDEUR, pas un sujet
Un niveau C4 répond à une question à un zoom. Deux niveaux de la même profondeur n'existent que parce qu'une section ne pouvait pas contenir le contenu ; un nouveau sujet n'est pas une raison pour ajouter une section.
- N'ajoutez jamais L1c, L1d, L1e ou un cinquième côté. L'échelle a trois profondeurs (contexte, conteneurs, composants) et au plus deux tranches à chaque profondeur en dessous de la première. Un canevas qui ajoute une tranche par sous-système ou par mention est une liste de sujets portant des titres de niveau.
- Une deuxième tranche doit nommer sa mesure : les routes que
--checkrefuse, le couloir partagé qu'elle signale, ou les rangs qui se heurtent (deux extrémités externes sans externe commun). "C'était bondé" n'en est pas une. - Les tranches de même profondeur partagent la boîte, les étiquettes et les identifiants. L1 et L1b dessinent le même composant avec les mêmes identifiants de membres ; L2 et L2b dessinent les mêmes membres. Seuls l'ensemble des arêtes et les rangs diffèrent, et chaque description dit de quelle tranche il s'agit.
- Une tranche est nommée
b(l1b,l2b) ; n'inventez jamais une profondeur. - La profondeur au-delà de L2 n'est pas dessinée. Si les internes des composants se décomposent encore, dites-le dans l'enregistrement et arrêtez : les classes de code et les fonctions ne sont pas un niveau C4 qu'un lecteur a demandé. L'échelle se termine là où la demande se termine.
- Tout ce que le registre contient et que l'échelle ne dessine pas est une décision de périmètre, écrite (Étape 6), pas une suppression silencieuse et pas une nouvelle section.
Étape 5 — Mécanique de spécification
Un canevas, une direction de lecture, une section par niveau :
{
"direction": "lr",
"theme": "gruvbox",
"diagrams": [
{
"id": "l0",
"title": "L0 · Contexte système",
"description": "Contexte système : le gestionnaire est une boîte. ...",
"nodes": [ ... ],
"edges": [ ... ]
},
{
"id": "l1",
"title": "L1 · Le gestionnaire",
"description": "La boîte de L0 ouverte d'un niveau : ...",
"containers": [ { "label": "Gestionnaire de machines", "members": ["api", "q", "db"] } ],
"layers": [ ["actor"], ["api", "q"], ["db"], ["prov"] ],
"nodes": [ ... ],
"edges": [ ... ]
}
]
}
direction: "lr"une fois, sur le canevas. Jamais de remplacement par diagramme : un lecteur qui se réoriente à chaque section a perdu l'échelle.- Cinq sections.
l0,l1,l1b,l2,l2b, dans cet ordre. Une sixième section nécessite que la mesure de l'étape 3 soit enregistrée ; un canevas de dix ou vingt sections est une liste de sujets, pas une échelle — repliez-le à trois profondeurs, deux tranches chacune. containersest une liste par diagramme de{label, members}. La boîte est un vrai rectangle à double bordure qui grandit autour des membres, avec l'étiquette placée dans son bord supérieur. Plusieurs boîtes par diagramme sont autorisées.- Les identifiants de membres doivent exister dans les
nodesde ce diagramme. Un identifiant de membre qui ne nomme aucun nœud est silencieusement ignoré par l'analyseur — la boîte se dessine alors autour de moins de nœuds que vous n'avez déclarés et--checkn'avertit pas. Comptez la boîte dessinée par rapport aux membres que vous avez déclarés. - Règle de contiguïté. Un conteneur est un rect ...
[Le reste du contenu original a été tronqué pour la traduction ; la compétence complète continue avec les étapes suivantes.]