candor について
candor-ts は TypeScript 関数が呼び出し連鎖を通じて到達する副作用を分析し、決定論的なポリシーゲートを適用します。コードの境界や変更の影響を確認する開発者向けで、MCP サーバーからコーディングエージェントが事前生成レポートを照会できます。
主な機能
- 01呼び出し連鎖をまたぐ副作用の追跡
- 02未解決箇所を Unknown として表示
- 03ポリシーゲートによる違反検出
- 04副作用ベースラインの比較
- 05影響範囲・呼び出し元・経路の照会
活用シーン
- 01関数変更の実行時影響を評価
- 02ネットワークやデータベースへの到達を確認
- 03CI で新たな副作用を阻止
- 04コーディングエージェントから事前分析を照会
MCP 対応
candor-ts-mcp は影響範囲、到達可能な効果、呼び出し経路、ポリシー判定、ベースライン差分を読み取り専用の MCP ツールとして公開します。事前生成レポートを読み取り、スキャンは行いません。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y candor-tsCANDOR_ROOTLock the server to one workspace: every report prefix is resolved under this root. Unset, the server reads .candor/ relative to the client's working directory.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
16掲載時のツール一覧 · 4 日前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| fn | string | はい | 評価対象の関数/ユニット |
| report | string | いいえ | レポートのプレフィックス(任意。デフォルトは $CANDOR_REPORT) |
詳細
candor-ts
TypeScript 向け candor:関数ごとの副作用を推移的に解析し、決定論的なポリシーゲートで判定します。 candor-ts は TypeScript コンパイラー API を使ってすべての呼び出しを解決し、プロジェクト内の各関数が到達しうる影響(Net、Fs、Db、Exec、Env、Clock、…)を報告します。ファイルをまたぐあらゆる呼び出し経路を通じて継承される影響も対象とし、解決に失敗した箇所(コールバック値、any 型の呼び出し先など)では Unknown を明示します。暗黙に純粋と扱うことはありません。Rust および JVM エンジンと並ぶ、candor-spec の実装です。
サイト: candor.poly.io — 計測されたケースを 5 分で確認できます。
npm install # typescript + @types/node
node scan.mjs <project-dir> # tsconfig.json honored; tests excluded; writes
# <dir>/.candor/report.json + .callgraph.json
node scan.mjs . --policy .candor/policy # the §6.2 gate: exit 1 on violation, 2 if unreadable
node scan.mjs . --gate-json gate.json # + the structured verdict {spec, ok, violations} (§3.3)
CANDOR_BASELINE=saved.json node scan.mjs . # AS-EFF-005 guard: exit 1 if an existing fn GAINED an
# effect vs the saved report; 2 if it can't evaluate
node scan.mjs --version # installed build + spec contract (offline), + upgrade line
node query.mjs show .candor/report db.save 1 # a function's effects (match ladder)
node query.mjs where .candor/report Net 1 # direct sources vs inheritors
node query.mjs callers .candor/report db.save 1 # the blast radius (transitive callers)
node query.mjs map .candor/report 1 # module → effects overview
node query.mjs containment .candor/report # §6.1 boundary-effect dispersion (+ baseline = ratchet)
node query.mjs blindspots .candor/report # the Unknown SOURCES, ranked by blast radius
node query.mjs whatif .candor/report db.save Net policy # pre-edit gate verdict (exit 1)
node query.mjs diff .candor/report baseline 1 # per-function effect delta (exit 1 on a gain;
# a baseline from a DIFFERENT build ⇒ disclosed ⚠ + exit 0)
node query.mjs gate --report dep/.candor/report.json --policy arch.policy --gate-json -
# ⟨0.24⟩ apply a policy to an EXISTING report, NO scan
# (exit 0/1/2) — the supply-chain verb: gate a
# DEPENDENCY's published report without its source.
# Reads that report and nothing else; a rule the wire
# cannot answer (`forbid`, `allow`, a class-scoped
# `deny` with the scoping field absent) is REFUSED
# with exit 2, never evaluated on partial evidence.
リポジトリにチェックインされた .candor/config(spec §3.4)を使うと、環境変数による設定が不要になります。policy arch.policy/baseline <report.json>/deps <report paths> を 1 行ずつ記述し、スキャン対象から親ディレクトリをたどって設定を検出します。相対パスは設定ファイルがあるリポジトリを基準に解決されるため、CI では「リポジトリを指定する」だけで済みます。設定済みの config/policy/baseline が使用できない場合は、必ずエラーになります(終了コード 2)。ポリシーゲートが無効な状態で黙って続行することはありません。
スキャン時の ベースラインガード(AS-EFF-005、spec §7)により、影響の回帰をリリースできないようにします。保存済みレポートを CANDOR_BASELINE(または config の baseline キー)で指定すると、影響が増えた関数があればスキャンは失敗します(終了コード 1)。その記録は --gate-json の判定に加わり、それぞれに origin(existing/new/unknown)が付きます。⟨0.40⟩ ベースラインに存在しない関数は比較対象がないため、影響を持つ新しい関数は失敗し、純粋な新しい関数は合格します。Unknown のみを持つ新しい関数は注記に表示されます。Module.fn キーを使うため、ファイル名を変更すると、ベースライン上では存在しない関数として扱われます。再記録する前に candor diff <this run's report> <baseline> で確認してください。このガードはポリシーゲートと同様にフェイルクローズです。存在するが解析できないベースライン、または異なるエンジンビルドで生成されたベースライン(§2.1 — エンジンのアップグレードによりベースラインは無効になります)がある場合は、評価を行わず終了コード 2 で終了します。ファイルが実際に存在しない場合に限り、1 行の注記を出します(ガードは無効です)。2 つの仕組みを混同しないでください。query diff は読み取り専用の比較機能で、生成元ビルドの不一致を開示し(⚠、終了コード 0)、情報を提供します。一方、スキャン時のガードはゲート品質のフェイルクローズ機能であり、CI で適用すべきものです。セマンティクスはリファレンスエンジン(candor-java)と完全に一致します。
最新の状態に保つには: インストール済みのバージョンを確認し、アップグレードしてください — candor/AGENTS.md §2a。npx -y candor-ts --version はビルド、spec、アップグレード用の 1 行コマンドを表示します(オフラインで動作し、candor が外部に通信することはありません)。
関数名にはモジュール名を . 区切りで付与します(src.db.save)。そのため、ポリシーのスコープを自然に記述できます。TS namespace 内で宣言された関数では、fn と呼び出しグラフのキー(src.util.Ns.helper)に名前空間の各要素が含まれるため、名前空間に対するレイヤーポリシーが適用されます。一方、§2 の hash 結合キーではローカル名のみが使われます。0.8.7 より前のビルドでは各要素が省略されていたため、その境界をまたぐと保存済みのベースラインが無効になります(再生成してください)。pure <scope> ルールが禁止するのはすべての影響であり、Unknown は禁止しません。§4 の信頼マーカーは不確実性であり、影響ではないためです(リファレンスエンジンの candor-java と同じです)。検証できないケースも明示的に除外する必要がある境界には、deny Unknown <scope> を使用してください。
# .candor/policy
deny Net domain # the domain layer reaches no network, even through helpers
pure parse # parsing is effect-free
allow Db in db orders audit_log # the db layer touches ONLY these tables
allow Net in billing api.stripe.com # billing talks ONLY to Stripe
forbid domain -> infra # the domain layer must not depend on infra
このレポートには、宣言によって判定可能になる 4 つのリテラルなサーフェスが含まれます。Net 呼び出しでの hosts、Db 呼び出しでの tables(SQL のテーブル位置。Rust/JVM の抽出器とまったく同じで、さらに、受信側の Repository<T> 型引数を通じて読み取る TypeORM の @Entity("user") 宣言を含みます)、Exec での cmds、Fs でのパス形式の paths です。実行時に計算される値や、推移的に伝播した値から取得することはありません。上記の allow ルールによって適用されます。実際の Nest アプリでは、これによりテーブルレベルのポリシーが有効になります。allow Db in article.service article comments は、user と follows に到達するサービスを検出します。
分類器は厳選されています(他のエンジンと同様、過小報告の場合はその旨を明示します)。Node の組み込み機能(fs、net/http/tls、dns、child_process、worker_threads、node:sqlite、node:vm、process.env、時計)、Web のネットワークグローバル(fetch、XMLHttpRequest、navigator.sendBeacon、WebSocket/EventSource。コンストラクター、send、close は、lib.dom 経由でも @types/node の undici-types 再エクスポート経由でも同じように扱われます)、HTTP/キュー/メール層(axios/got/node-fetch/undici/ws/socket.io/nodemailer、gaxios + googleapis-common + google-auth-library、stripe、@sentry/*、posthog-node、bull/bullmq)、データベースドライバー(pg/mysql2/mongodb/redis/ioredis/sqlite3/better-sqlite3/knex)および ORM 層(TypeORM — @Entity("…") によるテーブル抽出を含みます — Prisma、Mongoose、Sequelize、drizzle-orm)、さらに execa/cross-spawn/shelljs/open、fs-extra/graceful-fs/rimraf/glob/chokidar、dotenv、winston/pino/bunyan が含まれます。リストにない npm パッケージは何も加えません。影響を憶測することはありません。ただし、スキャンではそのパッケージを明示します。レシートのカバレッジ台帳の行(マーカー:classifier doesn't cover)には、コードから呼び出されていることが確認できるものの、分類器が分類も純粋であるとのレビューもしていないパッケージがすべて列挙されます。また、各関数には推移的に到達する invisible リストが付きます。
⟨0.32⟩ Node コアは、厳選リストではなく拒否リストになっている分類器の唯一の部分です。 組み込み機能は有限の集合であるため、各モジュールのサーフェスを確認します。コアメンバーが分類されておらず、かつ副作用がないとレビューされていない場合は、Unknown[native:<module>.<member>] として扱い、決して純粋とはみなしません。そのため、v8.writeHeapSnapshot() は Fs、inspector.open() は Net となり、repl.start()、process.dlopen()、new worker_threads.Worker(file) は、見落とされずに明示されます。
MCP サーバー — エージェントの確かな根拠としての candor
candor-ts-mcp は読み取り専用のクエリをMCPサーバーとして公開するため、
コーディングエージェントは 「これを変更した場合、実行時の影響範囲はどうなるか?」 や 「ネットワークに到達するのは何か?」 と尋ね、
手作業で呼び出しグラフをたどってトークンを消費する代わりに、事前計算されたレポートから確定的な根拠を得られます
(影響範囲に関する質問で、実測約700〜2000倍のトークン削減)。
// in an MCP client config — point it at a report you've already scanned
{ "command": "npx", "args": ["-y", "candor-ts-mcp"],
"env": { "CANDOR_REPORT": ".candor/report.myPkg.scan" } }
ツール:candor_impact(逆方向の影響範囲)、candor_reachable(実行時に実行されるもの)、
candor_where(副作用の範囲)、candor_path(副作用に到達する経路)、candor_callers、
candor_show、candor_map、candor_containment、candor_blindspots、candor_whatif(編集前の
ゲートチェック — 指定されているのに読み取れないポリシーは明確なエラーになり、問題なしという判定にはなりません)、candor_gate
(チェックイン済みの.candor/configポリシー判定)、candor_diff/candor_gains(ベースラインとの差分)。
それぞれ任意のreportプレフィックスを指定できます(指定しない場合は$CANDOR_REPORT)。--root <dir>を指定すると、
サーバーを単一のワークスペースに固定します。サーバーはクエリ専用で、スキャンは行いません(アナライザー自身の境界、仕様
§7.12:エージェントまたはフックがレポートを生成し、サーバーはそれを読み取るだけで、Fs のみを使用します)。クエリロジックは共有の
query-core.mjsで、CLI と同じ回答を返します。
candor-lsp は、LSP ネイティブの任意のエディター(helix、
neovim。JetBrains プラグインには同梱されています)で、コードのある場所に同じレポートを表示します。副作用のある関数ごとの CodeLens
(⚡ Db, Net · blast radius 12)、ホバーでの由来情報(継承された副作用が実行される箇所までのホップチェーン)、および
リポジトリのポリシー判定を診断として表示します。MCP サーバーと同様、これは純粋なレポート利用者であり、任意のエンジンの
レポートを扱えます。また、スキャンは行いません。(レポートから計算される両方のゲート判定は参考情報です。エンジン自身の
--gate-json実行は、マスクされた/不完全なリテラルの範囲も失敗扱いにし、CI での正式な判定方法となります。)
編集前の疑問にも、その場で答えます。関数内で、その関数がまだ実行していない境界の副作用ごとにコードアクションが表示されます — candor: what if handler performed Net? — これは
candor-ts-query whatif/candor_whatifと同じ whatif を実行し(影響範囲+発火するポリシールール)、
判定をメッセージと関数位置の一時的な診断として表示します(ファイルを次に開くか保存すると消去されます。ポリシーが見つからない場合はその旨を示し、影響範囲のみを報告します)。
通常のtextDocument/codeAction+workspace/executeCommand(candor.whatif)を使うため、helix、neovim、VS Code、JetBrains(LSP4IJ 経由)で
変更なしに動作します。
ライブのループ — candor-ts-watch はエージェントが編集するたびにレポートを最新の状態に保つため、回答は古いスナップショットではなく、現在のコードに基づきます。
candor-ts-watch ./src --out .candor/report # re-scans only when a tracked source actually changes
プロジェクトのソースをコンテンツハッシュで追跡し、実際に変更があった場合に再スキャンします(変更のない保存や無関係な書き込みでは何も起きません)。MCP サーバーが読み取るものと同じプレフィックスに書き込みます。つまり、エージェントが編集 → ウォッチャーがレポートを更新 → エージェントがcandor_impactを尋ね、編集後の回答を得る。 さらに、レポートが最新であることだけでなく、副作用の範囲に対して編集が何をもたらしたかを示す編集差分も報告します(re-scanned (1 changed: app.ts) — Δ f +Net)。これにより、エージェントは自身の変更の結果を把握できます。v1 では変更ごとに完全な(健全性を保つ)スキャンを実行します。より踏み込んだパフォーマンス最適化、つまりプロジェクト全体ではなく変更されたファイルのサブグラフだけを再解析する機能は、段階的に導入する次のステップです(コンテンツハッシュのゲートはその第一段階です)。
信頼性に関する契約(spec §4)
candor-tsが解決できないものはすべて、暗黙のpureではなくUnknownを返すように設計されています。既知の未解決箇所はcandor-specのSOUNDNESS.mdに記載されています(例:R780、R803、R804)。関数型のパラメーターやフィールドが呼び出される場合、呼び出し先がany型の場合、解決先が本体ではなく型である場合などです。
未キュレーションの依存関係は、package.jsonでエフェクトを宣言することで、Unknown/暗黙のpureを回避できます("candorEffects": ["Net"]。spec §5.1のエフェクトマニフェスト)。candor-tsはこれを「宣言済み・未検証」の階層として読み取ります。パッケージの呼び出しは宣言された集合に分類され、カバレッジ台帳の盲点ではなくなります。§1の語彙にない名前があると、宣言は明示的に無効になります(タイプミスによって対象範囲が暗黙に狭まることはありません)。また、candor-ts-query gains <cur> <base>は、サプライチェーンの差分、つまり2つのレポート間で対象範囲に追加されたエフェクトを示します。
実環境での結果は、rimraf(50ファイル、55関数を解析)で測定されています。DIスタイルのfs注入により、多くの関数がUnknownを返すことが明らかになります。これは契約が機能していることを示すもので、ノイズではありません。レポートが示すのは「到達しうる」であり、「到達する」ではありません。リテラルが存在しないことは、不在の主張にはなりません。
フレームワークの依存性注入(spec §2の有界CHA条項)。 rimrafの注入は関数型パラメーターであり、Unknownを返します。DI コンテナーは別途、実行済みフィクスチャを使ってNestJS 10で測定します。
-
トークンがプロジェクトのCLASSであるプロバイダーエントリー
{ provide: Store, useClass | useValue | useFactory | useExisting: … }は変換として読み取られます。代替実装がStoreの候補に加わるため、this.store.save()にはStore.saveと代替実装のsaveの和集合が適用されます(SOUNDNESS R1061 — 以前は暗黙でしたが、3つの形式すべてを実行済みです)。 -
INTERFACEで型付けされたパラメーター(
@Inject("SINK") sink: Sink)は、プロジェクト内で参照可能なSinkの実装をディスパッチ対象とします。そのため、implements SinkのuseClassは適用されます。何も実装していないuseValueのオブジェクトリテラルはUnknownを返します(dispatch:)。 -
TOKENはチェッカーの型を介して読み取られるため、
const TOK = Store、インポートされたトークン、TOKENS.store、[Store]に対する.mapはいずれもStoreを識別します。ヘルパーのtoken/implパラメーターは、参照可能な呼び出し箇所を介して読み取られます。Function/Type<any>型のトークンは、代替実装を代入可能なすべてのプロジェクトクラスに加えます(R1061)。 -
スキャンで識別できない代替実装(
Type<Store>型のuseClass: impls[i]、any型のuseValue)の場合、トークンクラスに対するディスパッチはUnknownを返します(dispatch:)。
**名前付きの見落とし:**スキャンからまったく見えないプロバイダーリスト(未スキャンのパッケージからインポートされたもの)と、依存関係が所有するトークンに対して依存関係から提供される代替実装です。DIを多用するサービスには、deny <E> Unknownとdeny <E>の両方でゲートを設定してください。
エンジン間の一貫性 — 機械検証済み
candor-tsは、specのエンジン間適合性テストスイートによって連携する4つのコードエンジンの1つです(リファレンスエンジンのcandor-java、Rustエンジン、candor-swiftとともに)。このスイートには、共有エフェクト集合オラクル、§6.2のポリシー文法バッテリー(allow Dbを含む)、§3.1のクエリー形状とマッチラダーのチェック、ゲートの終了コードに関する契約、そしてpureとUnknownの裁定までの新しい部分(PART 16)が含まれます。specへのプッシュごとに、各エンジンは同じ回答を返さなければなりません。
解析コアが実装する内容(および仕様の根拠)
| 項目 | 仕様の参照元 |
|---|---|
コンパイラー API(getResolvedSignature)を使ってすべての呼び出しを解決し、構文からは決して解決しません | CLASSIFIER §1 |
分類器は、解決された対象のモジュールをマッピングします(node:fs→Fs、node:net→Net、…) | CLASSIFIER §2、TS notes |
process.env のプロパティ読み取り → Env、Date.now → Clock | SPEC §1 |
| ローカルエッジ(ファイル間)+最小不動点伝播 | SEMANTICS §5a |
| クロージャーの本体は、最も近い外側の関数に帰属します | SEMANTICS §2 |
型(関数型フィールド/パラメーター)に解決される呼び出し → Unknown。暗黙に pure とはしません | SPEC §4 |
| 一致しない外部呼び出しは何も寄与しません(キュレーションされた分類器に関する注意点) | SEMANTICS §8 C1 |
リテラルサーフェス hosts/cmds/paths/tables。リテラル読み取りのみ | SPEC §2 |
{ candor: { version, toolchain, spec: "0.40" }, functions } エンベロープ。pure 関数は省略 | SPEC §2/§2.1 |
| 呼び出しグラフのサイドカー。解析対象のすべての関数をキーに含めます | SPEC §2.2 |
| ゲート:AS-EFF-006/008/009。読み取れないポリシーには明確にエラーを出します | SPEC §6.2 |
起源:導出可能性の証明
このエンジンは、新しい言語の実装を仕様だけから導出できるかを実行可能な形で確かめるため、Rust や JVM のソースを参照せず、仕様書(SPEC.md、SEMANTICS.md、CLASSIFIER.md)のみを基に、意図的に最小限の単一ファイルのスライスとして作成されました。共有オラクルで 20/20 を達成しています。クリーンルームでの導出という主張はコミット a29b152 の時点で固定されています。それ以降のすべて(マルチファイルプロジェクト、クエリーサーフェス、ゲート、リテラルサーフェス)は仕様に基づいて実装されていますが、事後的なものであり、その保証はクリーンルーム由来ではなく、上記の適合性差分です。元の導出で必要だった唯一のエンジン修正(関数-型宣言に到達する呼び出しが、§4 を適用するまでは pure として読み取られていたこと)は、今も証明の要点です。修正の根拠は「§4 の記述どおりにする」であり、「Rust のソースを読む」ではありません。
ステータス
candor-spec 0.40 に準拠しています(パッケージのバージョンは package.json にあります)。解析コア、ゲート(--policy/--gate-json/.candor/config)、§3.1 の完全なクエリーサーフェス(containment、blindspots、--include-unknown ディスパッチフロンティア、⟨0.24⟩ gate --report を含みます。これは既存のレポートにゲートを適用するもので、判定結果は scan --policy とバイト単位で同一です)、MCP サーバー、LSP サーバー、ウォッチループが実装済みで、動作テスト済みです(npm test — 6 つのハーネスで実行する動作テストスイート)。また、検証可能な効果を伴うサウンドネス・ファジング済みです(node fuzz.mjs — 仕様 §7.13:エンコードされたすべての呼び出し形式を通る効果チェーンを生成し、pure として誤って扱われるものがあれば失敗します)。Rust/JVM/Swift エンジンとの適合性も維持しています。npm の分類器層は、意図的にキュレーションされており、ケースごとに拡充を続けています。エントリーポイント(Nest/Next の対象)、unknownWhy の起点、reachable、パッケージ間の継承(CANDOR_DEPS と仕様 §2 の hash。§2.1 に従ってバージョンを信頼)、--allow-js はすべて実装済みです。npm では npx -y candor-ts <dir> を実行します。リリースごとの詳細(⚠ はレポート/判定に影響する変更を示します):CHANGELOG.md。
開発
ビルド手順は不要です。エンジンは Node 上で直接動作します。
npm install
npm test # the full CI gate: lint + unit (node:test) + behavioural + MCP + watch + the
# fabrication probe + the §7.13 soundness fuzzer
npm run test:unit # just the native unit tests — the query algebra + policy DSL + the scan-core
# classifier/literal leaves (query-core / policy / scan-core)
npm run lint # eslint (the recommended ruleset; the CI lint gate)
node scan.mjs <dir | file.ts | tsconfig.json> --out .candor/report # scan a project
pure コアはインポート可能なモジュールに分割されています。query-core.mjs(§3.1 のクエリー)、policy.mjs(§6.2 の DSL+リテラルマッチャー)、scan-core.mjs(分類器+SQL/コマンド/ホスト抽出器)としてユニットテストを直接実行できます。TS コンパイラーを使った走査は scan.mjs に残されています。
エージェント:candor を MCP サーバーとしてインストールする
candor-ts は candor の読み取り専用クエリー機能を MCP サーバーとして提供するため、エージェントは「これを変更した場合の影響範囲は?」や「ネットワークに到達するのは何?」と尋ねると、該当箇所を grep する代わりに、事前計算されたレポートから決定的な回答を得られます。
npx -y candor-ts --mcp # the server, over stdio
candor mcp install # or let the umbrella write/merge .mcp.json for you
登録手順はすでにインストールしたパッケージから確認できます。candor mcp --help は .mcp.json のスニペットと claude mcp add の行を表示し、candor mcp install はそれらを書き込みます。取得、改ざん、自動実行されるリモートファイルはなく、エージェントに、すでにインストールを選んだもの以外を実行するよう指示することもありません。
検出は受動的です。マニフェストは server.json で、公式の MCP Registry に公開されているため、クライアントやディレクトリはスクリプトを渡されなくても candor を見つけられます。
レジストリは**package.json の mcpName フィールド**から名前空間の所有権を検証します。これが npm の仕組みです。(mcp-name: README マーカーは crates.io の慣例であり、ここでは何の効果もありません。初期版ではこれを使用していたため、レジストリは missing required 'mcpName' field というエラーで公開を拒否しました。以下の行は、人が名前空間を grep する方法としてのみ残しています。)
mcp-name: io.github.tombaldwin/candor
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。