Skip to content

fix(cli): curate channel escalate-rule-list with compact default projection - #174

Merged
ysyneu merged 2 commits into
mainfrom
fix/curate-escalate-rule-list
Aug 28, 2026
Merged

fix(cli): curate channel escalate-rule-list with compact default projection#174
ysyneu merged 2 commits into
mainfrom
fix/curate-escalate-rule-list

Conversation

@ysyneu

@ysyneu ysyneu commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Defect

channel escalate-rule-list was still a raw generated command (internal/cli/zz_generated_channels.go): in json/toon mode it marshaled the full nested EscalateRuleItem record for every rule — layers with their notify targets, time_filters, template_id, etc. A channel with a few dozen rules produced a structured dump of tens of thousands of characters — far over any sane budget for piping into jq or reading directly — forcing callers to re-fetch with client-side filtering to get at the fields they actually wanted.

Root cause

No projection layer: the generated command passes the SDK response straight to printGenericResult, which marshals every field of every row. This is the same class of defect already fixed for incident list (see internal/cli/fieldproject.go).

Fix

Curate the command exactly on the incident-list pattern — the hand-written leaf in internal/cli/channel.go registers first, so genAddLeaf drops the generated twin (generated code untouched):

  • Identical request surface: escalate-rule-list <channel-id>, requireBodyFieldOrExactArg(channel_id,channel-id), same --channel-id/--data flags, same genAssembleBody + genFoldPositional + genBindBody assembly into flashduty.ChannelScopedListRequest.
  • Structured mode (json/toon): rows default to the compact projection rule_id,rule_name,status,priority,filters with a stderr note (noteDefaultProjection); --fields overrides it (empty value is an error, unknown field names the valid set); output runs through projectFieldsboundProjectedOutput(proj, compactListOutputLimit)noteProjectionShortening.
  • Table mode: explicit columns ID / NAME (MaxWidth 50) / STATUS / PRIORITY / UPDATED.
  • --help documents the compact default plus the full response-field block (top-level array).
  • make gen-cards regenerates the GENERATED:channel[escalate-rule] fence in skills/flashduty/reference/escalation.md (CI's skilldoc check validates it).

Intentional break (same as the incident-list curation): structured output changes from {items:[...full records...]} to a bare top-level array of compact rows. Old jq '.items[]' consumers fail loudly (jq cannot index an array), and the stderr note + updated --help + regenerated card point at the new shape. Note: filters is a nested object that boundProjectedOutput does not truncate (it only shortens top-level strings), so a pathologically large filter set errors with the responsible field named instead of silently corrupting.

Verification

  • make fmt — gofumpt/gci clean
  • go test ./internal/cli/ -run TestChannelEscalateRuleListStructuredProjection -v — all subtests PASS (json default keys + stderr note + absence of layers/template_id; toon default; --fields override; empty --fields error; unknown-field error; table-mode headers without note; channel_id folded from positional and --channel-id into the request body)
  • go test ./... — whole suite green (incl. TestEveryOperationHasPathCommand / TestGeneratorTargetsFullSpec)
  • make gen-cards → only skills/flashduty/reference/escalation.md modified; go run ./internal/cmd/skilldoc checkskilldoc: cards OK
  • make lint — 0 issues; make build — builds
  • flashduty channel escalate-rule-list 1 2Error: expects exactly one channel_id (curated leaf owns the path); --help shows --fields, the compact-default prose, and the TOP-LEVEL array response block

ysyneu added 2 commits August 27, 2026 22:24
…ection

The generated escalate-rule-list command marshaled the full nested
EscalateRuleItem record (layers, time_filters, template, ...) for every
rule in json/toon mode, so a channel with a few dozen rules produced an
output far too large to read or pipe usefully.

Shadow the generated twin with a curated command (same path, same
positional/--channel-id/--data request surface): structured mode now
projects each rule to rule_id,rule_name,status,priority,filters by
default with a stderr note, overridable via --fields through the
existing fieldproject helpers and bounded to the 16 KiB list budget;
table mode renders explicit ID/NAME/STATUS/PRIORITY/UPDATED columns.
Regenerate the escalation.md card fence (skilldoc check passes).
Conflict in internal/cli/fieldproject_test.go: both sides appended test
functions after TestBoundProjectedListErrorNamesLargestFields (this branch:
the escalate-rule-list projection suite; main: the identifier-exemption
suite). Kept both blocks.
@ysyneu
ysyneu merged commit a1c6bfe into main Aug 28, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant