From 801da51124ef78a0100b59e934e3cc0fee012690 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sun, 31 May 2026 18:16:48 +0900 Subject: [PATCH 1/4] Fix readiness trust checks (#1485 #1607 #1647) --- AGENT_GUIDE.md | 2 +- DEVELOPER_GUIDE.md | 1 + README.md | 32 ++++----- changelog.d/unreleased/1485.fixed.md | 17 +++++ changelog.d/unreleased/1607.fixed.md | 15 ++++ changelog.d/unreleased/1647.fixed.md | 21 ++++++ .../Database/DbReader.FilesStatus.cs | 2 + src/CodeIndex/Database/DbReader.cs | 72 ++++++++++++++++++- .../Database/DegradationReasonCodes.cs | 7 ++ src/CodeIndex/Models/QueryResults.cs | 3 + tests/CodeIndex.Tests/DbReaderTests.cs | 59 +++++++++++++++ 11 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 changelog.d/unreleased/1485.fixed.md create mode 100644 changelog.d/unreleased/1607.fixed.md create mode 100644 changelog.d/unreleased/1647.fixed.md diff --git a/AGENT_GUIDE.md b/AGENT_GUIDE.md index d8375944ff..e50796feed 100644 --- a/AGENT_GUIDE.md +++ b/AGENT_GUIDE.md @@ -131,7 +131,7 @@ CI watching must be bounded. Do not loop indefinitely. ## Status Contract -- `status --json` and related JSON/MCP payloads currently expose the trust fields documented in `README.md` and `DEVELOPER_GUIDE.md`, including `fold_ready`, `fold_ready_reason`, `graph_table_available`, `issues_table_available`, `file_issues_data_current`, `migration_in_progress`, `sql_graph_contract_ready`, `sql_graph_contract_degraded_reason`, `hotspot_family_ready`, `hotspot_family_degraded_reason`, `csharp_symbol_name_ready`, `csharp_metadata_target_ready`, `csharp_metadata_target_degraded_reason`, `indexed_head_commit`, `worktree_head_changed`, `index_writer_version`, `index_newer_than_reader`, `index_newer_than_reader_reason`, `unknown_extension_file_count`, `path_case_sensitive`, `data_dir`, `data_dir_source`, `data_dir_mode`, `mac_profile`, `db_size_bytes`, `wal_size_bytes`, `db_pragma_settings` (`journal_mode`, `synchronous`, `wal_autocheckpoint`, `page_count`, `freelist_count`, `page_size`), `symbols_by_language`, `process`, `last_index_run`, `hooks`, MCP-only `mcp_session`, and the `status --check`-only `stale_after_seconds` / `index_age_seconds` threshold audit fields. +- `status --json` and related JSON/MCP payloads currently expose the trust fields documented in `README.md` and `DEVELOPER_GUIDE.md`, including `fold_ready`, `fold_ready_reason`, `graph_table_available`, `issues_table_available`, `file_issues_data_current`, `migration_in_progress`, `sql_graph_contract_ready`, `sql_graph_contract_degraded_reason`, `hotspot_family_ready`, `hotspot_family_degraded_reason`, `language_readiness`, `csharp_symbol_name_ready`, `csharp_metadata_target_ready`, `csharp_metadata_target_degraded_reason`, `indexed_head_commit`, `worktree_head_changed`, `index_writer_version`, `index_newer_than_reader`, `index_newer_than_reader_reason`, `unknown_extension_file_count`, `path_case_sensitive`, `data_dir`, `data_dir_source`, `data_dir_mode`, `mac_profile`, `db_size_bytes`, `wal_size_bytes`, `db_pragma_settings` (`journal_mode`, `synchronous`, `wal_autocheckpoint`, `page_count`, `freelist_count`, `page_size`), `symbols_by_language`, `process`, `last_index_run`, `hooks`, MCP-only `mcp_session`, and the `status --check`-only `stale_after_seconds` / `index_age_seconds` threshold audit fields. - When any readiness field is degraded, the CLI adds `degraded_root_cause`, `degraded_reason`, `recommended_action`, `alternative_action`, and `readiness_degradations[]`. `degraded_root_cause` is the primary stable machine code; `readiness_degradations[]` lists every degraded field with `root_cause`, human reason, and remediation strings. - `issues_table_available` reports physical `file_issues` table presence only. `file_issues_data_current` reports whether the table is also stamped current for the active index generation. - `index_writer_version` records the `cdidx` version that last wrote to the DB (stamped into `codeindex_meta` as `cdidx_writer_version` on every full scan, update, and MCP index). `index_newer_than_reader` flips to `true` whenever any persisted numeric contract stamp in `codeindex_meta` (or unknown `PRAGMA user_version` readiness bits) exceeds the current binary's compiled maximum, so an older CLI re-opening a DB written by a newer CLI degrades loudly with an audit trail instead of silently dropping back to text-search fallbacks. `index_newer_than_reader_reason` enumerates the specific newer-than-reader stamps. diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 3c3d3a7117..d94204b08e 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -311,6 +311,7 @@ Current stable codes and triggers: | `fold_ready=false` | aggregate fold readiness bit is degraded | `cdidx backfill-fold` or full rebuild | | `sql_graph_contract_ready=false` | SQL graph rows do not match the current call-column / qualified-name contract | `cdidx index ` | | `hotspot_family_ready=false` | one or more hotspot-family languages lack current authoritative family stamps | `cdidx index ` | +| `partial_family_key_population` | hotspot-family metadata is stamped but some indexed symbols still have NULL `family_key` values | `cdidx index ` | | `graph_table_available=false` | `symbol_references` is missing or not graph-ready | `cdidx index ` | | `issues_table_available=false` | `file_issues` is missing or not issue-ready | `cdidx index ` | | `csharp_symbol_name_ready=false` | C# canonical symbol-name stamps are stale | `cdidx index ` | diff --git a/README.md b/README.md index ba52e204ff..2aa9e31892 100644 --- a/README.md +++ b/README.md @@ -168,14 +168,14 @@ The documented `status --json` trust contract covers these fields: fold_readyfold_ready_reasongraph_table_availableissues_table_available file_issues_data_currentmigration_in_progressdegraded_root_causereadiness_degradations sql_graph_contract_readysql_graph_contract_degraded_reasonhotspot_family_readyhotspot_family_degraded_reason -csharp_symbol_name_readycsharp_metadata_target_readycsharp_metadata_target_degraded_reasonindexed_head_commit -worktree_head_changedindexed_head_shaindexed_head_branchindexed_head_timestamp -commits_ahead_of_indexed_headindex_writer_versionindex_newer_than_readerindex_newer_than_reader_reason -unknown_extension_file_countpath_case_sensitivedata_dirdata_dir_source -data_dir_modemac_profiledb_size_byteswal_size_bytes -db_pragma_settingssymbols_by_languageprocesslast_index_run -hooksstale_after_secondsindex_age_secondsdegraded_reason -recommended_actionalternative_actionmcp_session +language_readinesscsharp_symbol_name_readycsharp_metadata_target_readycsharp_metadata_target_degraded_reason +indexed_head_commitworktree_head_changedindexed_head_shaindexed_head_branch +indexed_head_timestampcommits_ahead_of_indexed_headindex_writer_versionindex_newer_than_reader +index_newer_than_reader_reasonunknown_extension_file_countpath_case_sensitivedata_dir +data_dir_sourcedata_dir_modemac_profiledb_size_bytes +wal_size_bytesdb_pragma_settingssymbols_by_languageprocess +last_index_runhooksstale_after_secondsindex_age_seconds +degraded_reasonrecommended_actionalternative_actionmcp_session @@ -391,14 +391,14 @@ upgrade / downgrade 後はインストール済み補完 script を再生成し fold_readyfold_ready_reasongraph_table_availableissues_table_available file_issues_data_currentmigration_in_progressdegraded_root_causereadiness_degradations sql_graph_contract_readysql_graph_contract_degraded_reasonhotspot_family_readyhotspot_family_degraded_reason -csharp_symbol_name_readycsharp_metadata_target_readycsharp_metadata_target_degraded_reasonindexed_head_commit -worktree_head_changedindexed_head_shaindexed_head_branchindexed_head_timestamp -commits_ahead_of_indexed_headindex_writer_versionindex_newer_than_readerindex_newer_than_reader_reason -unknown_extension_file_countpath_case_sensitivedata_dirdata_dir_source -data_dir_modemac_profiledb_size_byteswal_size_bytes -db_pragma_settingssymbols_by_languageprocesslast_index_run -hooksstale_after_secondsindex_age_secondsdegraded_reason -recommended_actionalternative_actionmcp_session +language_readinesscsharp_symbol_name_readycsharp_metadata_target_readycsharp_metadata_target_degraded_reason +indexed_head_commitworktree_head_changedindexed_head_shaindexed_head_branch +indexed_head_timestampcommits_ahead_of_indexed_headindex_writer_versionindex_newer_than_reader +index_newer_than_reader_reasonunknown_extension_file_countpath_case_sensitivedata_dir +data_dir_sourcedata_dir_modemac_profiledb_size_bytes +wal_size_bytesdb_pragma_settingssymbols_by_languageprocess +last_index_runhooksstale_after_secondsindex_age_seconds +degraded_reasonrecommended_actionalternative_actionmcp_session diff --git a/changelog.d/unreleased/1485.fixed.md b/changelog.d/unreleased/1485.fixed.md new file mode 100644 index 0000000000..0210413a38 --- /dev/null +++ b/changelog.d/unreleased/1485.fixed.md @@ -0,0 +1,17 @@ +--- +category: fixed +issues: + - 1485 +affected: + - src/CodeIndex/Database/DbReader.cs + - src/CodeIndex/Database/DegradationReasonCodes.cs + - tests/CodeIndex.Tests/DbReaderTests.cs +--- + +## English + +- **Hotspot family readiness now verifies row-level family keys (#1485)** - `status` and hotspot readers now degrade when current hotspot-family metadata is stamped but indexed symbols still have NULL `family_key` values. + +## 日本語 + +- **hotspot family readiness が行単位の family key を検証するようになりました (#1485)** - current な hotspot-family metadata が stamp 済みでも、indexed symbol に NULL の `family_key` が残っている場合は `status` と hotspot reader が degraded として扱います。 diff --git a/changelog.d/unreleased/1607.fixed.md b/changelog.d/unreleased/1607.fixed.md new file mode 100644 index 0000000000..c93b3ac3e3 --- /dev/null +++ b/changelog.d/unreleased/1607.fixed.md @@ -0,0 +1,15 @@ +--- +category: fixed +issues: + - 1607 +affected: + - src/CodeIndex/Database/DbReader.cs +--- + +## English + +- **Read-only fallback now verifies fold-ready rows before trusting stale ready bits (#1607)** - readers opened through the read-only fallback path now demote `fold_ready` when the ready bit is set but folded-name rows are incomplete. + +## 日本語 + +- **read-only fallback が stale ready bit を信頼する前に fold-ready 行を検証するようになりました (#1607)** - read-only fallback 経路で開いた reader は、ready bit が立っていても folded-name 行が未完成なら `fold_ready` を degraded に降格します。 diff --git a/changelog.d/unreleased/1647.fixed.md b/changelog.d/unreleased/1647.fixed.md new file mode 100644 index 0000000000..a6e97fa572 --- /dev/null +++ b/changelog.d/unreleased/1647.fixed.md @@ -0,0 +1,21 @@ +--- +category: fixed +issues: + - 1647 +affected: + - src/CodeIndex/Database/DbReader.cs + - src/CodeIndex/Database/DbReader.FilesStatus.cs + - src/CodeIndex/Models/QueryResults.cs + - README.md + - DEVELOPER_GUIDE.md + - AGENT_GUIDE.md + - tests/CodeIndex.Tests/DbReaderTests.cs +--- + +## English + +- **Status now exposes per-language readiness details (#1647)** - `status --json` includes `language_readiness` so clients can distinguish language-scoped readiness from aggregate readiness fields. + +## 日本語 + +- **status が言語別 readiness の詳細を返すようになりました (#1647)** - `status --json` に `language_readiness` を追加し、クライアントが aggregate readiness と言語単位の readiness を区別できるようにしました。 diff --git a/src/CodeIndex/Database/DbReader.FilesStatus.cs b/src/CodeIndex/Database/DbReader.FilesStatus.cs index d60c6ca48b..3c09fee3e1 100644 --- a/src/CodeIndex/Database/DbReader.FilesStatus.cs +++ b/src/CodeIndex/Database/DbReader.FilesStatus.cs @@ -418,6 +418,7 @@ public StatusResult GetStatus() : _csharpMetadataTargetDegradedReason; var sqlGraphContractSignal = GetSqlGraphContractSignal(lang: null); var hotspotFamilySignal = GetHotspotFamilySignal(lang: null); + var languageReadiness = GetLanguageReadiness(); var foldReadyReason = ResolveFoldReadyReason(); var foldReady = _foldReady && foldReadyReason == null; @@ -502,6 +503,7 @@ GROUP BY COALESCE(f.lang, 'unknown'), s.kind MigrationInProgress = batchInProgress, HotspotFamilyReady = hotspotFamilySignal.Ready, HotspotFamilyDegradedReason = hotspotFamilySignal.DegradedReason, + LanguageReadiness = languageReadiness.Count > 0 ? languageReadiness : null, CSharpSymbolNameReady = csharpSymbolNameReady, CSharpMetadataTargetReady = csharpMetadataTargetReady, CSharpMetadataTargetDegradedReason = csharpMetadataTargetDegradedReason, diff --git a/src/CodeIndex/Database/DbReader.cs b/src/CodeIndex/Database/DbReader.cs index 16c965eef4..6b08c3040c 100644 --- a/src/CodeIndex/Database/DbReader.cs +++ b/src/CodeIndex/Database/DbReader.cs @@ -17,6 +17,12 @@ public readonly record struct HotspotFamilySignal( bool Relevant, string? DegradedReason); +public sealed record LanguageReadinessSignal( + [property: System.Text.Json.Serialization.JsonPropertyName("ready")] bool Ready, + [property: System.Text.Json.Serialization.JsonPropertyName("degraded_reason")] + [property: System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + string? DegradedReason); + public readonly record struct SqlGraphContractSignal( bool Ready, bool Relevant, @@ -728,7 +734,66 @@ private string ResolveHotspotFamilyDegradedReason(string lang) return string.IsNullOrWhiteSpace(fingerprint) ? DegradationReasonCodes.HotspotFamilyDisabledAtIndexTime - : DegradationReasonCodes.HotspotFamilyMetadataStale; + : HasIncompleteHotspotFamilyRows(lang) + ? DegradationReasonCodes.HotspotFamilyRowsIncomplete + : DegradationReasonCodes.HotspotFamilyMetadataStale; + } + + private bool HasIncompleteHotspotFamilyRows(string lang) + { + if (!_symbolColumns.Contains("family_key")) + return true; + + using var cmd = _conn.CreateCommand(); + cmd.CommandText = @" + SELECT EXISTS( + SELECT 1 + FROM symbols s + JOIN files f ON f.id = s.file_id + WHERE f.lang = @lang + AND s.name IS NOT NULL + AND s.family_key IS NULL + LIMIT 1)"; + cmd.Parameters.AddWithValue("@lang", lang); + var raw = cmd.ExecuteScalar(); + return raw is long l ? l != 0 : raw is int i && i != 0; + } + + public Dictionary> GetLanguageReadiness() + { + var result = new Dictionary>(StringComparer.Ordinal); + + foreach (var lang in _indexedHotspotFamilyLanguages.OrderBy(value => value, StringComparer.Ordinal)) + { + var signal = GetHotspotFamilySignal(lang); + AddLanguageReadiness(result, lang, "hotspot_family", signal.Ready, signal.DegradedReason); + } + + if (ScopeMayIncludeCSharpFiles("csharp", pathPatterns: null, excludePathPatterns: null, excludeTests: false, since: null)) + { + AddLanguageReadiness(result, "csharp", "symbol_name", _csharpSymbolNameContractCurrent, + _csharpSymbolNameContractCurrent ? null : DegradationReasonCodes.CSharpSymbolNameNotReady); + AddLanguageReadiness(result, "csharp", "metadata_target", _csharpMetadataTargetReady, + _csharpMetadataTargetReady ? null : _csharpMetadataTargetDegradedReason); + } + + return result; + } + + private static void AddLanguageReadiness( + Dictionary> result, + string lang, + string feature, + bool ready, + string? degradedReason) + { + if (!result.TryGetValue(lang, out var features)) + { + features = new Dictionary(StringComparer.Ordinal); + result[lang] = features; + } + + features[feature] = new LanguageReadinessSignal(ready, degradedReason); } private string GetHotspotFamilyRecoveryAction(IReadOnlyList languages) @@ -767,7 +832,7 @@ private static int ParseFoldVersion(SqliteConnection conn) { if (_foldReady) { - if (ShouldVerifyFoldReadyRows() && HasIncompleteFoldRows()) + if ((_readOnlyFallback || ShouldVerifyFoldReadyRows()) && HasIncompleteFoldRows()) return DegradationReasonCodes.FoldReadyBitSetButRowsIncomplete; return null; @@ -843,7 +908,8 @@ private HashSet LoadHotspotFamilyReadyLanguages(SqliteConnection conn) if (raw is string s && int.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var version) && version == DbContext.HotspotFamilyVersion - && !string.IsNullOrWhiteSpace(fingerprint)) + && !string.IsNullOrWhiteSpace(fingerprint) + && !HasIncompleteHotspotFamilyRows(lang)) { readyLangs.Add(lang); } diff --git a/src/CodeIndex/Database/DegradationReasonCodes.cs b/src/CodeIndex/Database/DegradationReasonCodes.cs index dc84f44f35..c511632d31 100644 --- a/src/CodeIndex/Database/DegradationReasonCodes.cs +++ b/src/CodeIndex/Database/DegradationReasonCodes.cs @@ -19,6 +19,7 @@ public static class DegradationReasonCodes public const string HotspotFamilySupportNotIndexed = "hotspot_family_support_not_indexed"; public const string HotspotFamilyMetadataStale = "hotspot_family_metadata_stale"; public const string HotspotFamilyDisabledAtIndexTime = "hotspot_family_disabled_at_index_time"; + public const string HotspotFamilyRowsIncomplete = "partial_family_key_population"; public const string GraphTableMissing = "graph_table_available=false"; public const string IssuesTableMissing = "issues_table_available=false"; public const string FileIssuesDataStale = "file_issues_data_current=false"; @@ -42,6 +43,7 @@ public static class DegradationReasonCodes HotspotFamilySupportNotIndexed, HotspotFamilyMetadataStale, HotspotFamilyDisabledAtIndexTime, + HotspotFamilyRowsIncomplete, GraphTableMissing, IssuesTableMissing, FileIssuesDataStale, @@ -146,6 +148,11 @@ private static DegradationReasonMetadata CreateMetadata(string code) "Cross-file hotspot grouping metadata was stamped without marker fingerprints, so authoritative family grouping cannot be trusted.", "Run `cdidx index ` to rebuild and stamp authoritative hotspot families.", "Run `cdidx index --rebuild` for a full rebuild."), + HotspotFamilyRowsIncomplete => new( + code, + "Cross-file hotspot grouping metadata is stamped, but some indexed symbols still lack family keys.", + "Run `cdidx index ` to restamp authoritative hotspot families.", + "Run `cdidx index --rebuild` for a full rebuild."), GraphTableMissing => new( code, "Reference / caller / callee / unused counts are degraded to 0 because the symbol_references table is missing.", diff --git a/src/CodeIndex/Models/QueryResults.cs b/src/CodeIndex/Models/QueryResults.cs index de7b56144a..170ff33f7a 100644 --- a/src/CodeIndex/Models/QueryResults.cs +++ b/src/CodeIndex/Models/QueryResults.cs @@ -626,6 +626,9 @@ public class StatusResult [JsonPropertyName("hotspot_family_degraded_reason")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? HotspotFamilyDegradedReason { get; set; } + [JsonPropertyName("language_readiness")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary>? LanguageReadiness { get; set; } /// /// True when C# canonical symbol-name upgrades (for operators, conversion operators, /// indexers) have been applied to all indexed C# rows in this DB. False means exact-name diff --git a/tests/CodeIndex.Tests/DbReaderTests.cs b/tests/CodeIndex.Tests/DbReaderTests.cs index 31e3a9d96f..c67bdd0dd0 100644 --- a/tests/CodeIndex.Tests/DbReaderTests.cs +++ b/tests/CodeIndex.Tests/DbReaderTests.cs @@ -4125,6 +4125,65 @@ WHERE file_id IN ( Assert.Contains("hotspot_family_support_not_indexed=csharp", signal.DegradedReason); } + [Fact] + public void GetHotspotFamilySignal_CurrentStampWithPartialFamilyRowsIsDegraded() + { + InsertIndexedFile("src/Api.Part1.cs", "csharp", + """ + public partial class Api + { + public void Run() { } + } + """); + InsertIndexedFile("src/Api.Part2.cs", "csharp", + """ + public partial class Api + { + public void Run(int value) { } + } + """); + + using (var cmd = _db.Connection.CreateCommand()) + { + cmd.CommandText = """ + UPDATE symbols + SET family_key = NULL + WHERE file_id IN ( + SELECT id FROM files WHERE path = 'src/Api.Part2.cs' + ) + """; + cmd.ExecuteNonQuery(); + } + + var reader = new DbReader(_db.Connection); + var signal = reader.GetHotspotFamilySignal("csharp"); + + Assert.True(signal.Relevant); + Assert.False(signal.Ready); + Assert.Contains("partial_family_key_population=csharp", signal.DegradedReason); + } + + [Fact] + public void GetStatus_ExposesPerLanguageReadinessMap() + { + InsertIndexedFile("src/Api.Part1.cs", "csharp", + """ + public partial class Api + { + public void Run() { } + } + """); + + var reader = new DbReader(_db.Connection); + var status = reader.GetStatus(); + + Assert.NotNull(status.LanguageReadiness); + Assert.True(status.LanguageReadiness!.ContainsKey("csharp")); + Assert.True(status.LanguageReadiness["csharp"]["hotspot_family"].Ready); + Assert.True(status.LanguageReadiness["csharp"].ContainsKey("symbol_name")); + Assert.True(status.LanguageReadiness["csharp"].ContainsKey("metadata_target")); + } + [Fact] public void GetHotspotFamilySignal_MissingMarkerFingerprintIsStillDegraded() { From 0f99619f1f8d3e2023fd62df09ada8122ac08716 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sun, 31 May 2026 18:41:10 +0900 Subject: [PATCH 2/4] Refine hotspot family row verification (#1485) --- src/CodeIndex/Database/DbReader.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/CodeIndex/Database/DbReader.cs b/src/CodeIndex/Database/DbReader.cs index 6b08c3040c..353a712536 100644 --- a/src/CodeIndex/Database/DbReader.cs +++ b/src/CodeIndex/Database/DbReader.cs @@ -753,6 +753,16 @@ FROM symbols s WHERE f.lang = @lang AND s.name IS NOT NULL AND s.family_key IS NULL + AND EXISTS ( + SELECT 1 + FROM symbols s2 + JOIN files f2 ON f2.id = s2.file_id + WHERE f2.lang = f.lang + AND s2.name = s.name + AND s2.kind = s.kind + AND COALESCE(s2.container_qualified_name, '') = COALESCE(s.container_qualified_name, '') + AND s2.family_key IS NOT NULL + ) LIMIT 1)"; cmd.Parameters.AddWithValue("@lang", lang); var raw = cmd.ExecuteScalar(); From b83d2961a18f80fa9dac0c193585e0c2260e30a8 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sun, 31 May 2026 19:00:05 +0900 Subject: [PATCH 3/4] Document language readiness status contract (#1647) --- DEVELOPER_GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 9b85c4572c..b902e70220 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -894,7 +894,7 @@ 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`, `file_issues_data_current`, `migration_in_progress`, `sql_graph_contract_ready`, `sql_graph_contract_degraded_reason`, `hotspot_family_ready`, `hotspot_family_degraded_reason`, `csharp_symbol_name_ready`, `csharp_metadata_target_ready`, `csharp_metadata_target_degraded_reason`, `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`, `data_dir_mode`, `mac_profile`, `stale_after_seconds`, `index_age_seconds`, the remediation fields `degraded_root_cause`, `degraded_reason`, `recommended_action`, `alternative_action`, `readiness_degradations`, and MCP-only `mcp_session`. MCP `mcp_session` is session-scoped diagnostics, not persisted DB state, and contains `log_level`, `roots`, optional `client_info`, and optional `client_capabilities`. Keep this list synchronized with `README.md` and `AGENT_GUIDE.md`; `DocumentationStatusContractTests` fails when any required field is missing from one of those docs. +The documented `status --json` trust contract spans `fold_ready`, `fold_ready_reason`, `graph_table_available`, `issues_table_available`, `file_issues_data_current`, `migration_in_progress`, `sql_graph_contract_ready`, `sql_graph_contract_degraded_reason`, `hotspot_family_ready`, `hotspot_family_degraded_reason`, `language_readiness`, `csharp_symbol_name_ready`, `csharp_metadata_target_ready`, `csharp_metadata_target_degraded_reason`, `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`, `data_dir_mode`, `mac_profile`, `stale_after_seconds`, `index_age_seconds`, the remediation fields `degraded_root_cause`, `degraded_reason`, `recommended_action`, `alternative_action`, `readiness_degradations`, and MCP-only `mcp_session`. MCP `mcp_session` is session-scoped diagnostics, not persisted DB state, and contains `log_level`, `roots`, optional `client_info`, and optional `client_capabilities`. 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. From 0b5ff4ec381efcbe1e3c95d788d185c461025195 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sun, 31 May 2026 22:49:15 +0900 Subject: [PATCH 4/4] Update status JSON snapshot for language readiness (#1647) --- tests/CodeIndex.Tests/golden/status.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/CodeIndex.Tests/golden/status.json b/tests/CodeIndex.Tests/golden/status.json index aa31679b19..d498c44408 100644 --- a/tests/CodeIndex.Tests/golden/status.json +++ b/tests/CodeIndex.Tests/golden/status.json @@ -83,6 +83,22 @@ "migration_in_progress": false, "hotspot_family_ready": false, "hotspot_family_degraded_reason": "cross-file hotspot family grouping is degraded (hotspot_family_support_not_indexed=csharp); Run \u0060cdidx index \u003CprojectPath\u003E\u0060 to rebuild and stamp authoritative hotspot families.", + "language_readiness": { + "csharp": { + "hotspot_family": { + "ready": false, + "degraded_reason": "cross-file hotspot family grouping is degraded (hotspot_family_support_not_indexed=csharp); Run \u0060cdidx index \u003CprojectPath\u003E\u0060 to rebuild and stamp authoritative hotspot families." + }, + "symbol_name": { + "ready": false, + "degraded_reason": "csharp_symbol_name_ready=false" + }, + "metadata_target": { + "ready": false, + "degraded_reason": "csharp_metadata_target_stamp_outdated" + } + } + }, "csharp_symbol_name_ready": false, "csharp_metadata_target_ready": false, "csharp_metadata_target_degraded_reason": "csharp_metadata_target_stamp_outdated",