Skip to content

Commit 009d557

Browse files
committed
Harden bounded suggestion projections (#5061)
1 parent 514d08d commit 009d557

7 files changed

Lines changed: 105 additions & 38 deletions

File tree

DEVELOPER_GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2208,7 +2208,7 @@ Local suggestion records use the `status` lifecycle field instead of a binary su
22082208

22092209
`suggestions list|export --query <text>` matches the NFKC-normalized query as an ordinal, case-insensitive substring against the redacted stable ID, sampled title, description, context, evidence paths, category, and language. Applying `SuggestionStore.RedactSensitiveText` before matching is a confidentiality contract: a caller cannot use zero-result/count differences to probe a value removed by redaction. Status, time, category, language, and agent filters run first; the text query follows; records are then ordered by descending `CreatedAt` and ordinal stable ID before offset/limit pagination.
22102210

2211-
The structured history projections share one JSON envelope. `--count` and `--summary-only` summarize the complete filtered set rather than the requested page; summary dimensions have fixed distinct-value caps (status 16, category 32, language 20) and expose their own omitted/truncated metadata. `--compact` emits only redacted bounded list fields. `--max-json-bytes` measures the serialized UTF-8 document plus its final platform newline and removes complete trailing result rows until the envelope fits. `total_count` remains authoritative, while `byte_limit_omitted_count`, `next_offset`, and recovery guidance describe byte truncation. If the metadata-only envelope cannot fit, the runner writes no stdout JSON. These projections are local read-only operations and do not change the streaming store's retention or mutation contracts.
2211+
The structured history projections share one JSON envelope. `--count` and `--summary-only` summarize the complete filtered set rather than the requested page; they report zero pagination omissions and classify non-emitted records as projection omissions. Summary dimensions have fixed distinct-value caps (status 16, category 32, language 20) and expose their own omitted/truncated metadata. `--compact` emits only redacted bounded list fields. `--max-json-bytes` measures the serialized UTF-8 document plus its final platform newline, uses a logarithmic fitting-prefix search, and removes complete trailing result rows until the envelope fits. `total_count` remains authoritative, while `byte_limit_omitted_count`, `next_offset`, and recovery guidance describe byte truncation. Row-producing compact and byte-bounded modes reject `--limit 0` so every advertised continuation can progress. If the metadata-only envelope cannot fit, the runner writes no stdout JSON. These projections are local read-only operations and do not change the streaming store's retention or mutation contracts.
22122212

22132213
`suggestions export --format markdown|issue-drafts --output <path>` renders the bounded payload in memory, rejects payloads over 16 MiB before writing, and refuses the selected database or suggestion-store path. For existing files it compares filesystem identities as well as normalized path spelling, so symlinked parents, mount aliases, and hard links cannot bypass source protection. Existing destinations are rejected unless `--overwrite` is explicit. Publication uses a sibling temporary file, flushes its contents, and performs a same-filesystem no-overwrite move or atomic replacement; failed publication cleans the temporary file. The writer emits UTF-8 without a BOM, creates missing parent directories, and keeps JSON-format suggestion exports on stdout. Tests cover the store transition/revision contract, CLI validation and filtering, source-target alias rejection, no-overwrite race safety, replacement, and temporary-file cleanup.
22142214

@@ -5910,7 +5910,7 @@ suggestion sidecar は `DataDirectorySecurity.ResolveSensitiveSidecarDirectoryFo
59105910

59115911
`suggestions list|export --query <text>` は、NFKC 正規化した query を、redaction 済みの stable ID、sampled title、description、context、evidence path、category、language に対して ordinal・大文字小文字を区別しない部分一致で照合します。照合前に `SuggestionStore.RedactSensitiveText` を適用することは confidentiality contract です。caller は 0 件結果や count の差を使って redaction により除去された値を探索できません。status、時刻、category、language、agent の filter を最初に適用し、次に text query、続いて `CreatedAt` 降順と ordinal stable ID の順で並べてから offset/limit pagination を行います。
59125912

5913-
履歴の structured projection は共通 JSON envelope を使います。`--count` と `--summary-only` は要求された page ではなく filter 後の全集合を要約します。summary dimension は distinct 値に固定上限(status 16、category 32、language 20)を持ち、それぞれ omitted/truncated metadata を公開します。`--compact` は redaction・上限付きの list field だけを出力します。`--max-json-bytes` は serialized UTF-8 document と末尾の platform newline を計測し、envelope が収まるまで末尾の完全な result row だけを取り除きます。`total_count` は authoritative なまま、`byte_limit_omitted_count`、`next_offset`、recovery guidance が byte truncation を表します。metadata-only envelope が収まらない場合、runner は stdout JSON を一切書きません。これらの projection は local read-only 操作で、streaming store の retention や mutation contract は変更しません。
5913+
履歴の structured projection は共通 JSON envelope を使います。`--count` と `--summary-only` は要求された page ではなく filter 後の全集合を要約し、pagination omission を 0、出力しない record を projection omission として報告します。summary dimension は distinct 値に固定上限(status 16、category 32、language 20)を持ち、それぞれ omitted/truncated metadata を公開します。`--compact` は redaction・上限付きの list field だけを出力します。`--max-json-bytes` は serialized UTF-8 document と末尾の platform newline を計測し、対数回の fitting-prefix search を使って envelope が収まるまで末尾の完全な result row だけを取り除きます。`total_count` は authoritative なまま、`byte_limit_omitted_count`、`next_offset`、recovery guidance が byte truncation を表します。row を返す compact / byte 上限付き mode は `--limit 0` を拒否し、公開する continuation が必ず進捗できるようにします。metadata-only envelope が収まらない場合、runner は stdout JSON を一切書きません。これらの projection は local read-only 操作で、streaming store の retention や mutation contract は変更しません。
59145914

59155915
`suggestions export --format markdown|issue-drafts --output <path>` は上限付き payload をメモリ上で描画し、書き込み前に 16 MiB 超過を拒否し、選択中の database または suggestion-store path も拒否します。既存ファイルでは正規化した path 表記に加えて filesystem identity も比較するため、symlink 付き親 directory、mount alias、hard link で source 保護を迂回できません。既存の出力先は `--overwrite` を明示しない限り拒否します。公開処理は兄弟一時ファイルを使い、内容を flush してから同一 filesystem 上で no-overwrite move または原子的置換を行い、失敗時は一時ファイルを片付けます。writer は BOM なし UTF-8 を出力し、不足している親 directory を作成し、JSON 形式の suggestion export は stdout のままです。test は store の遷移・revision 契約、CLI validation と filtering、source target alias 拒否、no-overwrite の race safety、置換、一時ファイル cleanup を網羅します。
59165916

TESTING_GUIDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
653653
Local suggestion JSON storage: opaque/stable-ID allocation and legacy migration, all-editable-field revision conflicts, dedup hashing, submission-finalization races, persistence, corruption recovery, atomic writes. Keep suggestion-redaction cases table-driven with negative fixtures for structured PascalCase, snake_case, leading-underscore, and recipe identifiers plus positive fixtures for opaque mixed-character and known token formats; the persistence case should retain an identifier and redact a secret from the same context.
654654
- `DataDirectorySecurityTests.cs`, `ProgramCliTests.cs` suggestion-sidecar coverage
655655
Shared-temp database routing, owner-only directory/file modes, colocated private-directory behavior, and structured CLI filesystem failures.
656+
Suggestion-history query coverage keeps NFKC/case-insensitive matching across every documented field, structured filters before deterministic pagination, aggregate omission reasons, compact redaction, progressing continuations, and whole-document UTF-8 byte budgets in the same production-runtime fixture.
656657
- `SourceCodeDetectorTests.cs`
657658
Source code leak prevention: allowed natural-language inputs vs rejected code blocks (fenced, indented, import runs, etc.).
658659
- `ConsoleUiTests.cs`
@@ -1651,6 +1652,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
16511652
ローカル提案JSON蓄積: 不透明で安定した ID の割り当てと legacy migration、全編集対象 field の revision conflict、ハッシュ重複排除、submission finalization race、永続化、破損復旧、アトミック書き込み。提案 redaction のケースは table-driven に保ち、構造化された PascalCase、snake_case、先頭 underscore 付き、recipe 形式の識別子を negative fixture、不透明な混合文字列と既知の token 形式を positive fixture として含めてください。永続化ケースでは、同じ context 内の識別子を保持しつつ secret を伏字化することを確認します。
16521653
- `DataDirectorySecurityTests.cs`、`ProgramCliTests.cs` の suggestion-sidecar coverage
16531654
shared-temp database routing、owner-only の directory / file mode、private directory での隣接配置、structured CLI filesystem failure。
1655+
suggestion-history query coverage は、全 documented field の NFKC / case-insensitive 照合、決定的 pagination より先の structured filter、aggregate omission reason、compact redaction、進捗可能な continuation、document 全体の UTF-8 byte budget を同じ production-runtime fixture で維持します。
16541656
- `SourceCodeDetectorTests.cs`
16551657
ソースコード漏洩防止: 許容される自然言語入力 vs 拒否されるコードブロック(フェンス、インデント、import連打等)。
16561658
- `ConsoleUiTests.cs`

USER_GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3428,7 +3428,7 @@ Use `cdidx suggestions list` to review recorded suggestions, `cdidx suggestions
34283428

34293429
For full-text triage, add `--query <text>` to `suggestions list` or `suggestions export`. It performs a Unicode NFKC-normalized, case-insensitive substring search over the redacted stable ID, sampled title, description, context, evidence paths, category, and language; normalized queries longer than 1000 characters are rejected. Redaction happens before matching, so a secret removed from persisted output cannot be discovered by querying for its original value. Exact filters and `--query` are applied before deterministic newest-first ordering (`created_at`, then stable ID) and before `--offset` / `--limit`.
34303430

3431-
Large-history automation can select a bounded JSON projection. `--count` returns the authoritative filtered count (a scalar for human `suggestions list`, or a JSON envelope with `--json`); `--summary-only` returns bounded `by_status`, `by_category`, and `by_language` counts without record bodies; and `--compact` returns only `id`, bounded redacted `title`, `status`, and redacted `evidence_paths`. `--summary-only` and `--compact` imply JSON. Add `--max-json-bytes <n>` to cap the complete UTF-8 JSON document, including its final newline. When the limit removes rows, cdidx removes only whole trailing rows and reports authoritative `total_count`, `returned_count`, `byte_limit_omitted_count`, `truncated`, `next_offset`, and `recovery_guidance`; resume with the reported offset or increase the byte limit. A limit too small for the metadata-only envelope fails without emitting partial JSON. Count, summary, and compact are mutually exclusive, and structured projection flags on `suggestions export` require `--format json`.
3431+
Large-history automation can select a bounded JSON projection. `--count` returns the authoritative filtered count (a scalar for human `suggestions list`, or a JSON envelope with `--json`); `--summary-only` returns bounded `by_status`, `by_category`, and `by_language` counts without record bodies; and `--compact` returns only `id`, bounded redacted `title`, `status`, and redacted `evidence_paths`. `--summary-only` and `--compact` imply JSON. Add `--max-json-bytes <n>` to cap the complete UTF-8 JSON document, including its final newline. When the limit removes rows, cdidx removes only whole trailing rows and reports authoritative `total_count`, `returned_count`, `byte_limit_omitted_count`, `truncated`, `next_offset`, and `recovery_guidance`; resume with the reported offset or increase the byte limit. A limit too small for the metadata-only envelope fails without emitting partial JSON. Count, summary, and compact are mutually exclusive, and structured projection flags on `suggestions export` require `--format json`. Row-producing compact and byte-bounded projections require a positive `--limit`; aggregate count and summary modes continue to ignore pagination.
34323432

34333433
Maintainers can make an explicit audited transition with `cdidx suggestions update <id> --status wont_fix --actor <name> --reason "<text>"`. Manual targets are `draft`, `open_in_upstream`, `resolved_in_upstream`, `wont_fix`, `duplicate`, and `superseded`; `submitted_pending_triage` is reserved for successful GitHub submission. The two upstream states require an existing upstream URL or issue number, while returning to `draft` is allowed only when no upstream reference exists. Local `wont_fix`, `duplicate`, and `superseded` dispositions suppress automatic resubmission of the same suggestion but remain distinguishable from an actual upstream submission. A status transition cannot be combined with content edits, refuses a no-op transition, changes `revision_hash`, and records the latest `previous_status`, `status_changed_at`, `status_changed_by`, and optional `status_change_reason`. Audit text is redacted before its final length cap, and the actor defaults to `cdidx-cli` when omitted.
34343434

@@ -6837,7 +6837,7 @@ cdidx には、AI エージェントがギャップや不具合に気づいた
68376837

68386838
全文 triage では、`suggestions list` または `suggestions export` に `--query <text>` を追加します。redaction 済みの stable ID、sampled title、description、context、evidence path、category、language に対して Unicode NFKC 正規化と大文字小文字を区別しない部分一致検索を行い、正規化後に 1000 文字を超える query は拒否します。照合前に redaction するため、出力から除去された secret を元の値で検索して発見することはできません。厳密 filter と `--query` は、決定的な新しい順の並び(`created_at`、次に stable ID)および `--offset` / `--limit` より先に適用されます。
68396839

6840-
大きな履歴を扱う automation では、上限付き JSON projection を選べます。`--count` は filter 後の authoritative な件数を返します(人間向け `suggestions list` では scalar、`--json` 併用時は JSON envelope)。`--summary-only` は record 本文を含めず、上限付きの `by_status`、`by_category`、`by_language` 件数を返します。`--compact` は `id`、上限・redaction 済み `title`、`status`、redaction 済み `evidence_paths` だけを返します。`--summary-only` と `--compact` は JSON を暗黙に有効化します。`--max-json-bytes <n>` は末尾改行を含む UTF-8 JSON document 全体を制限します。上限によって row が省略される場合、cdidx は末尾の完全な row だけを取り除き、authoritative な `total_count`、`returned_count`、`byte_limit_omitted_count`、`truncated`、`next_offset`、`recovery_guidance` を返します。報告された offset から再開するか byte 上限を増やしてください。metadata-only envelope にも足りない上限では、partial JSON を出さず失敗します。count、summary、compact は互いに排他的で、`suggestions export` の structured projection flag は `--format json` を必要とします。
6840+
大きな履歴を扱う automation では、上限付き JSON projection を選べます。`--count` は filter 後の authoritative な件数を返します(人間向け `suggestions list` では scalar、`--json` 併用時は JSON envelope)。`--summary-only` は record 本文を含めず、上限付きの `by_status`、`by_category`、`by_language` 件数を返します。`--compact` は `id`、上限・redaction 済み `title`、`status`、redaction 済み `evidence_paths` だけを返します。`--summary-only` と `--compact` は JSON を暗黙に有効化します。`--max-json-bytes <n>` は末尾改行を含む UTF-8 JSON document 全体を制限します。上限によって row が省略される場合、cdidx は末尾の完全な row だけを取り除き、authoritative な `total_count`、`returned_count`、`byte_limit_omitted_count`、`truncated`、`next_offset`、`recovery_guidance` を返します。報告された offset から再開するか byte 上限を増やしてください。metadata-only envelope にも足りない上限では、partial JSON を出さず失敗します。count、summary、compact は互いに排他的で、`suggestions export` の structured projection flag は `--format json` を必要とします。row を返す compact / byte 上限付き projection の `--limit` は正数でなければならず、aggregate の count / summary mode は pagination を引き続き無視します。
68416841

68426842
メンテナーは `cdidx suggestions update <id> --status wont_fix --actor <name> --reason "<text>"` で、監査情報付きの明示的な状態遷移を実行できます。手動で指定できる遷移先は `draft`、`open_in_upstream`、`resolved_in_upstream`、`wont_fix`、`duplicate`、`superseded` です。`submitted_pending_triage` は GitHub 送信成功時だけ自動設定されます。upstream の2状態には既存の upstream URL または Issue 番号が必要で、`draft` に戻せるのは upstream 参照がない場合だけです。ローカルの `wont_fix`、`duplicate`、`superseded` は同じ提案の自動再送を抑止しますが、実際の upstream 送信済み状態とは区別されます。状態遷移は content 編集と同時指定できず、同じ状態への遷移を拒否し、`revision_hash` を更新して、最新の `previous_status`、`status_changed_at`、`status_changed_by`、任意の `status_change_reason` を記録します。監査テキストは最終的な長さ制限より前に redaction され、`--actor` を省略した場合は `cdidx-cli` です。
68436843

changelog.d/unreleased/5061.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ affected:
1313
- tests/CodeIndex.Tests/ConsoleUiTests.cs
1414
- USER_GUIDE.md
1515
- DEVELOPER_GUIDE.md
16+
- TESTING_GUIDE.md
1617
---
1718

1819
## English

0 commit comments

Comments
 (0)