Summary
Three current --fields failures arise in the same bounded-response projection pipeline:
- an empty graph result becomes one phantom
{} row;
--fields silently overrides an explicit non-JSON format;
- selecting
body_content drops the metadata needed to know it was truncated and how to continue.
These belong in one PR because --fields activates JsonEnvelopeWrapper, which owns primary collection extraction, output-mode wrapping, and projected companion fields.
Environment
origin/main: c642bc4c3dda4ef659df8e6b699e10f61da3c836
cdidx v1.44.3, locally built
- fresh and complete repository index
- full net8.0 and net9.0 suites pass
Reproduction
A. Empty graph projection fabricates a row
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll callees \
DefinitelyMissingSymbol_42 --exact-name \
--db .cdidx/codeindex.db --json \
--fields path,caller_name,callee_name,first_line
Actual:
{
"metadata": {
"result_count": 1,
"returned_count": 1,
"total_count": 1
},
"results": [{}]
}
Without --fields, the same query correctly returns zero callees. With --strict-not-found, the command exits 2 but still reports the phantom result.
B. Explicit CSV is silently replaced by JSON
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll definition \
GetCompletionScript --exact-name \
--path src/CodeIndex/Cli/ConsoleCompletionRenderer.cs \
--db .cdidx/codeindex.db --format csv --limit 1 \
--fields path,name
The command exits 0 and emits a JSON envelope. In contrast, --json --format csv correctly returns a usage error.
C. Body projection loses truncation/recovery facts
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll definition \
ConsoleCompletionRenderer --exact-name --body --limit 1 \
--db .cdidx/codeindex.db --json \
--fields path,name,kind,line,body_content
body_content ends mid-expression at .Append("bat"), but the row omits body_content_truncated, next-start/range data, truncation reasons, and recovery command. Without --fields, the response correctly reports body_content_truncated: true and how to continue.
Expected behavior
- Empty source collections remain empty after projection.
- Explicit incompatible output selectors fail with
E010_USAGE_ERROR; --fields does not silently replace --format csv.
- Selecting bounded body content automatically retains mandatory truncation, range, omission, and recovery metadata.
- Counts remain derived from the real pre-projection collection.
Root cause and evidence
src/CodeIndex/Cli/JsonEnvelopeWrapper.Bounded.cs:87-106 auto-wraps whenever --fields is present before preserving or rejecting an explicit non-JSON formatter.
JsonEnvelopeWrapper.Bounded.cs:1043-1120 does not authoritatively unwrap the empty callers/callees collection, so the whole zero-result payload falls through as one row and projects to {}.
JsonEnvelopeWrapper.Bounded.cs:1298-1324 copies only directly requested fields and aliases, with no dependency closure for bounded body metadata.
Focused PR scope
One projection-contract PR should:
- resolve and validate output mode before auto-wrapping;
- reject
--fields with incompatible non-JSON formats unless formally supported;
- authoritatively extract empty and non-empty references/callers/callees collections;
- define central companion-field groups for bounded bodies;
- derive counts from the real source collection;
- reject unmappable projections instead of fabricating
{};
- add a bilingual changelog fragment.
Non-goals
- Do not add CSV column projection in this PR.
- Do not redesign every JSON output shape.
- Do not change body caps or recovery paging semantics.
- Do not broadly rewrite unrelated serializers.
Implementation guidance and cautions
- Model projection dependencies centrally rather than copying body field lists per command.
- Distinguish an empty collection, a non-empty row lacking one field, and an envelope that was not unwrapped.
- Preserve trust/completeness/recovery fields required to interpret selected data.
- Keep byte-budget accounting truthful after adding mandatory companions.
- If the minimum safe projection cannot fit a byte cap, return the bounded-response error rather than removing recovery metadata.
- Apply equivalent rules to definition, references, callers, callees, inspect, and impact body rows where applicable.
Required tests
Use a matrix covering:
- empty/non-empty references, callers, and callees;
- projected/unprojected envelope, compact, and array/stream forms where supported;
- explicit CSV/TSV/qf/LSP/SARIF conflicts;
- full, line-truncated, byte-truncated, and unavailable bodies;
body_content alone, --fields all, aliases, and unknown fields;
- byte budgets, strict-not-found behavior, counts, and cursors.
Acceptance criteria
- A missing graph target produces
results: [], returned count 0, and total count 0.
--format csv --fields ... never emits JSON; the current preferred contract is a clear usage error.
- Truncated
body_content always includes a truthful truncation flag, next range, and recovery instruction.
- No accepted projection fabricates
{} from an empty envelope.
- Counts, cursors, and byte-limit metadata remain truthful.
Regression history
No current issue covers these three failures in their shared --fields pipeline.
Summary
Three current
--fieldsfailures arise in the same bounded-response projection pipeline:{}row;--fieldssilently overrides an explicit non-JSON format;body_contentdrops the metadata needed to know it was truncated and how to continue.These belong in one PR because
--fieldsactivatesJsonEnvelopeWrapper, which owns primary collection extraction, output-mode wrapping, and projected companion fields.Environment
origin/main:c642bc4c3dda4ef659df8e6b699e10f61da3c836cdidx v1.44.3, locally builtReproduction
A. Empty graph projection fabricates a row
Actual:
{ "metadata": { "result_count": 1, "returned_count": 1, "total_count": 1 }, "results": [{}] }Without
--fields, the same query correctly returns zero callees. With--strict-not-found, the command exits 2 but still reports the phantom result.B. Explicit CSV is silently replaced by JSON
The command exits 0 and emits a JSON envelope. In contrast,
--json --format csvcorrectly returns a usage error.C. Body projection loses truncation/recovery facts
body_contentends mid-expression at.Append("bat"), but the row omitsbody_content_truncated, next-start/range data, truncation reasons, and recovery command. Without--fields, the response correctly reportsbody_content_truncated: trueand how to continue.Expected behavior
E010_USAGE_ERROR;--fieldsdoes not silently replace--format csv.Root cause and evidence
src/CodeIndex/Cli/JsonEnvelopeWrapper.Bounded.cs:87-106auto-wraps whenever--fieldsis present before preserving or rejecting an explicit non-JSON formatter.JsonEnvelopeWrapper.Bounded.cs:1043-1120does not authoritatively unwrap the empty callers/callees collection, so the whole zero-result payload falls through as one row and projects to{}.JsonEnvelopeWrapper.Bounded.cs:1298-1324copies only directly requested fields and aliases, with no dependency closure for bounded body metadata.Focused PR scope
One projection-contract PR should:
--fieldswith incompatible non-JSON formats unless formally supported;{};Non-goals
Implementation guidance and cautions
Required tests
Use a matrix covering:
body_contentalone,--fields all, aliases, and unknown fields;Acceptance criteria
results: [], returned count 0, and total count 0.--format csv --fields ...never emits JSON; the current preferred contract is a clear usage error.body_contentalways includes a truthful truncation flag, next range, and recovery instruction.{}from an empty envelope.Regression history
body_contentto remain grouped with line, truncation, and recovery metadata.{}.No current issue covers these three failures in their shared
--fieldspipeline.