Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 (default 127.0.0.1).
  • --port: the TCP port (default 8080).
  • -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 strict deny_unknown_fields parse, including the removed unit flag; see Compute metrics below), or a scope value that is not full / file.
  • 422 Unprocessable Entity: the file_name extension (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"; query GET /v1/languages for the supported set. (Before 2.0 this was a 404, indistinguishable from an unknown URL; see issue #634.)
  • 404 Not Found: the URL matches no endpoint.
  • 415 Unsupported Media Type: a known POST endpoint received a Content-Type that is neither application/json nor application/octet-stream (a charset parameter is allowed).
  • 405 Method Not Allowed: a known endpoint was called with the wrong HTTP method. The analysis endpoints accept POST; /v1/ping, /v1/version, /v1/languages, and /v1 accept GET and HEAD.
  • 406 Not Acceptable: the request Accept header named only media types the server cannot produce. The structured analysis endpoints serve application/json, application/yaml, and application/cbor; any other concrete type (application/xml, say) is a 406 carrying the not_acceptable token. 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 /vcs history 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 レベルとみなされるもの
/metrics128ネストした関数スペース 1 つ(関数の中の関数の中の…)
/ast512AST ノード 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/jsonapplication/json
application/yaml(または text/yaml、application/x-yaml)application/yaml
application/cborapplication/cbor
その他の任意の具体的なタイプ406 Not Acceptable

ルール:

  • JSON is the default. Without an Accept header, the server returns JSON: the same body and Content-Type earlier releases always returned, so existing clients need no change. JSON entries and wildcards (*/*, application/*) make JSON a candidate.
  • q weights are honored. The supported candidate with the highest q weight wins (Accept: application/json;q=0.5, application/yaml;q=0.9 returns YAML), with ties resolved by list order; q=0 refuses 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/comment octet-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: when true, comment nodes are omitted from the tree. Optional; defaults to false.
  • span: when true, each node carries its source span; when false, span is null. Optional; defaults to false.

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 submitted code), or null when span was false. These span keys use the *_line vocabulary shared with /function and /metrics (issue #638 renamed the former *_row keys).
  • field_name: the tree-sitter grammar field through which the parent reaches this node (name, left, body, and so on), or null for 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 recursive spaces list for every function, class, and other unit. file returns only the file-level root with its spaces children cleared. This field replaces the pre-2.0 boolean unit flag (issue #638); sending the old unit key now fails with 400.

ペイロードは厳密に検証されます。認識されないキー(タイプミスや、削除済みの 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_path is a trust boundary. The repo_path field 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 optional cache_dir field 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 controlling cache_dir can direct the server to write cache files at any path the server process can write to. (cache_dir is accepted only by /vcs; /vcs/trend and /vcs/jit do 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/jit to untrusted clients without an authorization layer in front of bca-web. The default 127.0.0.1 bind 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 as 12mo or 90d. Defaults 12mo / 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.