コンテンツにスキップ

SuperAuditor 標準 (v1)

翻訳について: 正本は英語版です。日本語版が古い場合は英語版を参照してください。

findings の配信契約です: findings とは、サーバーが自身の保存状態について すでに算出している drift・staleness・整合性の観測結果を指します。本標準は サーバーが findings をどう報告するか — その継ぎ目 — を規定し、サーバーが 何を検出するかについては意図的に何も述べません。

名前が職務内容をそのまま表しています: 監査人は検査して報告するのであって、 修復はせず、オペレーターが次に何をすべきかも決めません。

本文書のステータス

v1、稼働中の実装から抽出したものです。 以下の pull 契約・severity の語彙・ キャップの意味論は、CScheduler 0.9.0 (2026-07-31) で出荷され、本文書が書かれる 前に本番で実測されたものです。ここに投機的な設計はありません。パイロットの設計 メモと出荷されたコードが食い違った箇所では、コードを正としました。

2 つ目の実装として想定しているのは CPersona です。2 つ目の実装が存在するまでは、 風変わりに見える要件は、一般化された知見ではなく 1 つのシステムから得られた証拠 として扱ってください。

共有ライブラリはありません。 実装に共通ランタイムのリンクを MUST NOT 要求します。一貫性は本文書と conformance/superauditor/v1/ にある適合性 フィクスチャが担うため、別の言語による実装が他言語のバグを移植させられることは 決してありません。これは MGP で採った spec-first のアプローチと同じ考え方です。

キーワード MUST, MUST NOT, SHOULD, MAY は RFC 2119 に従って用います。

1. 動機

findings を算出するサーバーは、それをいつ引き渡すかを決めなければなりません。 安直な既定は読み取りのたびに付けることであり、CScheduler は 13 個の読み取り ツールでまさにそうしていました。変更前の実トランスクリプトで実測した結果 (2026-07-31、運搬役となっていたツールへの 97 回の呼び出し):

  • findings ブロックは応答 1 件あたり中央値で 4,578 文字 ≈ 2,773 トークン あり、相乗りしたペイロードの 43.9%、小さい応答では最大 99.1% を 占めていた。
  • 唯一の消費側がそれを読んだのは、セッション終了時の 1 回だけだった。
  • ブロックが push される以上、未解決の状態はそのまま呼び出しごとの固定費に 変換される: 誰も対処しない findings が、読み取りのたびに再課金される。

push 配信はまた、findings をアンビエントなものにします。求めてもいないのに、 無関係な作業の最中に届く — まさに最も対処しづらいタイミングで届くのです。

解決策は検出を減らすことではありません。検出 (変更なし) と配信 (pull、要求に応じて) を分離することです。

2. 範囲と非目標

範囲内 — 継ぎ目:

  • finding の形
  • severity の語彙と、severity がどう割り当てられるか
  • pull ツール、そのパラメータ、その応答
  • 切り詰めの正直な報告
  • findings が呼び出し側の隔離フィルタおよびセッション同一性とどう関係するか
  • 既存の broadcast を、その消費側を壊さずにどう退役させるか

範囲外 — スコープクリープこそ本文書が防ぐために存在する具体的な失敗なので、 ここに名指しで挙げます:

  • 実行層は持ちません。 finding に対処するには判断が要ります。標準は配信し、 オペレーターが決めます。行動まで行う検出器は、データ層の服を着たポリシー エンジンです。
  • 自動修復は持ちません。 修復ツール (例: check_health(fix=true)) は自身の 修復機能を持ち続けます。SuperAuditor の実装は状態を MUST NOT 変更します。
  • 検出カタログは持ちません。 何を finding とみなすかは各サーバーの裁量です。 CScheduler は計画グラフの意味的な drift を検出し、CPersona はストレージの 整合性を報告することが想定されています。契約は同じ、中身は別です。
  • confidence スコアは持ちません。 §4 を参照してください。
  • probe の精度要件は持ちません。 検出器の改善は直交する作業であり、本契約を 通じて MUST NOT 紛れ込ませます。

3. finding オブジェクト

finding は JSON オブジェクトです。本標準が定義するキーは 2 つです:

キー 要件
kind string MUST。それを生成した probe を指す、サーバー定義で安定した識別子 (例: stale_pending)。
severity string MUST。§4 の値のいずれか 1 つ。

それ以外のキーはすべてペイロードであり、サーバー定義です: 消費側が対処するために 必要な識別子・件数・経過時間・タイトルなどです。消費側は未知のペイロードキーを MUST 許容します。

kind の語彙は標準化しません。同じ kind 名を共有する 2 つの実装は SHOULD それによって同じことを意味しますが、標準は kind を列挙せず、名前の予約も行い ません。

4. severity

値はちょうど 3 つ、順序付きです:

severity 意味
critical 読み取り契約がいま破れている — 呼び出し側にすでに渡したデータが信用できない。
warn 保存された 2 つの事実が矛盾している。いま何かがおかしい。
info 観測または提案。対処するかどうかは判断次第。

割り当て規則:

  1. severity はインスタンスではなく kind の属性である MUST。 実装は静的な kind ごとのマップから severity を MUST 割り当てます。モデルに よる判断も、finding ごとのスコアリングも、confidence 値もありません。消費側 は、severity を導出し直すことなくそれに基づいて振り分けられなければなり ません。
  2. このマップは probe レジストリを網羅する MUST であり、エントリを持たない probe kind があればテストが失敗することで強制されます。実装は実行時 フォールバックを MAY 併せ持ちますが、持つ場合、そのフォールバックは最も弱い severity (info) で MUST — マップされていない probe が警報を作り出せては なりません。
  3. 前提が字句一致である probe を MUST NOT warn または critical に します。 自由文に対するキーワード一致は、もっともらしいが誤っている findings を生みます。CScheduler のパイロットでは、そうした probe を、 フラグが立ったレコードの全文読解と突き合わせたところ精度は 0/5 でした。 証拠が文字列一致でしかない finding に、欠陥を主張する資格はありません。

実装は、ある severity を未使用のまま MAY 残せます。CScheduler は critical を 出しません: 計画グラフの drift が読み取りを偽にすることはないからです。

5. pull 配信

5.1 ツール

get_session_findings(session_key?, per_kind_limit?, include_summary?) -> object

このツールは MUST read-only であり、MUST いつ呼び出しても安全です。findings に コストを払う価値があるのはいつかを決めるのは消費側であり、サーバーはレート制限や 古い結果のキャッシュによって MUST NOT それを差し置いて判断します。

パラメータ 既定値 意味
session_key なし 不透明で、クライアントが宣言するセッション同一性 (§7)。
per_kind_limit 5 kind ごとに返す findings の上限。
include_summary true 人間可読な summary のレンダリングを含める。

include_summary=false が存在するのは、その散文が findings を言い直したもの だからです。機械的な消費側は、その分のコストを払わないことを MUST 選べます。

5.2 応答

{
  "findings":            [ { "kind": "stale_pending", "severity": "info", "task_id": 6, "days_stale": 62 } ],
  "total":               34,
  "counts_by_kind":      { "stale_pending": 20, "active_goal_claims_achievement": 13, "duplicate_pending": 1 },
  "counts_by_severity":  { "info": 33, "warn": 1 },
  "capped_kinds":        ["stale_pending"],
  "per_kind_limit":      20,
  "summary":             "Drift / maintenance findings: …",
  "identity_shared":     true,
  "_meta":               { "server_version": "0.9.0" }
}
キー 要件
findings MUST。per_kind_limit を適用した後の、切り詰め済みの集合。
total MUST。返された findings の件数。実在する真の件数ではありません — 切り詰めはこのフィールドではなく capped_kinds で報告されます。
counts_by_kind / counts_by_severity MUST。返却集合に対して算出するため、常に findings と一致します。
capped_kinds MUST。§6 を参照。何もキャップされなかったときは、空の値として存在します。
per_kind_limit MUST。実際に適用された上限をそのまま返すため、保存済みの応答を読む消費側は、要求の内容を知らなくても capped_kinds を解釈できます。
summary include_summary が true のとき MUST 存在し、findings と同じ切り詰め済み集合から MUST レンダリングされます — 散文と構造が食い違うことはありません。
identity_shared §7 が該当するときは MUST 存在し、値は true。該当しないときは SHOULD 省略します。
_meta.server_version MUST。稼働中のインスタンスが自分自身を名乗ります。

応答は broadcast ブロック (§8) を MUST NOT 運びます: このツールこそが findings のチャネルであり、push 用のペイロードを付ければ二重に課金することに なります。

5.3 検出器は 1 つ

実装が複数のチャネルで findings を配信する場合 (例: broadcast の移行期間中)、 すべてのチャネルは単一の検出実装から MUST 供給されます。probe が push された ときと pull されたときで別の意味を持てることは MUST NOT。

6. 正直なキャップ

per_kind_limit は切り詰めを行いますが、素朴な実装はそれを黙って行います: ちょうど上限と同数の kind は、200 行ある kind と見分けがつきません。これは消費側 には検出できない嘘です。

  • 実装は、返した件数より多くの findings が存在したすべての kind を capped_kinds で MUST 報告します。
  • 実装はこれを推論ではなく観測によって MUST 判定します — 参照実装の手法は per_kind_limit + 1 で問い合わせ、返す前に余分な 1 行を切り戻すことです。 count == limit から「キャップされた」と結論するのは適合しません。たまたま ちょうど limit 件の findings を持つ kind を、切り詰められたものとして報告 してしまうからです。
  • 応答のどこであれ、黙った切り詰めは禁止です。
  • 切り詰めは検出器の並び順を MUST 保ちます: ある kind について残す findings は、 その先頭 per_kind_limit 件です。並べ替え (severity 順・経過時間順・その他 何であれ) は切り詰めの前に MAY 行えますが、(検出器の出力, per_kind_limit) の組が返却集合を MUST 決定します — §9 のフィクスチャがそれに依存しています。

これが重要である理由はパイロットで実測されています: kind ごと 5 件でキャップ された broadcast が 11 件の findings を示していた場面で、より大きな上限での pull は 34 件を返しました。

7. セッション同一性と隔離

セッション同一性。 session_key は不透明で、クライアントが宣言するラベル です — 分割のヒントであって認証ではありません。セッション単位の probe (例: 「このセッションが触れ、pending のまま残したレコード」) を持つ実装は、宣言された キーで MUST スコープします。

デプロイがセッションを区別できない場合 — キーが宣言されていない共有のリモート トランスポートなど — 実装は推測するのではなく identity_shared: true でその旨を MUST 表明します。正直に縮退することは必須であり、黙って縮退することは適合し ません。

隔離フィルタ。 findings を呼び出し側の隔離軸 (project、agent、tenant、 またはそれに相当するもの) で MUST NOT フィルタします。このチャネルの目的は 忘れられた状態を表に出すことであり、呼び出し側がたまたま読んでいるバケットで 切り出せば、まさに忘れられていたレコードこそが隠れてしまいます。実装はこれを MUST 文書化します。そのようなサーバーにおける他のすべての読み取りとは逆の挙動 だからです。

8. 既存の broadcast との共存

すでに無関係な応答へ findings を push している実装に、適合のために消費側を壊す ことを MUST NOT 要求します。移行の契約は次のとおりです:

  1. pull ツールを先に出荷します。これは純粋に追加のみの変更です。
  2. broadcast を、少なくとも all (既存の挙動) と off (push しない) の値を 持つ実行時ノブの背後に置きます。指定した 1 つの応答にだけ push を残す中間の 値 — CScheduler はセッション開始時の読み取りである context を使っています — は RECOMMENDED です。残った消費側が動き続けられる一方で、他のすべての応答が 解放されるからです。
  3. 導入時の既定値は既存の挙動をバイト単位で MUST 維持し、それをテストで 証明します。オペレーターが自ら opt in します。
  4. 認識できないノブの値は、既存の挙動に MUST フォールバックし、警告をログに 出します。デプロイ環境でのタイプミスが黙って監査の目を潰すことは MUST NOT。
  5. broadcast のペイロードは凍結したまま SHOULD 残します — とりわけ、実装は そこに severity を SHOULD NOT 追加します。新しいキーを読まない消費側の ために既存応答のバイト列を変えることは、バイト単位で同一という既定を何の 見返りもなく手放すことです。2 つのチャネルの非対称性は、broadcast を切った ときに解消されるのであって、いま編集することで解消されるのではありません。

9. 適合性

実装は、上記のすべての MUST を満たし、次の各項目をテストで示したときに適合し ます。C1–C5 は conformance/superauditor/v1/ にある共有フィクスチャに対して 検証できます。これらは (入力の findings, per_kind_limit) の純粋関数であり、 したがって言語に依存しません。

id 要件
C1 切り詰め: findings の中で per_kind_limit を超える kind がない。
C2 capped_kinds は、より多くの findings が存在したすべての kind を挙げ、それ以外は挙げない — ちょうど per_kind_limit 件という境界条件を含む。
C3 totalcounts_by_kindcounts_by_severity が返却集合に対して算出され、findings と一致する。
C4 severity は静的マップに由来し、同じ kind は常に同じ severity になる。
C5 マップされていない kindinfo に解決される (そもそもフォールバックが存在する場合)。
C6 severity マップが probe レジストリを網羅している — エントリを持たない新しい probe は、黙って既定値になるのではなくテストを失敗させる。
C7 findings が呼び出し側の隔離軸でフィルタされない。
C8 session_key の宣言がない共有トランスポート下で、応答が identity_shared: true を持つ。
C9 broadcast がある場合: 既定のノブ値が変更前の応答をバイト単位で再現し、それ以外の各値が、自ら主張したとおりの応答からちょうど push を落とす。

broadcast を持たない実装は C9 を免除されます。

10. バージョニング

本文書は、いかなる実装からも独立してバージョン管理されます。追加的な明確化は マイナーバージョンを上げ、適合済みの実装を不適合にする変更はメジャーバージョンを 上げ、移行ノートを MUST 伴います。フィクスチャのディレクトリはメジャーバージョン に合わせてバージョン管理されます (conformance/superauditor/v1/)。

正本の置き場所: 標準の実装が 2 つ未満のあいだは本リポジトリです。より広く採用 された場合、正本の置き場所は移り、本文書はそのポインタになります。