Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ Adding `--json-envelope` to a query command (`search`, `definition`, `references

Every top-level CLI/MCP JSON DTO (`StatusResult`, `RepoMapResult`, `SymbolAnalysisResult`, `ImpactAnalysisResult`, `OutlineResult`, `FileExcerptResult`, `CompactSearchResult`, `SymbolResult`, `DefinitionResult`, `UnusedSymbolResult`, `ReferenceResult`, `CallerResult`, `CalleeResult`, `FileResult`, `FileFindResult`) carries an `api_version` string field stamped from `JsonOutputContract.ApiVersion`. The same value is mirrored on the `--json-envelope` `metadata` block. This describes the JSON output contract, not the cdidx binary version (which is still surfaced via `version.json` and `cdidx --version`). Bump `JsonOutputContract.ApiVersion` only on **breaking** shape changes — renames, removals, or type changes of an existing field. Additive changes (new optional fields, new readiness flags, new enum values) keep the version stable so older consumers continue to parse the payload. Strict downstream consumers should pin against the major value and degrade gracefully when it changes. Issue #1555.

The documented `status --json` trust contract spans `fold_ready`, `fold_ready_reason`, `graph_table_available`, `issues_table_available`, `sql_graph_contract_ready`, `sql_graph_contract_degraded_reason`, `hotspot_family_ready`, `hotspot_family_degraded_reason`, `csharp_symbol_name_ready`, `csharp_metadata_target_ready`, `indexed_head_commit`, `worktree_head_changed`, `indexed_head_sha`, `indexed_head_branch`, `indexed_head_timestamp`, `commits_ahead_of_indexed_head`, `index_writer_version`, `index_newer_than_reader`, `index_newer_than_reader_reason`, `unknown_extension_file_count`, `path_case_sensitive`, `stale_after_seconds`, `index_age_seconds`, plus the fold-only remediation fields `degraded_reason`, `recommended_action`, and `alternative_action`. Keep this list synchronized with `README.md` and `AGENT_GUIDE.md`; `DocumentationStatusContractTests` fails when any required field is missing from one of those docs.

`references` already prefixes each human-readable row with `reference_kind`, and `callers` does the same for its grouped caller rows. When one grouped container mixes kinds (for example `call` and `subscribe` on the same event member), the human-readable label joins the distinct kinds with `+` (for example `call+subscribe`) instead of collapsing to a single preferred label, and the reference-kind column widens dynamically to fit the longest label in the batch so mixed rows do not overrun the neighbouring column. JSON output for `callers` and `callees` keeps the scalar `reference_kind` for back-compat (it reports the preferred summary kind `instantiate` > `subscribe` > `MIN(call)`) and adds a sorted `reference_kinds` array plus a `has_mixed_reference_kinds` bool so consumers can detect mixed containers without trusting a single collapsed label. This lets terminal users distinguish `call` / `instantiate` / `subscribe` / mixed without re-running the command with `--json` and lets AI clients answer mixed-kind questions without chasing a second `--exact` query.

MCP tool calls return structured JSON in `structuredContent` plus a short summary in `content`, so clients can consume typed data directly.
Expand Down Expand Up @@ -2023,6 +2025,7 @@ USER_GUIDEの[終了コード](USER_GUIDE.md#終了コード)セクションを
- **AI向けの軽量検索スニペット** — `search --json` と MCP の `search` は、チャンク全文ではなく snippet range、match line、highlight、context count、`truncated_line_count` サマリを持つ一致中心スニペットを返す。`--snippet-lines` でペイロード量と文脈量のバランスを取れ、`--max-line-width`(CLI)/ `maxLineWidth`(MCP)は `find` / `references` / `excerpt` / `inspect` と同じ共有 `LineWidthFormatter.ClampLine` 契約で各スニペット行を最初のマッチトークン周辺にクランプするため、minified / transpiled / 生成された 1 行ファイル内の 1 ヒットで数百 KB を返さなくなる。クランプされた行はスニペットに `...(+N)...` マーカーが入り、`highlights[].truncated` と `highlights[].original_line_length` で AI クライアントがクランプを検出できる。
- **初動向けの repo map** — `map` は、インデックス済みデータから言語、モジュール、主要ファイル、ホットスポット、推定エントリポイントを集約し、AIクライアントが精密検索前に見るべき場所を決めやすくする。シンボル抽出が `Main` 系シンボルを出さない場合でも、既知のトップレベル実行ファイルへフォールバックして入口候補を補う。
- **信用判断のための鮮度メタデータ** — `status` はワークスペース全体の鮮度と git 状態を返す。`map` は `indexed_at` / `latest_modified` を絞り込み結果の鮮度として維持しつつ、`workspace_indexed_at` / `workspace_latest_modified` でワークスペース全体の鮮度も返す。`inspect` も同じワークスペース鮮度と git フィールドを返すため、シンボル中心の AI フローで `status` を別途呼ばずに済む。さらに `status` は `sql_graph_contract_ready` / `sql_graph_contract_degraded_reason`、`hotspot_family_ready` / `hotspot_family_degraded_reason` に加えて、forward-compatibility 監査 (`index_writer_version`、`index_newer_than_reader`、`index_newer_than_reader_reason`、詳細は「リーダー側の forward-compatibility 監査」を参照)、および fold-only remediation 用の `fold_ready_reason`、`degraded_reason`、`recommended_action`、`alternative_action` も返すため、AI クライアントは SQL graph/dependency/impact、duplicate-name hotspot family、Unicode `--exact` のどれが authoritative か、また DB が現在の binary より新しい `cdidx` で書かれていないかを最初に判断できる。現行の全体 scan 後は `unknown_extension_file_count` も返すため、未知拡張子で index 対象外になった件数を `status` から確認できる。これらの fold-only remediation field は、明示的な read-only `file:///...?...` DB URI から導出された場合でも、失敗する read-only URI をそのままコマンドへ埋め込まず、writable な filesystem path に正規化して返す。さらに `impact` / MCP `impact_analysis` に加えて、`inspect` / MCP `analyze_symbol`、`references` / `callers` / `callees`、`deps` / `unused` / `hotspots` 系も、SQL ベースの graph/dependency read が実際に結果へ関与したときだけ `sql_graph_contract_ready` / `sql_graph_contract_degraded_reason` を反映するため、stale な SQL 行が authoritative なヒットや 0 件応答に見えてしまうのを防ぎつつ、mixed-language index 内の純粋な非SQL結果を誤って degraded 扱いしない。`files` はファイルごとの checksum・modified・indexed timestamp を返す。古いDBに対する file 列の移行は可能なら自動で行い、その場移行できない場合でも読み取り経路がクラッシュしないようにする。CLI と MCP の 0 件 JSON レスポンスは `indexed_file_count`、`indexed_at`、`freshness_available` を含む。`freshness_available=true` で `indexed_at:null` なら空インデックス、`freshness_available=false` なら legacy/read-only DB で鮮度 timestamp を取得できず、理由は `freshness_degraded_reason` に入る。**HEAD 起点の stale 検知**: `cdidx index` の full scan が成功するたびに、現時点の `git HEAD` を `codeindex_meta` に stamp し、後続実行で workspace HEAD と比較できるようにする。`--rebuild` 指定なしに両者が異なる場合、CLI は `cdidx index <projectPath> --rebuild` を勧める `head_changed` 警告を表示し、`index --json` に `head_changed` / `prior_indexed_head_commit` / `current_head_commit` / `head_change_notice` を出力する。`status --check` も同じ比較を `workspace_check.head_changed` として公開し、差分時には `indexed_head_commit` / `workspace_head_commit` も併記するため、鮮度 gate ですでに `status --check` を通している AI クライアントは `git switch <branch>` 後の既定の incremental scan を別クエリなしで拒否できる。`--commits` / `--files` の部分更新は意図的に記録 HEAD を維持し、次の full scan が worktree を再インデックスするまで stale 通知が継続する。非 Git workspace と HEAD を記録していない legacy DB は比較自体をスキップし、false-positive な警告を出さない。
文書化された `status --json` trust contract は `fold_ready`、`fold_ready_reason`、`graph_table_available`、`issues_table_available`、`sql_graph_contract_ready`、`sql_graph_contract_degraded_reason`、`hotspot_family_ready`、`hotspot_family_degraded_reason`、`csharp_symbol_name_ready`、`csharp_metadata_target_ready`、`indexed_head_commit`、`worktree_head_changed`、`indexed_head_sha`、`indexed_head_branch`、`indexed_head_timestamp`、`commits_ahead_of_indexed_head`、`index_writer_version`、`index_newer_than_reader`、`index_newer_than_reader_reason`、`unknown_extension_file_count`、`path_case_sensitive`、`stale_after_seconds`、`index_age_seconds`、および fold-only remediation field の `degraded_reason`、`recommended_action`、`alternative_action` を対象にします。この一覧は `README.md` と `AGENT_GUIDE.md` に同期してください。いずれかの必須 field がこれらの docs から漏れると `DocumentationStatusContractTests` が失敗します。
- **再解析不要の folded-key アップグレード** — `backfill-fold` と MCP `backfill_fold` は、既存 DB 行から `name_folded` / `*_folded` を直接再計算し、必要な folded 値に NULL が残っていないことを検証してから `FoldReadyFlag` を stamp する。これにより、pre-#86 DB から AI クライアントやユーザーが低コストで Unicode `--exact` へ上がれる。さらに `fold_key_version` が未記録または不一致なら全 folded 行を再生成するため、将来の `NameFold.Version` 変更後に古い key を silent に再 stamp してしまうことも防ぐ。
- **まとめて取るシンボル分析** — `inspect` と MCP の `analyze_symbol` は、定義、近傍シンボル、参照、caller、callee、ファイルメタデータ、ワークスペース信頼メタデータ、graph 対応メタデータを1回で返し、AIクライアントが一般的なシンボル調査を少ない往復で終えやすくする。
- **言語考慮の参照抽出** — `references`、`callers`、`callees` は、正規表現ベースの call/reference 抽出が意味を持つ言語だけに対してインデックス化された参照テーブルで支える。未対応言語では、低信頼な疑似グラフ結果を返す代わりにテキスト検索へ戻る前提で設計する。**nested generic 呼び出し**: `new Dictionary<string, List<int>>()` のような C#/Java のコンストラクタ呼び出しと、`Helper.DoWork<List<int>>()` のような C# generic method call は、平坦な regex fast-path で `>>` を釣り合わせられなくても depth-aware fallback scanner で拾い直し、外側 target を参照テーブルへ残す。**コンストラクタ連鎖呼び出し**: C# の `: this(...)` / `: base(...)` イニシャライザと、Java のコンストラクタ本体冒頭文 `this(...)` / `super(...)` は、汎用 call regex とは別に検出し、呼び先が実際のコンストラクタとなるように書き換える(`this` は外側の class/record、`base` / `super` は外側クラスのシグネチャから解析した基底型)。C# のクロス行イニシャライザは外側クラスではなく、そのコンストラクタに紐付ける。基底型の解析は generic 引数、record のプライマリコンストラクタ引数、`where` 制約、`global::` やドット付きの namespace 修飾を剥がす。Java の `super.method()` は通常のメソッド呼び出しのまま扱う。**型位置の依存エッジ**: C#/Java の継承リスト、宣言型、generic 制約、`throws`、`is` / `as` / `instanceof`、および実際の C# XML doc `///` `cref` は `type_reference` 行として索引し、既定の `callers` / `callees` が見せる動的 call graph を汚さずに、`references` / `impact` から compile-time rename 依存を辿れるようにする。**SQL qualified-name alignment**: SQL の graph/dependency reader は、各 reference 行の source-line context、記録済み call 列位置、enclosing container から SQL 参照名を復元して定義と照合するため、qualified な `references` / `callers` / `impact` query は exact / non-exact を問わず sibling schema へ widen しない。source 側が genuinely unqualified な場合にだけ bare leaf fallback を許可するので、qualified call を含む `deps` / `unused` / `hotspots` も schema 単位で整合し、`EXEC dbo.fn_Target; EXEC sales.fn_Target;` のような同一行 multi-call も二重計上しない。列位置が記録されている row は、その列に qualified token が見つからなければ whole-line の別 qualified token へ昇格させないため、行末コメント・文字列リテラル・後続の別 call が先頭の unqualified edge を横取りすることもない。qualified な `callees` query でも caller query 自体が unqualified なとき以外は leaf fallback を無効化したため、`callees sales.Caller` が `dbo.Caller` へ広がらない。SQL extractor は qualified-name の `.` 前後空白も許容し、definition 系 reader は quoted qualified SQL name (`[dbo].[fn_X]` → `dbo.fn_X`) を正規化してから照合する。さらに exact SQL 定義照合は segment 数を保持し、SQL の exact graph leaf fallback は Unicode folded exact path を維持するため、quoted single identifier の衝突や Unicode exact lookup の ASCII-only `NOCASE` 退行も防ぐ。
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ scripts, CI, or AI tools. Use `rg` when you only need a one-off text scan.
- Human `status` output translates readiness flags and `status --explain <field>`
describes one readiness field/remediation; `status --json` keeps raw fields for
automation, including the last full-scan unknown-extension count.
- The documented `status --json` trust contract covers `fold_ready`,
`fold_ready_reason`, `graph_table_available`, `issues_table_available`,
`sql_graph_contract_ready`, `sql_graph_contract_degraded_reason`,
`hotspot_family_ready`, `hotspot_family_degraded_reason`,
`csharp_symbol_name_ready`, `csharp_metadata_target_ready`,
`indexed_head_commit`, `worktree_head_changed`, `indexed_head_sha`,
`indexed_head_branch`, `indexed_head_timestamp`, `commits_ahead_of_indexed_head`,
`index_writer_version`, `index_newer_than_reader`,
`index_newer_than_reader_reason`, `unknown_extension_file_count`,
`path_case_sensitive`, `stale_after_seconds`, `index_age_seconds`,
`degraded_reason`, `recommended_action`, and `alternative_action`; keep this
list synchronized with `DEVELOPER_GUIDE.md` and `AGENT_GUIDE.md`.
- Local-first storage in `.cdidx/codeindex.db`.
- 78 detected languages, with symbol and graph support where available.

Expand Down Expand Up @@ -202,6 +214,18 @@ cdidx mcp
- 人間向け `status` は readiness flag を翻訳し、`status --explain <field>` は
個別 field の意味と対処を説明します。自動化向けの `status --json` は raw field
と直近 full scan の未知拡張子数を維持します。
- 文書化された `status --json` trust contract は `fold_ready`、
`fold_ready_reason`、`graph_table_available`、`issues_table_available`、
`sql_graph_contract_ready`、`sql_graph_contract_degraded_reason`、
`hotspot_family_ready`、`hotspot_family_degraded_reason`、
`csharp_symbol_name_ready`、`csharp_metadata_target_ready`、
`indexed_head_commit`、`worktree_head_changed`、`indexed_head_sha`、
`indexed_head_branch`、`indexed_head_timestamp`、`commits_ahead_of_indexed_head`、
`index_writer_version`、`index_newer_than_reader`、
`index_newer_than_reader_reason`、`unknown_extension_file_count`、
`path_case_sensitive`、`stale_after_seconds`、`index_age_seconds`、
`degraded_reason`、`recommended_action`、`alternative_action` を対象にします。
この一覧は `DEVELOPER_GUIDE.md` と `AGENT_GUIDE.md` に同期してください。
- `.cdidx/codeindex.db` に保存するローカルファースト設計。
- 78 言語を検出し、対応言語ではシンボルとグラフも利用可能。

Expand Down
17 changes: 17 additions & 0 deletions changelog.d/unreleased/1452.docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: docs
issues:
- 1452
affected:
- README.md
- DEVELOPER_GUIDE.md
- tests/CodeIndex.Tests/DocumentationStatusContractTests.cs
---

## English

- **Status contract docs now stay synchronized (#1452)** — `README.md`, `DEVELOPER_GUIDE.md`, and `AGENT_GUIDE.md` now document the same `status --json` trust fields, with a regression test that fails when one document drops a required field.

## 日本語

- **status contract docs の同期を固定しました (#1452)** — `README.md`、`DEVELOPER_GUIDE.md`、`AGENT_GUIDE.md` が同じ `status --json` trust fields を文書化し、必須 field がいずれかの文書から漏れると regression test が失敗するようになりました。
66 changes: 66 additions & 0 deletions tests/CodeIndex.Tests/DocumentationStatusContractTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace CodeIndex.Tests;

public class DocumentationStatusContractTests
{
private static readonly string[] StatusContractFields =
[
"fold_ready",
"fold_ready_reason",
"graph_table_available",
"issues_table_available",
"sql_graph_contract_ready",
"sql_graph_contract_degraded_reason",
"hotspot_family_ready",
"hotspot_family_degraded_reason",
"csharp_symbol_name_ready",
"csharp_metadata_target_ready",
"indexed_head_commit",
"worktree_head_changed",
"indexed_head_sha",
"indexed_head_branch",
"indexed_head_timestamp",
"commits_ahead_of_indexed_head",
"index_writer_version",
"index_newer_than_reader",
"index_newer_than_reader_reason",
"unknown_extension_file_count",
"path_case_sensitive",
"stale_after_seconds",
"index_age_seconds",
"degraded_reason",
"recommended_action",
"alternative_action",
];

[Theory]
[InlineData("README.md")]
[InlineData("DEVELOPER_GUIDE.md")]
[InlineData("AGENT_GUIDE.md")]
public void StatusContractDocs_MentionEveryTrustField(string relativePath)
{
var repoRoot = GetRepositoryRoot();
var docPath = Path.Combine(repoRoot, relativePath);
var content = File.ReadAllText(docPath);

foreach (var field in StatusContractFields)
{
Assert.Contains(field, content, StringComparison.Ordinal);
}
}

private static string GetRepositoryRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AGENT_GUIDE.md")))
{
return directory.FullName;
}

directory = directory.Parent;
}

throw new InvalidOperationException("Could not locate repository root from test output directory.");
}
}
Loading