Expand description
GAP-SG-142: agent-native reshaping (--select, --filter, --sort,
--dedupe-by, --max-items, --count-only, --truncate-content,
--max-output-bytes) applied at the single stdout emission point.
GAP-SG-142: agent-native reshaping of the JSON envelope.
Every subcommand used to hand its whole envelope back to the caller, so an
agent had to keep a jaq filter in its prompt just to read one field. This
module gives the CLI the projection / filter / sort / dedup / limit /
truncation surface the sibling tools already expose, applied at a single
point: crate::output serializes the response, hands the resulting
serde_json::Value to apply, and writes what comes back.
Working on the serialized value rather than on each command’s response struct is what keeps this DRY — one implementation covers the whole CLI and no subcommand needs to know the surface exists.
§Invariants
- Failures always reach the caller. An envelope carrying
error: trueorok: falseis emitted verbatim;--filtershapes result rows, never the error contract. - JSON Schema documents are never shaped.
--print-schemaoutput is recognised by its$schemamember and passes through untouched. - Truncation is never silent. Anything that removes data records it
under the
agent_surfacemember and raises a top-leveltruncatedflag. - Derived arrays never survive a reshape. Members that merely restate
the reshaped array (
memories,entities,direct_matches,graph_matches,related_memories) are dropped and listed underaliases_removed. Without a knob the surface is inert, so the envelope stays byte-for-byte identical to the pre-v1.2.2 output and the v1.0.66 alias contract is untouched. - NDJSON streams bypass the surface. Line-oriented emitters keep one record per line; reshaping them would change the stream contract.
§Scope of each knob (GAP-SG-191)
An envelope may carry more than one array, and the three ceilings do NOT all reach the same members. The split is deliberate, and it follows from what each knob removes:
| knob | reaches | why |
|---|---|---|
--max-output-bytes | every array | it removes whole elements to hit a byte budget the caller set for the envelope as a whole |
--max-items | every array | same: it removes whole elements, so a secondary member simply gets the same cap |
--select, --filter, --sort, --dedupe-by | primary array only | they act on the fields or the ordering of elements |
A secondary array is a different collection, not a restatement of the primary
one: graph pairs nodes with edges. Projecting id over edges would
rewrite every element to {} and erase source/target — the projection
would destroy the collection rather than narrow it. Filtering and sorting
fail the same way, on keys that member never had.
Until v1.2.4 --max-items also stopped at the primary array, so
graph --select id --max-items 2 answered with two nodes and all 59 066
edges: 4.55 MB for a request that asked for two items. Members shortened by
the cap are listed under agent_surface.secondary_capped.
Precedence for every numeric knob is the crate-wide one: CLI flag > XDG
config set > named constant. No product environment variable is read.
Modules§
- budget
- Hard ceiling on the serialized size of one envelope (
--max-output-bytes). - filter
- Filter expression grammar for
--filter. - gate
- The refusals. One place decides when a shaping request cannot be honoured.
- shape
- Stateless reshaping primitives applied to the result array of an envelope.
- stream
- GAP-SG-215: the NDJSON stream contract, decided.
- target
- GAP-SG-205: the resolved database target, reported on every envelope that resolved one.
- universe
- GAP-SG-201: what the QUERY already discarded, declared so the output surface can stop describing a set it never saw.
- vocabulary
- GAP-SG-202 / GAP-SG-203: resolving the caller’s keys against the envelope BEFORE any predicate runs.
Structs§
- Agent
Surface - Resolved output-shaping request for the current process.
Functions§
- active
truewhen the installed surface would change an envelope.- apply
- Applies
surfacetovalue, honouring the invariants documented above. - apply_
global - Applies the installed surface to
value. - apply_
with_ premises - The body of
apply, with BOTH ambient facts supplied rather than read. - apply_
with_ target - The body of
apply, with the resolved target supplied rather than read. - get
- Borrows the installed surface, or an inert one when
initnever ran. - init
- Installs the process-wide surface. Idempotent, first call wins.