REST API
bca-web serves the big-code-analysis library over HTTP. This page is the reference for a client author: every route, its request and response shape, the error envelope, and the status codes. For running the daemon (flags, environment variables, resource limits, and the trust boundaries to respect before exposing it) see Operating bca-web; for end-to-end curl examples see Driving the REST API.
The source-in-body endpoints remove comments, list function spans, return the tree-sitter syntax tree, and compute metrics. Three further endpoints compute change-history (VCS) metrics over a repository on the server’s filesystem.
Running the server
bca-web --host 127.0.0.1 --port 9090
--host: the address to bind (default127.0.0.1).--port: the TCP port (default8080).-j: the number of worker threads (optional).--cors: enable CORS for browser-based tooling (off by default).
CORS
By default bca-web emits no CORS headers: a browser script served from a different origin cannot read the API’s responses. This keeps a local bca-web (the default 127.0.0.1 bind) from exposing its repository paths and metrics to any website the operator happens to be visiting.
オプトインするには --cors を渡します。引数は オリジン の明示的なカンマ区切り許可リストです。許可リストに含まれるオリジンだけが Access-Control-Allow-Origin ヘッダーを受け取り、マッチしたオリジンがそのままエコーバックされます(それ以外のオリジンからのリクエストはヘッダーを受け取らず、ブラウザによってブロックされます):
bca-web --cors https://app.example,https://tools.example
すべてのオリジンに Access-Control-Allow-Origin: * で応答するには、リテラルの * を渡します:
bca-web --cors '*'
全開放の * は、サーバーのメトリクスとリポジトリのパスをあらゆるオリジンに公開するため、信頼できるネットワークでのみ使用してください。
CORS が有効な場合、プリフライトの OPTIONS リクエストには 204 No Content で応答し、Access-Control-Allow-Origin、Access-Control-Allow-Methods(そのリソース自身が受け付けるメソッド。Allow ヘッダーが広告するのと同じ集合)、および Access-Control-Allow-Headers(リクエストの Access-Control-Request-Headers をエコーし、素のプローブに対しては Content-Type, Accept)を付与します。この API には認証も Cookie もないため、Access-Control-Allow-Credentials は決して送信されません。
API バージョニング
すべてのエンドポイントは /v1 プレフィックスの下にマウントされます(例えば /v1/metrics)。ルートの全集合は /v1/ping、/v1/version、/v1/languages、/v1/ast、/v1/comment、/v1/function、/v1/metrics、/v1/vcs、/v1/vcs/trend、/v1/vcs/jit、およびルートインデックス /v1 です。プログラムから発見するには GET /v1 を使用します(ルートインデックス を参照)。
以前の 1.x リリースが非推奨エイリアスとして提供していたプレフィックスなしのパス(/metrics、/comment、/ast、/、…)は 2.0 で削除されました。現在それらをリクエストすると 404 が返ります。どこでも /v1 形式を使用してください。
エラーレスポンス
Errors are reported with an HTTP status code, not inside a 200 body. Every error (on the JSON endpoints, the raw/octet-stream endpoints, and the 415/405/404 fallbacks alike) returns one uniform machine-readable JSON body, so clients parse a single error shape regardless of the success content-type:
{
"error": "human-readable message",
"error_kind": "stable_machine_token",
"id": "echoed-request-id"
}
error is the specific human-readable cause; error_kind is a stable snake_case machine token (unknown_field, unsupported_language, bad_request, parse_timeout, and so on) so clients branch on the cause without string-matching the prose (issue #631). The token vocabulary is closed and governed by STABILITY.md.
The id key is always present. It carries the client-supplied correlation id when the request had one (the JSON endpoints), and an empty string otherwise: the octet-stream / query endpoints carry no id, and the content-type / method / not-found fallbacks, like any request whose body failed to parse before the id was read, have no parsed id to echo.
ステータスコード:
400 Bad Request: a malformed body or query parameter: invalid JSON, a missing required field, an unrecognised key (the strictdeny_unknown_fieldsparse, including the removedunitflag; see Compute metrics below), or ascopevalue that is notfull/file.422 Unprocessable Entity: thefile_nameextension (and content sniffing) maps to no supported language. The route matched and the body parsed; only the submitted entity cannot be processed. The response carries the stable machine token"error": "unsupported_language"; queryGET /v1/languagesfor the supported set. (Before 2.0 this was a404, indistinguishable from an unknown URL; see issue #634.)404 Not Found: the URL matches no endpoint.415 Unsupported Media Type: a knownPOSTendpoint received aContent-Typethat is neitherapplication/jsonnorapplication/octet-stream(acharsetparameter is allowed).405 Method Not Allowed: a known endpoint was called with the wrong HTTP method. The analysis endpoints acceptPOST;/v1/ping,/v1/version,/v1/languages, and/v1acceptGETandHEAD.406 Not Acceptable: the requestAcceptheader named only media types the server cannot produce. The structured analysis endpoints serveapplication/json,application/yaml, andapplication/cbor; any other concrete type (application/xml, say) is a406carrying thenot_acceptabletoken. See Content negotiation.413 Payload Too Large: the request body exceeded the server limit.500 Internal Server Error: metric computation or AST construction failed for an otherwise-valid request, or a/vcshistory walk failed on the server side. This also covers a response too deeply nested to serialize (serialize_failed); see Nesting limits below.503 Service Unavailable: the parse pool is saturated by orphaned (timed-out) tasks; retry later.504 Gateway Timeout: the parse (or history walk) exceeded the server’s configured deadline.
ネストの上限
Responses are trees, and the server’s serializers recurse through nested response trees, one native stack frame per level. A deeply nested response could overflow the stack, which aborts the whole process and takes every in-flight request with it. So the server refuses to serialize past a fixed depth and answers 500 with the serialize_failed token:
| レスポンス | 上限 | 1 レベルとみなされるもの |
|---|---|---|
/metrics | 128 | ネストした関数スペース 1 つ(関数の中の関数の中の…) |
/ast | 512 | AST ノード 1 つ |
Both limits sit far above anything real source reaches. Across the integration corpus under tests/repositories (TensorFlow, DeepSpeech, serde, and others) the deepest AST measured 188 nodes and the deepest function-space nesting 10 when the limits were set (#1056). The /metrics limit is also more generous than the read side: a JSON document of nested spaces cannot be parsed back past ~61 levels, because serde_json’s own 128-level recursion limit charges two levels per space.
コンテンツネゴシエーション
The structured analysis endpoints (/v1/ast, /v1/comment in its JSON variant, /v1/function, /v1/metrics, /v1/vcs, /v1/vcs/trend, and /v1/vcs/jit) choose their response serialization from the request Accept header, mirroring the CLI’s -O json|yaml|cbor outputs. The same value serializes byte-for-byte identically whether it comes from the CLI or the server.
Accept の値 | レスポンスの Content-Type |
|---|---|
なし、*/*、application/*、application/json | application/json |
application/yaml(または text/yaml、application/x-yaml) | application/yaml |
application/cbor | application/cbor |
| その他の任意の具体的なタイプ | 406 Not Acceptable |
ルール:
- JSON is the default. Without an
Acceptheader, the server returns JSON: the same body andContent-Typeearlier releases always returned, so existing clients need no change. JSON entries and wildcards (*/*,application/*) make JSON a candidate. qweights are honored. The supported candidate with the highestqweight wins (Accept: application/json;q=0.5, application/yaml;q=0.9returns YAML), with ties resolved by list order;q=0refuses a type.- サポートされないタイプは
406になり、 暗黙の JSON フォールバックは行われません。ボディはerror_kind: "not_acceptable"を持つ統一の{error, error_kind, id}エンベロープで、メッセージにはサポートされるメディアタイプが列挙されます。 - Only structured serializations are offered. TOML and CSV are excluded: TOML is awkward for the deeply nested space tree and CSV is flat and tabular. The error-envelope body is always JSON and the
/v1/commentoctet-stream variant (raw byte-in / byte-out) is always raw bytes; neither negotiates. The introspection routes (/v1,/v1/version,/v1/languages) return JSON metadata only.
# YAML メトリクス
curl --silent \
--header 'Content-Type: application/json' \
--header 'Accept: application/yaml' \
--data '{"file_name": "foo.py", "code": "def f():\n pass\n"}' \
http://127.0.0.1:8080/v1/metrics
# CBOR メトリクス(バイナリ。デコーダーにパイプしてください)
curl --silent \
--header 'Content-Type: application/json' \
--header 'Accept: application/cbor' \
--data '{"file_name": "foo.py", "code": "def f():\n pass\n"}' \
http://127.0.0.1:8080/v1/metrics --output metrics.cbor
エンドポイント
1. Ping the server
Checks that the server is running.
リクエスト:
GET http://127.0.0.1:8080/v1/ping
レスポンス:
- Status code:
200 OK - ボディ:空。
Use curl -sf http://127.0.0.1:8080/v1/ping && echo ok to script a liveness check; -f makes curl exit non-zero on any HTTP error.
2. Remove comments
Removes comments from the submitted source. The endpoint accepts two Content-Type variants: application/octet-stream for raw byte-in / byte-out, and application/json for a JSON envelope.
リクエスト:
POST http://127.0.0.1:8080/v1/comment
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code with comments"
}
id: a correlation identifier for the request. Optional (issue #645); omitting it defaults to an empty string (treated as “no correlation id”).file_name: the name of the file being analyzed.code: the source code with comments.
レスポンス(JSON バリアント):
{
"id": "unique-id",
"language": "cpp",
"code": "print"
}
The response envelope reports id, the detected language (the canonical lowercase slug; see Compute metrics below), and the code result key. The code field is a string holding the stripped source: the request code arrived as a JSON string, so the stripped output is guaranteed valid UTF-8 and is handed back as a string, matching the request and every other JSON endpoint. The application/octet-stream variant returns the stripped source as the raw response body (no envelope), which suits binary-faithful round-trips and shell pipelines; its errors still use the uniform JSON error body above.
ソースに除去可能なコメントが含まれない場合、どちらのバリアントも 200 ステータスと空のペイロードで空の結果を通知します。JSON バリアントは "code": ""(空文字列)を返し、octet-stream バリアントは空のボディを返します。したがって、要求された Content-Type にかかわらずステータスコードとエンベロープの形は同一です。octet-stream バリアントは 204 No Content ではなく空の 200 ボディを返します。
3. Retrieve function spans
Returns the line span of every function in the submitted source.
リクエスト:
POST http://127.0.0.1:8080/v1/function
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code with functions"
}
id: a correlation identifier for the request. Optional (issue #645); omitting it defaults to an empty string (treated as “no correlation id”).file_name: the name of the file being analyzed.code: the source code with functions.
レスポンス:
{
"id": "unique-id",
"language": "cpp",
"spans": [
{
"name": "function_name",
"start_line": 1,
"end_line": 10
}
]
}
The envelope reports id, the detected language slug, and the spans result key. name is null when the parser could not resolve the function’s name from the AST (an anonymous or malformed definition). A null name is the malformed-span signal.
4. AST の取得
Returns bca’s JSON representation of the parsed syntax tree, as a recursive node tree. The dump normalizes some node kinds and flattens some constructs, including string literals.
リクエスト:
POST http://127.0.0.1:8080/v1/ast
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code to parse",
"comment": false,
"span": true
}
id: a correlation identifier for the request. Optional; omitting it defaults to an empty string (treated as “no correlation id”).file_name: the name of the file being analyzed.code: the source code to parse.comment: whentrue, comment nodes are omitted from the tree. Optional; defaults tofalse.span: whentrue, each node carries its sourcespan; whenfalse,spanisnull. Optional; defaults tofalse.
id、comment、span は省略可能で、デフォルトは上記のとおりです(issue #645)。file_name と code は必須です。未知のキーは 400 で拒否されます(issue #633)。
レスポンス:
{
"id": "unique-id",
"language": "rust",
"root": {
"type": "source_file",
"value": "",
"span": { "start_line": 1, "start_col": 1, "end_line": 2, "end_col": 1,
"start_byte": 0, "end_byte": 13 },
"field_name": null,
"children": [
{
"type": "function_item",
"value": "",
"span": { "start_line": 1, "start_col": 1, "end_line": 1, "end_col": 13,
"start_byte": 0, "end_byte": 12 },
"field_name": null,
"children": [
{
"type": "identifier",
"value": "main",
"span": { "start_line": 1, "start_col": 4, "end_line": 1, "end_col": 8,
"start_byte": 3, "end_byte": 7 },
"field_name": "name",
"children": []
}
]
}
]
}
}
The envelope reports id, the detected language slug, and root, the root AST node. Each node carries:
type: tree-sitter 文法のノード種別です(文法固有です。どの文法が生成したかはlanguageスラッグで分かります)。value: リーフ/名前付きトークンのソーステキストです(内部ノードでは空です)。span: a{ start_line, start_col, end_line, end_col, start_byte, end_byte }object (lines and columns 1-based; byte offsets 0-based and half-open, indexing into the submittedcode), ornullwhenspanwasfalse. These span keys use the*_linevocabulary shared with/functionand/metrics(issue #638 renamed the former*_rowkeys).field_name: the tree-sitter grammar field through which the parent reaches this node (name,left,body, and so on), ornullfor the root, anonymous tokens, and unfielded children.children: ノードの子ノードです(再帰的に続きます)。
Unlike the metric and function endpoints, the AST endpoint reports node coordinates over the exact bytes the client submitted: the source is not EOL-normalised, so spans line up with the client’s own copy (issue #640).
5. Compute metrics
Computes the maintainability metrics for the submitted source.
リクエスト:
POST http://127.0.0.1:8080/v1/metrics
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code for metrics",
"scope": "full"
}
id: a correlation identifier for the request. Optional (issue #645); omitting it defaults to an empty string (treated as “no correlation id”).file_name: the filename of the source code file.code: the source code to analyze.scope: how much of the space tree to return.full(the default) returns the complete nested space tree: the file-level root plus a recursivespaceslist for every function, class, and other unit.filereturns only the file-level root with itsspaceschildren cleared. This field replaces the pre-2.0 booleanunitflag (issue #638); sending the oldunitkey now fails with400.
ペイロードは厳密に検証されます。認識されないキー(タイプミスや、削除済みの unit)は 400 と統一 JSON エラーボディで拒否され、問題のフィールド名が示されます(issue #633)。
application/octet-stream バリアントでは、ソースは生のリクエストボディで渡し、scope はクエリパラメータとして指定します(?file_name=…&scope=full)。パラメータを省略するとデフォルトの full になり、認識されない値は 400 で拒否されます。
レスポンス:
{
"id": "unique-id",
"language": "rust",
"root": {
"name": "sample.rs",
"start_line": 1,
"end_line": 7,
"kind": "unit",
"spaces": [
{
"name": "double",
"start_line": 1,
"end_line": 7,
"kind": "function",
"spaces": [],
"metrics": {
"cyclomatic": { "sum": 2, "average": 2.0, "min": 2, "max": 2 },
"loc": { "sloc": 7, "ploc": 7, "lloc": 1, "cloc": 0, "blank": 0, "dloc": 0 },
"nom": { "functions": 1, "closures": 0, "total": 1 }
}
}
],
"metrics": { "...": "the same metric block, aggregated over the file" }
}
}
The response envelope reports id, the detected language slug, and root, the single file-level space object (issue #638 renamed this key from the misleading plural spaces). root carries name (the request file_name), the start_line / end_line span, a kind discriminator (unit, function, class, …), its metrics block, and a recursive spaces list of child units. The example above is trimmed: each real metrics block contains every metric family (cyclomatic, cognitive, halstead, loc, nom, nargs, nexits, tokens, mi, abc, and, on the file root and container spaces, wmc, npm, and npa) with the same nested fields the bca CLI emits. When scope is file, root.spaces is an empty list.
The language value is the canonical lowercase slug (rust, cpp, csharp, tsx), the same token the language vocabulary accepts, not a human-pretty display name. Every analysis endpoint (/ast, /comment, /function, /metrics) reports this language field so clients can confirm which grammar was selected.
6. Server and library version
実行中のサーバーのバージョンと、そのビルドに使用された big-code-analysis ライブラリのバージョンを報告します。
リクエスト:
GET http://127.0.0.1:8080/v1/version
レスポンス:
{
"server": "2.4.0",
"library": "2.4.0"
}
7. Supported languages
対応言語と、登録されているファイル拡張子の一覧を返します。名前は正規の小文字スラッグです。一覧と拡張子はライブラリの言語テーブルから取得され、ハードコードされることはありません。
リクエスト:
GET http://127.0.0.1:8080/v1/languages
レスポンス:
{
"languages": [
{ "name": "cpp", "extensions": ["cpp", "cc", "hpp", "..."] },
{ "name": "rust", "extensions": ["rs"] }
]
}
他のすべてのエンドポイントと同様に、/version と /languages は /v1 プレフィックスの下でのみ提供されます。プレフィックスなしの 1.x エイリアスは 2.0 で削除されました(API バージョニングを参照)。
8. ルートインデックス
Returns a machine-readable index of every registered route (its path, the HTTP methods it accepts, and a one-line description) so clients can discover the API surface without scraping this chapter. The index is generated from the same route table the server registers, so it cannot drift from the live routing.
リクエスト:
GET http://127.0.0.1:8080/v1
レスポンス:
{
"service": "bca-web",
"version": "2.4.0",
"routes": [
{ "path": "/v1", "methods": ["GET", "HEAD"], "description": "This route index." },
{ "path": "/v1/metrics", "methods": ["POST"], "description": "Compute maintainability metrics for the source." }
]
}
service は常に bca-web です。version は GET /v1/version の server フィールドと一致します。以前のリリースでこのエンドポイントのエイリアスとして提供されていたプレフィックスなしのルート / は、2.0 で削除されました。
変更履歴(VCS)メトリクス
Three endpoints expose the change-history (version-control) metrics, the same numbers bca vcs computes from the CLI. Unlike every other endpoint, these analyse a git repository already present on the server’s filesystem rather than source code carried in the request body: VCS metrics derive from commit history, which has no in-request representation.
Operator warning:
repo_pathis a trust boundary. Therepo_pathfield is a server-side filesystem path. These endpoints make the server walk any git repository it can read and return that repository’s relative file paths, churn, and author signals. This is materially different from the source-in-body endpoints, which only ever see code the client sends. The optionalcache_dirfield is a second caller-supplied server-side path that grants a write capability: with caching enabled (the default), the server creates directories and writes JSON cache files under it (<cache_dir>/<repo>/<head_sha>.json), so a caller controllingcache_dircan direct the server to write cache files at any path the server process can write to. (cache_diris accepted only by/vcs;/vcs/trendand/vcs/jitdo not cache.) The endpoint’s filesystem reach is therefore an arbitrary read of any readable git repository and an arbitrary write of cache files under any writable path. Do not expose/vcs,/vcs/trend, or/vcs/jitto untrusted clients without an authorization layer in front ofbca-web. The default127.0.0.1bind keeps them local. Each walk runs under the same parse-timeout and blocking-pool guard as the analysis endpoints.
All three endpoints are POST-only, accept application/json, echo the request id, and report errors with the uniform {error, error_kind, id} body; its error_kind tokens are the vcs_* family (vcs_not_a_repository, vcs_invalid_window, and so on). A client mistake is a 400: a repo_path that does not exist or is not a git working tree (both carry vcs_not_a_repository), an unresolvable ref/commit, a malformed or non-diff diff, an unusable author_hash_key (vcs_invalid_author_hash_key), or a malformed window / timestamp / formula / file-type / threshold / trend parameter. A failure of the history walk itself is a 500. A nonexistent repo_path is a typo, the most common client error here, so it answers 400 like a path that exists but is not a repository, not a 500 (issue 653).
9. Rank files by risk: /vcs
リポジトリの履歴を一度だけ走査し、複合リスクスコアでランク付けしたファイル一覧を返します(issue #328)。
リクエスト:
POST http://127.0.0.1:8080/v1/vcs
ペイロード:
{
"id": "unique-id",
"repo_path": "/srv/repos/my-project"
}
repo_path is required; every other field is optional and defaults to the bca vcs default. id is optional too (issue #645); omitting it defaults to an empty string, echoed back unchanged. The optional fields are:
long_window/recent_window: window specs such as12moor90d. Defaults12mo/90d.top: リスク上位 N 件のファイルのみを保持します。未指定の場合のデフォルトは50(bca vcs --topのデフォルト)で、明示的に0を指定するとすべてのファイルを返します。ref: 解析対象のリビジョンです(デフォルトはHEAD)。risk_formula:weighted(デフォルト)またはpercentileです。file_types:metrics(default: only files bca has metrics for),all(every tracked text file), or a comma-separated extension allow-list (rs,py).full_history: 第一親のみではなく、完全な DAG を走査します。include_merges: マージコミットを含めます。follow_renames: リネームを追跡します(デフォルトtrue)。exclude_bots: ボットの識別情報を除外します(デフォルトtrue)。bot_pattern: ボット作者を除外する正規表現を上書きします。as_of: reference “now” (RFC 3339 /@unix/ git date) for snapshots.emit_author_details: SHA-256 でハッシュ化した作者の識別情報を出力します。author_hash_key:emit_author_detailsを鍵付き HMAC-SHA256 に強化する秘密鍵です(emit_author_detailsが必要です。空の鍵、またはフラグなしでの指定は400になります)。include_deleted: 対象の ref で削除済みのファイルを含めます。bus_factor_threshold:(0, 1)の範囲で指定するバスファクターのカバレッジしきい値です(デフォルト0.5)。no_cache: このリクエストで永続的な変更履歴キャッシュを無効にします(デフォルトfalse)。cache_dir: サーバー側のキャッシュディレクトリを上書きします。
レスポンス:
{
"id": "unique-id",
"vcs_schema_version": 2,
"risk_score_version": 2,
"long_window_days": 365,
"recent_window_days": 90,
"truncated_shallow_clone": false,
"vcs_aggregate": { "...": "directory- / repo-level bus factor" },
"files": [
{
"path": "src/main.rs",
"vcs": {
"commits_long": 12,
"commits_recent": 3,
"churn_long": 540,
"churn_recent": 80,
"authors_long": 4,
"authors_recent": 2,
"risk_score": 1.42
}
}
]
}
files は vcs.risk_score の降順に並びます。各エントリはリポジトリ相対の path と、入れ子の vcs メトリクスブロック(bca vcs が出力するものと同じ形式、issue #684)を持ちます。含まれるのは、長期・直近ウィンドウでのコミット数とチャーン数、作者数、所有権シェア、バースト、バグ修正 / セキュリティ修正 / リバートの各カウント、経過期間、変更エントロピーと共変更エントロピー、そして合成値の risk_score です。hotspot_score とハッシュ化された author_ids は、計算可能な場合 / 要求された場合にのみそのブロック内に現れます。4 つの定数スタンプ vcs_schema_version、risk_score_version、long_window_days、recent_window_days はトップレベルに一度だけ置かれ、行ごとには現れません(issue #635)。vcs_aggregate はディレクトリレベルおよびリポジトリレベルのバスファクターを持ちます(issue #332)。
10. Historical trend: /vcs/trend
変更履歴メトリクスを等間隔の複数時点でサンプリングし、ファイルごとの時系列を返します(issue #333)。応答はランク付けされたスナップショットではなく系列であるため、/vcs とは別のルートになっています。
リクエスト:
POST http://127.0.0.1:8080/v1/vcs/trend
Payload: every /vcs field above except the cache controls (no_cache / cache_dir), plus the three below. Trend does not use the persistent cache, so sending either cache field is a 400 (issue #961) rather than a silent no-op.
points: 等間隔のサンプル点の数(>= 2)。省略時は12(bca vcs trend --pointsのデフォルト)になります。span: サンプル点全体がカバーする遡及期間(デフォルト12mo)。top_deltas: 改善 / 悪化それぞれのリストに載せる上位 N ファイル数。未指定の場合は10、明示的な0は全件を返します。
レスポンス:
{
"id": "unique-id",
"trend_schema_version": 1,
"vcs_schema_version": 2,
"risk_score_version": 2,
"long_window_days": 365,
"recent_window_days": 90,
"truncated_shallow_clone": false,
"as_of_points": [1704067200, 1711929600],
"files": {
"src/main.rs": [ { "as_of": 1704067200, "vcs": { "risk_score": 1.1 } }, null ]
},
"deltas": { "improved": [], "regressed": [] }
}
as_of_points はサンプル時刻を古い順に列挙します。files 内の各ファイルの配列はこれと 1:1 で対応し、その時点でファイルがまだ存在しなかった場合は null 要素になります。存在する各要素は { "as_of": ..., "vcs": { ... } } の形で、その時点のそのファイルの VCS ブロックが vcs の下に入れ子になります(issue #684)。4 つの定数スタンプはトップレベルに一度だけ置かれ、時点ごとには現れません(issue #635)。deltas は、系列全体でのリスクスコアの変動に基づいて、最も改善したファイルと最も悪化したファイルをランク付けします。
11. Just-in-time risk: /vcs/jit
Scores the just-in-time risk of a single change: either one commit on a server-side repository, or an arbitrary unified diff carried in the request body (issues #331 / #580). The two modes are mutually exclusive.
コミットモードは repo_path 上のコミットをスコアリングします:
{
"id": "unique-id",
"repo_path": "/srv/repos/my-project",
"commit": "HEAD"
}
コミットモードは、経験ウィンドウを調整する long_window、recent_window、full_history、include_merges、follow_renames、as_of も受け付けます。応答は source が "commit" の完全なレポートで、その risk_score は 5 つの特徴グループ(サイズ、拡散、履歴、経験、目的)すべてを織り込みます:
{
"id": "unique-id",
"jit_schema_version": 3,
"jit_score_version": 1,
"source": "commit",
"long_window_days": 365,
"recent_window_days": 90,
"risk_score": 0.87,
"commit": { "id": "…", "parent_count": 1, "is_merge": false, "purpose": {} },
"features": { "size": {}, "diffusion": {}, "history": {}, "experience": {} },
"contributions": { "size": 0.4, "diffusion": 0.2, "history": 0.1, "purpose": 0.0, "experience": -0.1 }
}
diff モードはリポジトリなしで任意の unified diff をスコアリングします:
{
"id": "unique-id",
"diff": "--- a/x\n+++ b/x\n@@ -1 +1 @@\n-old\n+new\n"
}
A bare diff carries no author, parent, or history, so only the size and diffusion groups are computable. The diff report’s source is "diff" and it reports partial_risk_score, not risk_score, because the missing groups are absent from the body entirely, never present as zero:
{
"id": "unique-id",
"jit_schema_version": 3,
"jit_score_version": 1,
"source": "diff",
"partial_risk_score": 0.6,
"size": {},
"diffusion": {},
"contributions": { "size": 0.4, "diffusion": 0.2 }
}
Branch on the source discriminator ("commit" vs "diff") to read the right score field. partial_risk_score sums only the size and diffusion contributions, while a commit’s risk_score also folds in history, purpose, and the negatively signed experience group, so the two are neither ordered nor on the same scale: rank diffs against other diffs, never against commit scores.
Mode conflict. Supplying diff together with any commit-mode field (repo_path, commit, a window, history, rename, or as_of knob) is rejected with a 400 rather than silently honouring the diff and dropping the rest. The two modes answer different, non-comparable questions, so the combination is treated as a client mistake (issue number 632).
Non-diff diff. A diff value that is not a git unified diff (the wrong field, an accidentally-mangled string, arbitrary text) is rejected with a 400 (vcs_invalid_diff) rather than scored as a confident partial_risk_score of 0.0. On a risk-gating endpoint a spurious “zero risk” is the most dangerous failure mode, so non-diff input is a hard error (issue 652). An empty or whitespace-only diff is the one exception: it legitimately means “no changes”, so it still returns a valid 0.0, and a CI step that computed an empty diff gets the zero-risk answer it expects.