mcpField
E
engineering-council

engineering-council

github.com/belsrc開発075 回インストール5 週間前に更新
リポジトリ

engineering-council について

engineering-councilは、エンジニアリング上の決定を検討するチーム向けのスキルです。エンジニアのペルソナを決定論的に選び、隔離されたサブエージェントから独立した見解を集め、合意を強制せずに一致点と相違点を整理します。

主な機能

  1. 01トピックとペルソナの決定論的ルーティング
  2. 02デュオとトライアドのモード
  3. 03隔離されたリポジトリ調査サブエージェント
  4. 04独立したペルソナ回答
  5. 05任意の反論ラウンド
  6. 06相違点を残す統合

活用シーン

  1. 01エンジニアリング上の選択肢を異なる視点で比較
  2. 02リポジトリの状況を踏まえた議論
  3. 03複数分野にまたがる決定の検討

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/belsrc/skills --skill engineering-council

インストール後、Claude Code で/engineering-councilを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
council.md
references/personas/
abramov.md
allspaw.md
beck.md
carmack.md
chase.md
chollet.md
dhh.md
dijkstra.md
fowler.md
green.md
hejlsberg.md
hickey.md
husain.md
kaminsky.md
karpathy.md
kay.md
khattab.md
knuth.md
lamport.md
lecun.md
majors.md
marlinspike.md
martin.md
muratori.md
ng.md
ousterhout.md
pearl.md
pike.md

詳細

Engineering Council

少人数のエンジニアリング担当者で構成される評議会を招集し、それぞれから独立した見解を得てから、一致点と現在も続く意見の相違をまとめます。対立しながらも妥当な見解の間にある緊張関係に価値があるため、無理に合意を形成することは決して目指しません。目指すのは、意思決定の選択肢をわかりやすく示すことです。

仕組み

ルーティングは決定論的で、テキストのみで行います。ディスカバリーと評議会は、分離されたサブエージェントによる2段階のマップ・リデュース処理です。

request
   │
   ▼
[route]  Layer 1 classify ──► topic ──► Layer 2 lookup ──► members      deterministic
   │
   ▼
[discover]  map: 1..n discovery subagents read files in their own context
            reduce: merge findings into one lean RepoBrief                grounds the council
   │
   ▼
[echo]  selection + brief + files read                                   audit gate
   │
   ▼
[council]  map: one persona subagent per member, given the RepoBrief     round one, uncorrelated
   │
   ▼
[debate]  optional: re-spawn each member with the others' round-one      round two, opt-in
          positions; one rebuttal pass, capped at two rounds
   │
   ▼
[synthesize]  positions, agreements, splits, and movement if debated

ルーティングは、監査可能性を保つために2つのレイヤーで行います。レイヤー2はreferences/routing.mdにある直接的な照合です。トピックとモードの組み合わせごとに、常に同じメンバーセットが選ばれます。レイヤー1ではトピックを選びます。一般的な経路で再現性を保つため、まずキーワードを照合し、該当するアンカーワードがない場合に限り、モデルが閉じたトピック列挙から推論します。

どちらのファンアウトにも、意図的に分離されたサブエージェントを使用します。ディスカバリーでは、生のファイル内容はディスカバリー用サブエージェント内でのみ扱われるため、オーケストレーターに届くのは要約された調査結果だけであり、評議会がファイルのダンプを保持することはありません。評議会では、各ペルソナが自身の定義、依頼内容、共有ブリーフに基づくクリーンなコンテキストから推論します。これにより、互いの意見を読んで収束したペルソナではなく、相関のない見解が得られます。デフォルトでは討論ラウンドが有効で、反論を1回追加するため、評議会のコストはおよそ2倍になります。単一ラウンドでの見解を希望する場合は無効にしてください。また、第1ラウンドで全員一致した場合は自動的に省略されます。

ステップ1:モードを決める

デフォルトはduo(対極にある2人による弁証法的なペア)です。ユーザーが希望する場合、3人の名前を挙げた場合、または依頼が明らかに複数の領域にまたがる場合は、triad(3人による多角的な視点)を使用します。

duoは単に「2人のメンバー」という意味ではありません。依頼の中心的な緊張関係を挟む軸を持つ、対極にあるペアです。triadは、関連する異なる領域をカバーするように選ばれた3人です。したがって、モードによって変わるのは人数だけではなく、選ばれるメンバーです。

討論ラウンドがオプトアウトされているかどうかも検出します。討論はモードとは独立したフラグです。duoとtriadのどちらでも機能し、デフォルトで有効です。反論のラウンドは、最初の反応を並べるだけでなく、熟慮した見解に評議会が至るための重要な要素だからです。無効にするのは、ユーザーが「討論なし」「単一ラウンド」「見解だけ」「反論を省略」と依頼した場合に限ります。このフラグはステップ6を制御します。

ステップ2:メンバーを選ぶ

以下の優先順位に従って選択します。最初に該当した分岐を採用します。どの分岐に該当したかをroutedByとして記録します。

  1. 明示的なメンバー(routedBy: "explicit")。ユーザーが具体的なメンバーを指定した場合(「KnuthとBeckに聞いて」「今回はCarmack対Muratori」など)。指定されたメンバーだけを使用します。分類は一切行いません。完全に決定論的です。
  2. 明示的なトピック(routedBy: "explicit")。ユーザーがトピックキーを指定した場合(「最適化判断の評議会を実行して」など)。references/routing.mdで照合します。完全に決定論的です。
  3. キーワードルート(routedBy: "keyword")。依頼文を確認し、references/routing.mdにあるトピックごとのアンカーワードと照合します。アンカーワードの一致数で各トピックを採点し、最も高いトピックを選びます。同点の場合は、routing.mdの末尾にある優先順位に従います(より具体的なトピックを優先)。これが想定される一般的な経路であり、安定しています。
  4. LLMフォールバック(routedBy: "llm")。アンカーワードがどれにも一致しなかった場合に限ります。routing.mdにある閉じた列挙から、最も適合するトピックを1つ選びます。列挙にないトピックを作ってはいけません。

トピックが決まったら、表からメンバーセットを読み取ります。duoモードではdefaultDuo、triadモードではdefaultTriadを使用します。duoがflexと記された一部のトピックでは、references/council.mdから依頼に最も合う軸を持つ対極のペアを選び、その旨を注記します。

Step 3: リポジトリのコンテキストを調査する(集約ブリーフ)

調査によって、依頼文だけではなく実際のコードベースに基づいて評議会が判断できるようにします。デフォルトで有効です。次の両方に該当する場合に実行されます。スキルがリポジトリ内で実行されていること、そして依頼が具体的な成果物(名前が指定されたファイル、コマンド、モジュール、または「この CLI ツール」や「当社の認証モジュール」のような所有を示す表現)に関係していることです。回答がリポジトリの状態によって変わらない抽象的な質問(「一般に、早期に最適化すべきか、後から最適化すべきか」など)は自動的にスキップし、リポジトリがない場合はテキストのみを対象にします。

トリガーとオーバーライド:

  • 上記の条件下ではデフォルトで有効です。 ブリーフに rooted: true を設定します。
  • オプトアウト。 ユーザーが根拠となる情報の調査をスキップするよう求めた場合(「調査なしで」「原則だけで」「リポジトリはスキップして」など)は、rooted: false を設定し、依頼文だけを根拠に判断します。
  • 強制的に有効化。 ユーザーがリポジトリを根拠にするよう求めた場合は、依頼がやや抽象的でも調査を実行します。
  • リポジトリがない場合、または関連する情報が見つからない場合。 rooted: false(リポジトリなし)、または rooted: true に設定し、質問に関係する情報が見つからなかったため、各ペルソナはテキストに基づいて判断するという概要を記載します。

調査はマップリデュース方式で実行します。

  1. 依頼とトピックから、対象を絞った質問を1つ以上導き出します。焦点が絞られた依頼では、デフォルトで質問は1つです。依頼に個別に切り分けられる観点がある場合にのみ、質問を分けます(「このツールはすでに出力を永続化しているか」と「ほかの場所に既存の出力フラグがあるか」は別々の質問です)。最大4つまでとします。トピックの検出ヒントを references/routing.md から確認し、メンバー選定の際と同様に、調査対象の選定に活用します。
  2. 質問ごとに調査サブエージェントを1つ起動し、並列かつ分離して実行します。各エージェントは、独自のコンテキスト内で対象を限定したファイル群(数ファイルまで)を読み、DiscoveryFinding を返します。ファイルの生の内容はサブエージェント内にとどめ、返されるのは調査結果のみです。
  3. 調査結果を1つの簡潔な RepoBrief にまとめます。filesRead を統合し、conventions を集約し、facts を総合して summary に記載します。ファイルの生の内容をブリーフに貼り付けてはいけません。評議会が判断に用いるのはブリーフであり、リポジトリツリーの写しではありません。

調査の対象範囲を絞ってください。ファンアウトで防げるのは、各サブエージェントがファイルの内容をそのまま返さず、調査結果を返す場合に限られます。コンテキストが膨らみすぎるブリーフは、調査が失敗する原因です。

リポジトリは信頼できるものとします。リポジトリに信頼できない内容が含まれる場合、ファイルの内容をサブエージェントに渡すことはプロンプトインジェクションの攻撃対象になります。v1 では、この前提を明記するにとどめ、対策はしません。

Step 4: 起動前に選定結果を提示する

これは監査のゲートです。選定結果と調査ブリーフを表示し、評議会をどのように選定し、何を根拠としたのかをユーザーが確認して、必要に応じて変更できるようにします。その後、ユーザーが前のターンで異議を唱えていない限り、待たずに処理を続行します。

Council: <topic> · <mode>
Members: <member list>
Routed by: <explicit | keyword | llm>
Why: <one line>
Grounded: <rooted ? "yes" : "no">
Files read: <files, or "none (reasoning from the request)">
Brief: <one to three line summary, or "n/a">
Debate: <on | off>

routedBy フィールドによってルーティングを確認でき、読み取ったファイルの一覧によって根拠を確認できます。そのため、調査対象の範囲が不適切だった場合に、見過ごされずに分かります。どちらも省略してはいけません。

Step 5: 評議会を起動する(第1ラウンド)

選定されたメンバーごとにサブエージェントを1つ起動し、同じターン内で並列かつ分離して実行します。各サブエージェントに渡すのは、次の項目だけです。

  1. references/personas/<member>.md を読み、そのペルソナを採用するよう指示すること。
  2. ユーザーの依頼文(そのまま)。
  3. 共有の RepoBrief。
  4. PersonaResponse JSON オブジェクト(以下のスキーマを参照)のみを返し、その前後に文章を付けないよう指示すること。

ブリーフが rooted の場合、ペルソナは依頼文だけでなく、その証拠にもレンズを当て、判断の根拠となる具体的なリポジトリ情報を groundedIn に列挙します。rooted でない場合、ペルソナは依頼文に基づいて判断し、groundedIn は省略します。

ほかのメンバーの回答をサブエージェントに見せてはいけません。分離することで、各自の見解が互いに影響されずに保たれます。すべてのペルソナに同じブリーフを渡すため、異なるのは各自のレンズだけです。

デフォルトで有効なディベートがオンの場合、これが第1ラウンドとなり、続けてステップ6を実行します。オプトアウトされている場合は、そのまま統合に進みます。

ステップ 6:討論ラウンド(任意)

デフォルトで実行します。スキップするのは、ユーザーがステップ 1 で辞退した場合、または第 1 ラウンドの結果が全会一致で、争点となる軸がなかった場合に限ります。全員がすでに同意しているなら、反論で取り上げるものがないためです。全会一致の場合はその旨を記し、すぐに統合へ進みます。それ以外の場合は、第 1 ラウンドに反論パスをちょうど 1 回追加するため、評議会のラウンド数は最大 2 回です。

第 1 ラウンドの結果が返ってきたら、各メンバーを同じターン内に並列で再起動します。各反論サブエージェントには、次の情報を渡します。

  1. references/personas/<member>.md を読み、そのペルソナを採用するよう指示します。ペルソナは第 1 ラウンドと同じです。
  2. リクエストと共有の RepoBrief。第 1 ラウンドと同じです。
  3. そのサブエージェント自身の第 1 ラウンドの PersonaResponse。
  4. 他のメンバーの第 1 ラウンドの立場(スタンス、判定、要点)。
  5. ペルソナファイルに記述されている PersonaResponse ではなく、PersonaRebuttal JSON オブジェクト(スキーマは以下)のみを、前後に文章を添えずに返すよう指示します。

各ペルソナには、他のメンバーに直接応答するよう求めます。納得できる指摘を認める、他のメンバーが十分に重視しなかった意見の相違を明確にする、またはより優れた論拠を示して立場を維持する、のいずれかです。修正後の判定または立場の核心が実際に変わった場合に限り、moved を true にします。表面的な編集では true にしません。

ラウンド内では独立性を保ってください。反論サブエージェントには他のメンバーの第 1 ラウンドの出力を見せますが、進行中の第 2 ラウンドの推論は見せません。全員が同じ第 1 ラウンドのコンテキストから並列に実行されるためです。これにより、第 2 パスでも相互に相関しない状態が維持されます。これは、第 1 ラウンドを独立したサブエージェントとして実行する価値を生むのと同じ性質です。

ここで打ち切ってください。2 ラウンドが上限です。わずかな変化のためにさらに N 回呼び出す価値はないため、第 3 ラウンドは実行しないでください。

ステップ 7:意見の相違を明示して統合する

討論を行わなかった場合は、第 1 ラウンドの PersonaResponse オブジェクトを統合します。討論を行った場合は最終状態を統合します。各メンバーの立場は、その PersonaRebuttal(revisedVerdict、精緻化された stance、更新された recommendation)です。また、各軸をより全体的に把握するため、第 1 ラウンドの tradeoffsRaised も引き続き参照します。意見の相違は明示し、投票で消し去らないでください。推測で意見の相違を探すのではなく、メンバー間の判定とトレードオフの差分を比較して、争点となる軸を機械的に特定します。メンバーが groundedIn を示している場合は、その立場がリポジトリの事実に基づく箇所を明記します。また、分かれている理由が純粋な原則ではなくリポジトリの事実にある場合は、その旨を伝えてください。これは読者が最も注目すべき意見の相違だからです。

次の構成を厳守してください。

## <topic> council · <mode>

### Positions
- **<Member>** (<verdict>): <one to two line condensation of stance + recommendation> [grounded | from principle]
- ... one bullet per member

### Where they agree
<the shared ground, if any. omit the section if there is none>

### Where they split
<the contested axes. for each, name the tradeoff and which member sits on which side. flag any split that turns on a repo fact. this is the heart of the output>

### How positions moved
<debate only. for each member whose moved is true, name what shifted and the specific argument that moved them; note who held firm. omit this section entirely when debate was off>

### Reading
<either a synthesized recommendation when the evidence points one way, or an explicit handoff: "the call hinges on <axis>; if you weight <X> over <Y>, go with <member>'s read.">

質問が真に二択である場合に限り、判定を 1 つの集計として示してください。その場合も、意見の分かれ方は明示します。調査を行った場合に限り、各立場に「根拠あり」または「原則に基づく」と記します。概要がない場合はタグを省略してください。討論を行った場合に限り、変化のセクションを含めます。ペルソナが譲歩した点や譲らなかった点は、出力全体の中で最も示唆に富む一文となることがよくあります。

契約

各ペルソナサブエージェントは、この形式で返します。スキーマを固定することで、文章の解析ではなく機械的に統合できます。

type CouncilVerdict = "for" | "against" | "depends";

type PersonaResponse = {
  readonly member: string;             // canonical id, e.g. "knuth"
  readonly stance: string;             // the member's lens applied to the request
  readonly verdict: CouncilVerdict;
  readonly keyConcerns: readonly string[];
  readonly tradeoffsRaised: readonly string[];
  readonly recommendation: string;
  readonly groundedIn?: readonly string[]; // repo facts the verdict leans on; absent when not rooted
};

討論を行う場合(ステップ 6)、各メンバーは第 2 パスで PersonaResponse の代わりにこれを返します。これは、変化に関わる差分と最終的な推奨事項を含むため、再導出することなく討論後の立場を統合できます。軸をより全体的に把握するための情報は、引き続き第 1 ラウンドの tradeoffsRaised から得られます。

type PersonaRebuttal = {
  readonly member: string;
  readonly respondsTo: readonly string[];  // members whose points this answers
  readonly revisedVerdict: CouncilVerdict;  // may match round one or move
  readonly stance: string;                  // refined position after seeing the others
  readonly recommendation: string;          // final recommendation, which can change when moved
  readonly moved: boolean;                   // whether this persona changed its position
};

選択は次の形式に従います。routedBy が explicit または keyword であれば、これは純粋な処理です。

type Mode = "duo" | "triad";

type Selection = {
  readonly topic: string;              // closed enum member from routing.md
  readonly mode: Mode;
  readonly members: readonly string[]; // from the static topic table
  readonly routedBy: "explicit" | "keyword" | "llm";
  readonly note: string;               // why this topic, for the echo
};

各調査サブエージェントは調査結果を返すため、概要への統合は機械的に行えます。

type DiscoveryFinding = {
  readonly question: string;           // the scoped question this agent investigated
  readonly filesRead: readonly string[];
  readonly facts: readonly string[];   // concrete observations, each tied to a file
  readonly relevant: boolean;          // false when nothing bearing on the question was found
};

オーケストレーターは調査結果を 1 つの簡潔な概要にまとめ、すべてのペルソナサブエージェントに渡します。生の DiscoveryFinding[] はエコー用にオーケストレーターが保持し、ペルソナに渡すのは次の内容のみです。

type RepoBrief = {
  readonly rooted: boolean;                // false when no repo, or discovery was skipped
  readonly filesRead: readonly string[];   // union across findings, echoed for audit
  readonly conventions: readonly string[]; // language, build tooling, existing patterns
  readonly summary: string;                // synthesis of the findings, not raw file dumps
};

参照ファイル

  • references/council.md は、メンバー一覧と極性のペアを記載しています。軽量なファイルで、ルーティングや flex デュオの決定に使います。最初に読み込んでください。
  • references/routing.md は、統合されたトピック表です。アンカーとなる用語、defaultDuo、defaultTriad、優先順位のタイブレーク一覧、そして検出時に確認する対象を示すトピックごとのヒントが含まれています。これはレイヤー 2 です。
  • references/personas/<member>.md は、詳細なペルソナプロンプトです。ペルソナファイルは、そのメンバーに割り当てられたサブエージェント内でのみ読み込まれるため、トークンの予算を全メンバーではなく評議会の規模に合わせられます。

ガードレール

各ペルソナは、実在する人物の、公開されているエンジニアリング哲学を反映しています。その人物の見解を個人的な信念として主張するのではなく、依頼にその哲学を適用した見解として提示してください。また、発言を捏造しないでください。文書化された立場に基づくことで、ペルソナはより明確になり、これは礼儀であると同時に品質向上にもつながります。

コメント