Communityコーディング&開発github.com

aviralmansingka/c4

C4アーキテクチャビュー(システムコンテキスト、コンテナ、コンポーネントレベル)を、スカウトサブエージェントを用いてコードベースから収集します。visualizeキャンバス上に、コンテナボックスとしてレベルラダー(L0システムコンテキスト、L1/L1bコンテナ、L2/L2bコンポーネント)を構築し、外部システムは各ボックスの外側に配置し、レベルごとに1つの説明をラップします。

c4 とは?

このスキルは、コードベースを調査するスカウトサブエージェントを使用して、実際のコードからC4アーキテクチャ図を生成します。システムコンテキスト(L0)、コンテナ(L1/L1b)、コンポーネント(L2/L2b)の3つのズームレベルを、`visualize`エンジンの単一のrataflowキャンバス上に、レベルラダーとして構築します。各レベルは、所有権を示すコンテナボックス内に要素を描画し、外部システムはボックス外に配置されます。このスキルはClaude Code、Cursor、CodexなどのAIエージェントと連携し、サブエージェントを並行して起動してスコープ、関係性、境界、レベルに関する事実をファイル行参照付きで収集し、それらを調整してC4図を生成します。このスキルは知らないリポジトリでも動作し、スカウトが参照できない要素や関係は決して発明せず、図から省略する場合は常にその理由を記録します。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aviralmansingka/dotfiles/tree/HEAD/agents/.agents/skills/c4

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

c4 は何をしますか?

C4 は、「このシステムは何か、何でできているか、誰が誰を呼ぶか」を3段階のズームで答え ます。表面は visualize エンジンです。1つのrataflowキャンバス、 レベルごとに1つの説明セクション、所有権を示す containers ボックス。 レンダラー自体については agents/.agents/skills/visualize/SKILL.md を読んでください — このスキルはC4が追加するものだけをカバーします。

C4 は visualize が行わない3つのことを追加します:

  1. レベルラダー。 L0コンテキスト、次にL1/L1bコンテナ、次にL2/L2b コンポーネント。各レベルは次のレベルを動機付けるために存在し、読者がいる レベルは凡例ではなくボックスから見えるようになっています。
  2. 事実収集。 C4は 誰が何をなぜ呼ぶか について完全でなければなりません。 スカウトはコードベースを読み、仕様が存在する前に証拠に基づいた要素と 関係のインベントリを作成します。
  3. 包含関係を主張として。 コンテナボックスは、コンポーネント自身のパーツのみを保持し、 それ以外は保持しません。これは所有権の主張であるため、ソースが必要であり、 読者がコードに対してボックスをチェックしても耐えられるものでなければなりません。

要素、関係、または境界を発明しないでください。スカウトがそれを引用できない場合、 描画されてはなりません。

ルーティング

質問使用するもの
実際のコードベースにおけるコンポーネントのC4レベルこのスキル
すでに持っている事実から1つの関係グラフvisualize
チャット返信内の図mermaid ツール
静的ANSI / PNGmermaid スキル
アーキテクチャの 散文 ページまたはデッキartifact スキル

/c4 <コンポーネント> または「XのC4ビューをくれ」が呼び出し方です。このスキルは これまで見たことのないリポジトリでも動作します。スカウトだけが唯一の学習手段であり、 ここではコードに関する事前知識に依存するものはありません。

ステップ0 — コンポーネントを特定する

スカウトを実行する前に、以下を記述します:

component: machine manager
repo root: /Users/aviral/modal
anchor:    machine_manager/  (1つのパッケージ、1つのデプロイ可能単位)
  • コンポーネント とは、読者がコード内で指し示せる1つのものです: サービス、 パッケージ、1つのデプロイ可能単位。リクエストが複数のデプロイ可能単位にまたがる サブシステムを指定する場合は、1つのデプロイ可能単位を選び、どの隣接要素を 外部として取り込むかを述べてください。
  • リポジトリルート はスカウトの開始場所です。チェックアウトが存在することを確認し、 パスを推測しないでください。
  • アンカー は、コンポーネントが1つのものであるという最も強力な単一の証拠です (パッケージマニフェスト、Helmチャート、Terraformモジュール、protobufサービス、main.go)。 1つ特定できない場合、コンポーネントはおそらく2つのコンポーネントです — 推測する代わりに人間に尋ねてください。

明確化の質問は最大1つだけしてください (コンポーネント境界が唯一尋ねる価値があるものです)。 それ以外は決定して記録してください。

ステップ1 — スカウトを起動する

4つのプロンプトが prompts/ にあります。それぞれが完全なサブエージェントタスクです。 agent: "scout" を指定した subagent 呼び出しとして起動します (読み取り専用の偵察 — スカウトはリポジトリを編集してはいけません)。すべてを1ターンで起動して並行実行させ、 cwd: はステップ0で解決されたリポジトリルートとし、コンポーネント名を <component> に 置き換えます:

スカウトプロンプト戻り値
スコープprompts/scope.md要素インベントリ: id、1行の役割、エントリポイント、デプロイ可能単位、ストア、外部サービス、アクター、ファイルパス
関係prompts/relationships.mdfrom → to : 動詞 + トランスポート 行、それぞれに file:line
境界prompts/boundary.mdメンバー vs 外部 + 各メンバーシップを正当化するモジュール/デプロイメント境界
レベルprompts/levels.md要素ごとのレベル割り当て、およびどのレベルをL1/L1b (L2/L2b) に分割しなければならないか

すべてのスカウトは file:line 参照付きの証拠 を返します。参照がない行は 噂です。削除するか、そのスカウトにソースを再要求してください。

リポジトリルートからスカウトを起動してください — そこでセッションを実行するか (すでにセッションがあるディレクトリと同じ cwd: にしてください)。cwd が 起動元のセッションと異なるスカウトは、プロセスが開始される前に終了する可能性があり、 その失敗は「ペインにpiプロセスが現れなかった」と読み取られます。

サブエージェントをポーリングしないでください。結果は自動的に到着します。 4つすべてが戻ってきたら続行してください。1つが失敗した場合は、どれかを述べ、 再試行するか、生成レコードにギャップを記録してください — メモリから穴を埋めることは 絶対にしないでください。

ステップ2 — 1つの台帳に統合する

4つの戻り値を、生成レコード内の3つのテーブルに統合します。これが 事実ベース全体です。仕様はこれに含まれていないものを含んではいけません。

elements:   id | label | role | class (actor|external|member|store) | evidence
relations:  id | from → to | verb + transport | level | evidence
boundary:   id | inside/outside | justifying boundary | evidence
  • 競合 は、より強力なソース (マニフェストやデプロイメントチャートはコメントに勝る; 呼び出しサイトはドキュメント文字列に勝る) を引用することで解決され、勝った主張が 記録され、負けた主張は削除されずに記録されます。
  • 未解決の競合 と、残存するソースのない関係は、未解決の質問として記録されます。 沈黙は欠陥です。目に見える疑問は欠陥ではありません。
  • 要素ごとに1つのIDを保持し、レベル間で再利用します。これにより、q → reg を 追跡する読者は、L1とL2で同じ2つのボックスを見ることになります。

ステップ3 — 何を描画するか (優先順位付け)

C4ビューは、重要度に関する判断であり、コールグラフのダンプではありません。 台帳はスカウトが見つけたすべての関係を保持します。図は読者が必要とするものを保持し、 何を省略したかを明確に述べます。

以下の条件の少なくとも1つを満たす場合、関係は描画する価値があります:

IDテスト例
I1コンポーネント境界を越える (メンバー ↔ 外部, 外部 ↔ 外部)webapi → Latitude API, machines → sshx.io
I2ソースが呼び出す主要なリクエスト、制御、またはデータパスである (プロビジョニング、ハートビート、監査、調整、起動)control plane → private gRPC, River queue → workers
I3読者が知る必要のあるプロトコルまたは修飾子を運ぶ: RPC動詞、トランスポート、ポート、バケット、間隔、タイムアウトregister + heartbeat every 1 min, gcore sync every 1 min
I4ボックスから読者が推測できないデータストアまたはアクターを説明するstore → Postgres, ops + mmctl → webapi
I5冗長または並列パスの1つの脚であり、カウント可能にしておく必要があるmachines → boot and boot → machines; 2つの独立したプル

ドロップクラス — 関係が図から削除されるのは、以下のいずれかの場合のみ:

IDクラス説明
C1内部の詳細完全に1つのコンテナ内にあり、境界、プロトコル、またはアクター情報を追加しない (worker → store のクエリごとの行だが、ストアの書き込み元はすでに描画されている)
C2重複呼び出しサイトすでに描画された関係と同じホップで、ジョブタイプまたは引数のみが異なり、描画されたラベルにすでに含まれている
C3ヘルパー唯一の役割が同じコンテナに再入力することである要素 (描画された2つのボックス間のクライアントラッパー)
C4冗長な脚読者がカウント可能性を必要としないパスのさらなる並列の脚 (同じバケット、同じトランスポートからの2つ目のアーティファクト)

判断を正直に保つルール:

  • 1つの矢印が複数の呼び出しサイトを運ぶことがあります、ラベルにその動詞が含まれている場合 (create / terminate / repair)。これはC2の折り畳みであり、削除ではありません。
  • 境界を越えるものが同点の場合に勝ちます (I1 > I3 > I2)。外部ホップを内部のもののためにスペースを空けるために削除する図は、自身の優先順位を逆転させています。
  • 疑わしい場合は描画し、レベルの説明でなぜそこにあるのかを述べてください。疑いは削除理由ではありません。C1–C4 が削除理由です。
  • 決して黙って削除しないでください。 台帳のすべての行は、以下のいずれかになります: 描画済み、<描画された関係> に折り畳まれた (折り畳みがC2として正当化されている)、または 省略 — C1|C2|C3|C4 と理由の1行。カバレッジマップ (ステップ7) は4つのカウントを述べます: 描画済み、折り畳み済み、クラス別に省略、未処理。
  • スコープの縮小は別のことであり、呼び出し元の言葉が必要です。 「コントロールプレーンビューのみ」または「最大20の関係」はスコープの要求です: それを基礎 (basis: caller scope, N relations) としてカウントされた台帳とともに記録してください。優先順位付けはデフォルトです。サブセット は発信者が行う決定であり、生成者が推測するものではありません。

ステップ4 — レベルラダー

目標の形状は、上から下に5つのセクションです:

L0 · System context      コンポーネント+その隣接要素の1つのノード
L1 · The component       コンポーネントが1回開かれ、そのパーツにコンテナ
L1b · <second side>      同じ深さ、システムの反対側
L2 · Inside — <slice A>  メンバー間の関係、それらにコンテナ
L2b · Inside — <slice B> 同じ深さ、もう1つのスライス

これがラダーです: 5セクションのキャンバスが成果物です。以下の分割規則が 数字で満たされていない限り、それ以上のセクションは欠陥です。

各レベルは、その上のレベルが存在した後にのみ描画し、次に進む前に それぞれに説明を与えてください。

レベル描画するものボックスの動作
L0 システムコンテキストコンポーネントを1つのプレーンノードとして、その周りのすべてのアクターと外部システム、およびそれらの間の大まかな矢印コンテナなし、focus なし、二重境界なし — ポイントは周囲の世界なので、コンポーネントは他のボックスと同じように見える
L1 コンテナコンポーネントを1レベル開いたもの: 自身のデプロイ可能/ランタイムユニットが 内部 の1つのコンテナに。L0の外部は外側に留まるメンバーにコンテナボックス
L1b サイドビュー1つのL1が本当にフローを保持できない場合のオプションの2番目のスライス (フリートエンド、データプレーン、バッチパス) — 測定され、決して新しいトピックではないコンテナ、同じルール
L2 コンポーネント内部 の1つのコンテナ内の関係 — L1のメンバーが互いに呼び合うそれらの周りにコンテナボックス
L2b同じ深さのオプションの2番目のスライス。1つのL2がその関係をルーティングできない場合、または2つのストーリーが別々のランクを必要とする場合コンテナ、同じルール

交渉不可のレベルルール:

  • L0はコンポーネントを1つのプレーンノードとして表現します。 コンポーネント全体に対して1つのボックス、通常の境界、およびその周りのアクターと外部システム。パーツ、リスナー、テーブルではありません — これらは 次の レベルです。L0にコンポーネント内部に属するノードが複数ある場合、それはコンテキストビューではありません。L0での包含関係は、読者にまだ示されていない内部を主張することになり、focus はまだ存在しないレベルを指すことになります。
  • L1/L1b は L0 のボックスを開きます。 すべてのメンバーはコンテナの内側に座ります。L0で外部だったすべての要素はその外側に座ります。ボックスの境界ボックスの外側に描画できないメンバーは、独自のコンテナエントリを取得します (連続性ルールを参照) — 決してごまかしのものではありません。
  • L2/L2b は1つのボックス内に留まります。 L2のすべての関係の両方のエンドポイントはコンテナメンバーです。関係がボックスを出る場合、それはL1/L1bに属します。
  • レベルはそれ自身を説明します。 タイトルは短く (L0 · System context)。説明は、ボックスの中身、ボックスの外側、および隣接レベルとの関係を述べます。2〜3行のラップを目指してください。
  • モーションはC4のプロパティではありません。 レベル0〜1bは静的です (dots: 0)。関係がライブフローである内部レベルは、ルートごとに最大1つのドットを持つことができます (flow: true, dots: 1)。その選択を記録してください。ラダーを飾るために外部の矢印をアニメーション化しないでください。

分割規則 — レベルは深さであり、トピックではありません

C4レベルは、1つのズームで1つの質問に答えます。同じ深さの2つのレベルが存在するのは、 1つのセクションが内容を保持できなかった場合のみです。新しい トピック はセクションを 追加する理由にはなりません。

  • L1c、L1d、L1e、または5番目の サイド を決して追加しないでください。 ラダーには3つの 深さ (コンテキスト、コンテナ、コンポーネント) があり、最初のレベルより下の各深さには 最大2つのスライスがあります。サブシステムごとまたは言及ごとにスライスが増えるキャンバスは、 レベルタイトルをまとったトピックリストです。
  • 2番目のスライスは、その測定値を指定する必要があります: --check が拒否するルート、 報告する共有コリドー、または衝突するランク (共通の外部を持たない2つの外部エンド)。 「混雑していた」は測定値ではありません。
  • 同じ深さのスライスは、ボックス、ラベル、およびIDを共有します。 L1とL1bは同じコンポーネントを 同じメンバーIDで描画します。L2とL2bは同じメンバーを描画します。エッジセットとランクのみが異なり、 各説明はどちらのスライスかを述べます。
  • スライスは b と名付けます (l1b、l2b)。深さを発明しないでください。
  • L2を超える深さは描画しません。 コンポーネント内部がさらに分解される場合、レコードにそう述べて 停止してください。コードクラスと関数は、読者が求めたC4レベルではありません。 ラダーはリクエストが終わるところで終わります。
  • 台帳が保持し、ラダーが描画しないすべてのものは、書き留められたスコープ決定です (ステップ6)、 黙った削除でも新しいセクションでもありません。

ステップ5 — 仕様の仕組み

1つのキャンバス、1つの読み取り方向、レベルごとに1つのセクション:

{
  "direction": "lr",
  "theme": "gruvbox",
  "diagrams": [
    {
      "id": "l0",
      "title": "L0 · System context",
      "description": "System context: the manager is one box. ...",
      "nodes": [ ... ],
      "edges": [ ... ]
    },
    {
      "id": "l1",
      "title": "L1 · The manager",
      "description": "L0's box opened one level: ...",
      "containers": [ { "label": "Machine manager", "members": ["api", "q", "db"] } ],
      "layers": [ ["actor"], ["api", "q"], ["db"], ["prov"] ],
      "nodes": [ ... ],
      "edges": [ ... ]
    }
  ]
}
  • direction: "lr" は1回のみ、キャンバス上で。図ごとのオーバーライドは禁止: すべてのセクションで向きを変える読者はラダーを見失います。
  • 5つのセクション。 l0、l1、l1b、l2、l2b、この順序で。6番目の セクションにはステップ3の測定値を記録する必要があります。10または20の セクションを持つキャンバスはトピックリストであり、ラダーではありません — 3つの深さ 各2スライスに折り畳み直してください。
  • containers は図ごとのリスト であり、{label, members} 形式です。ボックスは メンバーの周りに描かれた実際の二重境界の長方形であり、ラベルがその上端に 設定されます。図ごとに複数のボックスが許可されます。
  • メンバーIDはその図の nodes に存在しなければなりません。 ノードを指定しないメンバーIDは パーサーによって黙って無視されます — その場合、ボックスは宣言よりも少ないノードの周りに 描かれ、--check は警告しません。宣言したメンバーに対して描画されたボックスを数えてください。
  • 連続性ルール。 コンテナは長方形です

...(以降、元の英語の内容が続きます。実際の翻訳では全面翻訳が必要ですが、この例では簡略化のため途中で切っています。)

関連スキル