💡

構造で育てるシステムアーキテクチャ - 既存スタックを置き換えずに実装する

に公開

新しい業務スタックは作らなくてもよいという話

前回は、組織運用を人のセンスやその場の雰囲気ではなく、観測・判断・記録・再入可能性の問題として捉え直しました。

AI導入、採用、評価、1on1、振り返り、ロードマップ、例外承認。
こうしたテーマは一見バラバラに見えますが、実際にはどれも

  • 何を観測するか
  • 何を根拠に判断するか
  • どこまでを自律とみなすか
  • 何を記録し、あとでやり直せるようにするか

という同じ構造を持っています。

そして、それらはかなり自然に

  • proposal
  • verification
  • typed execution
  • audit

という枠に載せられる、という話をしました。

ここで「組織運用を決定論アーキテクチャで実装する」と言うと、既存の Slack や Notion や人事システムを全て捨てて、新しい巨大な業務OSを作る話のように見えてしまったかもしれません。

しかし、実際にやるべきことは逆です。

それは置き換えではなく、判断構造の明示化である

対象にしているのは既存システムの全面的な置換ではありません。

Slack を消すわけでもない。
GitHub を消すわけでもない。
Notion や Docs やカレンダーや採用管理システムを消すわけでもない。

むしろ、それらをまたいで流れている判断の構造を取り出し、

  • これは単なる提案なのか
  • これはレビュー要求なのか
  • これは承認待ちなのか
  • これは実際に effect を持つ操作なのか

を明示し、壊れにくい形にすることが本題です。

現実の組織では、判断はたいてい散らばっています。

会議で話した
Slack でだいたい合意した
ドキュメントに方針を書いた
上長が「それでいこう」と言った
GitHub のPRがマージされた
タスクが動いた
誰かの権限が変わった

表面上は全て別の出来事に見えますが、実装の観点から見れば重要なのは表面的な違いではありません。

重要なのは、

  • 何の判断だったのか
  • 誰が authority を持っていたのか
  • どんな観測に基づいていたのか
  • どこまでが support で、どこからが commit なのか
  • 不足があればどこへ差し戻すのか

です。

つまり、作るべきものは新しい万能の業務スタックではなく、既存運用を横断する判断のためのプロトコル層です。
そういう話をします。

まず分けるべきは、提案・レビュー・effect である

組織運用が壊れやすいのは、提案と判断と実行が混ざるからです。

誰かが出した草案が、いつの間にか決まったこと扱いになっている。
レビュー待ちの案が、暗黙に commit 扱いされている。
ただの相談が、既成事実として運用に入り込む。

これを避けるには、最初に少なくとも3つを分ける必要があります。

1つ目は proposal です。
たとえば、

  • 1on1 の問い案
  • 次回までの課題案
  • 面接の深掘り質問案
  • 評価文の下書き
  • ロードマップ修正案
  • retro の構造課題案

などはすべて proposal です。
ここではまだ何も決まっていません。人間が出してもAIが出してもよいですが、重要なのは、提案にすぎない、と明示されていることです。

2つ目は review です。
ここでは提案をそのまま通さず、

  • 入力は足りているか
  • 判断基準に合っているか
  • authority は適切か
  • high-risk な effect が混ざっていないか
  • 不足があれば何が missing か

を見ます。

ここで verifier が ACCEPT / DEGRADE / REJECT を返します。
この層があることで、なんとなくよさそう、から判断可能な状態かどうかへ視点を移せます。

3つ目が effect です。
effect とは、現実の組織状態を変える操作です。

  • 権限を付与する
  • 評価を確定する
  • 採用可否を進める
  • 重要方針を変更する
  • 外部向け案内を確定する
  • 人事・業務・顧客状態を更新する

ここに至って初めて判断は現実に影響を持ちます。

組織運用を決定論アーキテクチャで扱うとは、何より先にこのproposal / review / effect の分離を壊さないことです。

実装対象は画面ではなく、判断の型である

このような話をすると、すぐに「どんなUIにするのか」「どのツールと連携するのか」という話になりがちですが、順番は逆です。

先に定義すべきなのは画面ではなく、組織の中にどんな判断型があるかです。

たとえば組織には、繰り返し現れる型があります。

1on1判断
採用判断
評価判断
例外承認
ロードマップ変更
incident 後の再発防止判断
高リスクなAI利用可否判断
権限委譲判断

こうした判断型ごとに、最低限

  • 入力は何か
  • 誰が提案できるか
  • 誰がレビューできるか
  • 誰が commit できるか
  • どんな effect を持ちうるか
  • 差し戻しや rollback はどうなるか

を定義できれば、かなりの部分が扱えるようになります。

多くの組織では、業務システムはあっても判断型の定義が弱い。
そのため、画面はあるのに責任境界が曖昧で、ログはあるのに replay できず、承認フローはあるのに何を承認したのか曖昧、という状態になりがちです。

だから、最初に実装すべきなのは豪華な業務画面ではなく、判断型のカタログです。

authority は「偉い人」ではなく、effect ごとに定義する

組織運用があとから揉める理由のひとつは、最終的に誰が決めたのかが曖昧になることです。

会議では賛成っぽかった。
Slackでも異論はなかった。
マネージャーも見ていた。
でも正式に誰が commit したのか分からない。

この状態では audit も replay も成立しません。

必要なのは、偉い人が何となく決める構造ではなく、どの effect にどの authority が必要かを先に設計することです。

たとえば authority は、

  • 提案できる人
  • レビューできる人
  • 承認できる人
  • 例外を許可できる人
  • rollback を起動できる人

のように分解できます。

採用の最終可否を commit できる人と、面接メモを書ける人は同じである必要はありません。
評価文の下書きを作れる人と、評価を確定できる人も同じである必要はありません。
AI利用の提案を出せる人と、高リスク利用を承認できる人も同じである必要はありません。

重要なのは、authority が役職名として曖昧に置かれていることではなく、effect row と結びついていることです。
何を変えられるのか。何を決められるのか。どこで止められるのか。そこが先です。

既存システムは「中心」ではなく「チャネル」として接続する

ここでようやく、Slack や GitHub や Notion やカレンダーの話になります。

大事なのは、これらを判断システムの中心に置かないことです。
それらはあくまで、入力・表示・実行・通知のチャネルの一つです。

たとえば、

GitHub は proposal と review signal を運ぶ。
Slack は相談、補足観測、差し戻し、再入要求を運ぶ。
Notion や Docs は方針、説明、判断記録の可読面を担う。
カレンダーは follow-up action の実行先になる。
HRシステムや権限管理は effect の適用先になる。

この整理があると、全部入りの新プロダクトを最初から作る必要がなくなります。
既存ワークフローの上に、判断プロトコル層を載せればよいからです。

つまり、決定論アーキテクチャで組織運用を実装するとは、既存の道具を捨てることではなく、既存の道具の上を流れている判断を壊れにくい単位に整形することです。

YAML / JSONで見る最小スキーマ草案

今回は「判断の落とし込み」に絞る

前回は、観測入力、verification 結果、typed action、監査ログまで含めた最小構成を見ました。
今回はその続編として、既存の組織運用をどのような判断単位に落とし込むのかに絞って、より狭いスキーマを置いておきます。前回の抽象構造を受けつつ、今回は decision typing / gate / effect commit に焦点を当てます。

ここで重要なのは、最初から巨大な業務仕様を作ることではありません。

まず必要なのは、

  • これは単なる提案なのか
  • これは review を要する判断なのか
  • これは effect を持つ commit なのか

を機械的に区別できる最小構造です。

1. decision_support_envelope

まずは、まだ提案にすぎないものを保持するオブジェクトです。

ここで大事なのは、proposal を proposal のまま保存することです。
まだ review を通っていないものに、勝手に effect を持たせないこと。

主なフィールドは、

  • support_id
  • decision_type
  • submitted_by
  • authority_lane
  • input_refs
  • proposal_summary
  • open_questions
  • evidence_refs
  • effect_row

です。

特に effect_row: [] を明示しておくと、「これはまだ何も commit していない support-only の提案である」ということが構造として残せます。

kind: decision_support_envelope
version: v1
support_id: dse_2026_04_20_001
created_at: 2026-04-20T10:00:00+09:00

decision_type: "hiring_decision"
submitted_by:
  actor_id: "interviewer_003"
  actor_role: "interviewer"

authority_lane: "review_required"

input_refs:
  - "interview_note_01"
  - "interview_note_02"
  - "hiring_scorecard_backend_v1"

proposal_summary:
  candidate_assessment: "バックエンド設計力は強いが、運用設計の観測が不足"
  suggested_next_step: "追加面接で incident response の深掘りを行う"

open_questions:
  - "高負荷時の設計判断をどこまで自走できるか"
  - "運用改善の ownership を持てるか"

evidence_refs:
  - "score_signal_architecture"
  - "score_signal_debugging"

effect_row: []

2. review_gate_result

次に、review 層の返り値です。

前回の verification_result は汎用的でしたが、今回は既存運用から落とし込んだ判断が、effect に進めるかどうかを見るための gate に寄せます。

主なフィールドは、

  • gate_result_id
  • target_id
  • decision_type
  • verdict
  • reason_codes
  • missing_fields
  • required_review_lanes
  • next_step
  • created_at

です。

ここで verdict は変わらず ACCEPT / DEGRADE / REJECT です。
ただし今回示すのは、これは単なる品質判定ではなく、この判断を次の段階へ進めてよいかどうかの gate です。

{
  "kind": "review_gate_result",
  "version": "v1",
  "gate_result_id": "rgr_001",
  "target_id": "dse_2026_04_20_001",
  "decision_type": "hiring_decision",
  "verdict": "DEGRADE",
  "reason_codes": [
    "missing_operational_evidence",
    "insufficient_signal_for_commit"
  ],
  "missing_fields": [
    "incident_response_signal",
    "ownership_examples"
  ],
  "required_review_lanes": [
    "hiring_panel"
  ],
  "next_step": {
    "action_type": "request_additional_interview",
    "params": {
      "focus": [
        "incident_response",
        "ownership_under_ambiguity"
      ]
    }
  },
  "created_at": "2026-04-20T10:10:00+09:00"
}

3. effect_commit_record

最後に、実際に effect を持つ commit を保持するオブジェクトです。

ここではじめて、判断が現実の組織状態に影響します。

主なフィールドは、

  • commit_id
  • decision_type
  • authority_lane
  • review_lanes
  • committed_by
  • scope
  • effect_row
  • receipt_refs
  • created_at

です。

effect_row は自由文ではなく、何が変更されるのかを型付きで表す列です。
これがあることで、提案文や議事録をそのまま実行する、という事故を避けられます。

{
  "kind": "effect_commit_record",
  "version": "v1",
  "commit_id": "ecr_001",
  "decision_type": "access_grant",
  "authority_lane": "manager_plus_security",
  "review_lanes": [
    "security_review"
  ],
  "committed_by": {
    "actor_id": "manager_001",
    "actor_role": "engineering_manager"
  },
  "scope": {
    "target_user": "user_042",
    "target_resource": "prod_incident_dashboard",
    "duration": "temporary_14d"
  },
  "effect_row": [
    {
      "effect_type": "grant_access",
      "resource": "prod_incident_dashboard",
      "permission": "read_only",
      "ttl_days": 14
    }
  ],
  "receipt_refs": [
    "rgr_014",
    "approval_note_221"
  ],
  "created_at": "2026-04-20T11:00:00+09:00"
}

Pythonで見る最小ランタイム

今回は判断の落とし込みとgateに絞る

ここからは、実際に既存の組織運用をどう扱うかを最小コードで見ます。

大げさなフレームワークや複雑な分散処理は要りません。
まず必要なのは、

  • 自由文や会議メモを、そのまま effect にしないこと
  • support-only / review-only / effect-bearing を分けること
  • review gate の結果で次の動きを変えること

の3点です。

ここで以後の実装例では、判断の posture を
support-only / review-only / effect-bearing
の3つで表します。
support-only は提案支援のままでまだ effect を持たないもの、
review-only は review の対象ではあるがまだ commit ではないもの、
effect-bearing は commit されれば現実の状態変更を起こすものです。

説明上は proposal / review / effect という業務上の区分を使い、実装上はそれに対応する posture として support-only / review-only / effect-bearing というラベルを使います。

1. lower_decision()

会議メモや提案文を、そのまま実行しない

まずは、既存のメモやチケットや会議ログを判断単位へ落とし込みます。

ここでのポイントは入力を直接実行対象にしないことです。

from __future__ import annotations

from dataclasses import dataclass
from typing import Any


@dataclass(frozen=True)
class LoweredDecision:
    decision_type: str
    posture: str  # "support_only" | "review_only" | "effect_bearing"
    authority_lane: str
    summary: str
    evidence_refs: tuple[str, ...]
    effect_row: tuple[dict[str, Any], ...] = ()


def lower_decision(raw_note: dict[str, Any]) -> LoweredDecision:
    decision_type = raw_note["decision_type"]
    requested_action = raw_note.get("requested_action", "")
    evidence_refs = tuple(raw_note.get("evidence_refs", []))
    summary = raw_note.get("summary", "")

    if requested_action in ["draft_questions", "draft_feedback", "draft_plan"]:
        return LoweredDecision(
            decision_type=decision_type,
            posture="support_only",
            authority_lane="none",
            summary=summary,
            evidence_refs=evidence_refs,
            effect_row=(),
        )

    if requested_action in ["request_review", "request_approval"]:
        return LoweredDecision(
            decision_type=decision_type,
            posture="review_only",
            authority_lane="review_required",
            summary=summary,
            evidence_refs=evidence_refs,
            effect_row=(),
        )

    if requested_action == "grant_access":
        return LoweredDecision(
            decision_type=decision_type,
            posture="effect_bearing",
            authority_lane="manager_plus_security",
            summary=summary,
            evidence_refs=evidence_refs,
            effect_row=(
                {
                    "effect_type": "grant_access",
                    "resource": raw_note["resource"],
                    "permission": raw_note["permission"],
                    "ttl_days": raw_note.get("ttl_days", 7),
                },
            ),
        )

    return LoweredDecision(
        decision_type=decision_type,
        posture="review_only",
        authority_lane="manual_review",
        summary=summary,
        evidence_refs=evidence_refs,
        effect_row=(),
    )

この関数の役割は、賢い自然言語理解ではありません。
これは曖昧な入力をそのまま effect に進めないための整流器です。

ここで先に posture を決めておくことで、

  • これは support-only だから commit してはいけない
  • これは review-only だから gate を通す必要がある
  • これは effect-bearing だから authority と receipt が必要

という扱いが機械的になります。

2. gate_and_emit()

review の結果で、effect / extension / stop を分ける

次に、review gate の結果を見て、次に何をするかを決めます。

ここでも重要なのは、verdict に応じて runtime の振る舞いを固定することです。

from __future__ import annotations

from dataclasses import dataclass
from typing import Any


@dataclass(frozen=True)
class GateResult:
    verdict: str  # "ACCEPT" | "DEGRADE" | "REJECT"
    reason_codes: tuple[str, ...]
    missing_fields: tuple[str, ...]


def gate_and_emit(
    lowered: LoweredDecision,
    gate_result: GateResult,
) -> dict[str, Any]:
    if lowered.posture == "support_only":
        return {
            "runtime_action": "store_as_support",
            "effect_emitted": False,
            "reason": "proposal_only",
        }

    if gate_result.verdict == "ACCEPT" and lowered.posture == "review_only":
        return {
            "runtime_action": "mark_review_passed",
            "effect_emitted": False,
            "authority_lane": lowered.authority_lane,
            "next_phase": "await_commit_decision",
        }
    
    if gate_result.verdict == "ACCEPT" and lowered.posture == "effect_bearing":
        return {
            "runtime_action": "emit_effect_commit",
            "effect_emitted": True,
            "effect_row": list(lowered.effect_row),
            "authority_lane": lowered.authority_lane,
        }
    
    if gate_result.verdict == "DEGRADE":
        return {
            "runtime_action": "request_extension",
            "effect_emitted": False,
            "missing_fields": list(gate_result.missing_fields),
            "reason_codes": list(gate_result.reason_codes),
        }
    
    return {
        "runtime_action": "stop",
        "effect_emitted": False,
        "reason_codes": list(gate_result.reason_codes),
    }

ここでやっていることは単純です。

  • support-only なら保存だけする
  • ACCEPT かつ review-only なら「review は通過したが、まだ commit ではない」状態として次段へ送る
  • ACCEPT かつ effect-bearing なら commit を出す
  • DEGRADE なら追加観測や追加 review に戻す
  • REJECT なら止める

たったこれだけですが、組織運用ではこの分離が重要です。

多くの現場では、proposal なのか review 要求なのか effect なのかが曖昧なまま流れてしまいます。
すると、あとで「誰が決めたのか」「何が不足していたのか」「なぜ通ったのか」が分からなくなる。

逆に、この程度のランタイムでも posture と verdict を明示できれば、かなり壊れにくくなります。

このスキーマとコードで何が見えるか

ここで見せたのは、巨大な業務システムではありません。
もっと小さい話です。

  • 提案は proposal のまま保持する
  • review の返り値を型として持つ
  • effect を自由文ではなく effect row に閉じる
  • verdict に応じて runtime の動きを固定する

これだけです。

しかし、この4つが揃うだけで、組織運用はかなり変わります。

会議で出た話がそのまま既成事実になることが減る。
提案が勝手に commit 扱いされることが減る。
review 待ちのものが暗黙に effect を持つことが減る。
あとから replay したときに、何が support で何が decision で何が effect だったのかが見える。

つまり、ここで必要なのは豪華なAIより先に、判断境界を壊さない最小構造です。

MVP は、effect-bearing な判断から始めるのがよい

ここでも全部を一度に扱う必要はありません。
むしろ最初は絞った方がよいです。

MVP で先に対象にするべきは、あとで揉めやすくeffect が大きい判断です。

たとえば、

  • 権限付与
  • 評価確定
  • 採用可否
  • 高リスクAI利用
  • 重要方針変更

のようなものです。

こうした判断について、最低限

  • 入力
  • verdict
  • reason codes
  • missing
  • authority
  • action
  • created_at

程度の record を固定し、effect の前に verifier を挟むだけでもかなり違います。

ACCEPT なら通す。
DEGRADE なら追加観測へ戻す。
REJECT なら止める。

この最小ループがあるだけで、雰囲気で進んだ判断が減ります。

ここで重要なのは、全部の会話や全部のメモを統制しようとしないことです。
先に対象にすべきは、effect-bearing な判断です。

その後の拡張は、骨格の上に足せばよい

最初のMVPが回り始めた後は、いろいろな拡張が考えられます。

UI入力時に verifier で簡易検証して UX を作りこんでもよい。
十分なゴールデンケースでガードレールが整えば、軽量 verifier とは別に NLP や LLM を用いた重量級 verifier を挟んでもよい。
履歴と傾向分析から、詰まりやすい判断型や、頻出する reason code や、更新すべき policy の提案を返す機能を作ってもよい。

つまり、組織の判断タスクを段階的に吸収し、組織の成長に合わせてプロダクト自体も成長させていくことができます。

ただし、順番は逆にしない方がよいです。

最初に AI の派手な提案機能から入るのではなく、

  • 判断型
  • authority
  • proposal / review / effect 分離
  • verifier
  • receipts と audit

の骨格を先に作る。

その骨格があるからこそ、後から重い verifier や高度な提案機能を足しても、便利なデモで終わらずに運用へ乗せられます。

このアーキテクチャは、既存スタックを置き換えるものではない

ここまで見ると、このアーキテクチャの位置づけはかなりはっきりします。

これは、既存のアプリケーションスタック、組織図、データプラットフォーム、あるいはモデルスタックの代替ではありません。
むしろ、既存システムを横断して判断を通すためのプロトコル層と考えるのが適切です。

よって、内部実装の自由度は高く、既存のツール群の上に薄く載せることもできるし、一部に専用UIを作ることもできる。
スケールもしやすいです。

重要なのは、構造を壊さないことです。

proposal と effect を混ぜない。
authority を曖昧にしない。
review を省略しない。
reason codes と missing を残す。
必要なら replay できる。
戻すべきときに戻せる。

この構造さえ崩さなければ、内部の技術選択はかなり自由です。

まとめ

前回は、組織運用が決定論アーキテクチャで実装できることを見ました。

そして今回は、その実装が意味するのは「新しい全部入り業務スタック」を作ることではない、という話をしました。

本質は、

組織の中にある判断型を見つけ、
proposal / review / effect を分け、
authority を明示し、
verifier で gate し、
effect を typed にし、
receipts と audit を残すことです。

つまり、組織運用を雑にソフトウェア化するのではなく、組織の判断境界を壊れにくくするのが目的です。

AI時代に重要なのは、ただ速く下書きできることではありません。
その速さを、ちゃんと review できて、ちゃんと止められて、ちゃんと記録できて、必要ならやり直せる組織運用に変えられることです。

そのために必要なのは、新しい巨大スタックではなく、既存運用を横断する判断プロトコル層です。

続編 :「構造で育てるシステムアーキテクチャ - 判断プロトコル層の責務とは

Discussion