Skip to content

feat: full OpenAPI coverage via spec-driven generator + agent help-reliability#28

Merged
ysyneu merged 5 commits into
mainfrom
feat/cli-full-coverage
Jun 2, 2026
Merged

feat: full OpenAPI coverage via spec-driven generator + agent help-reliability#28
ysyneu merged 5 commits into
mainfrom
feat/cli-full-coverage

Conversation

@ysyneu
Copy link
Copy Markdown
Contributor

@ysyneu ysyneu commented Jun 2, 2026

Expands fduty to cover the entire Flashduty OpenAPI surface (254 commands across 27 services), generated from the go-flashduty SDK + spec, and hardens --help so an SRE agent can call correctly from the description alone — the goal being that --help is as reliable as the API reference, not something the agent has to guess around.

What's here (4 commits)

  1. cligen spec-driven generator — path-derived commands reflected from the go-flashduty SDK + OpenAPI spec; rich --help with per-field request descriptions (— desc [enum]) and a response-fields block.
  2. Full OpenAPI coverage — every public operation gets a command.
  3. List-envelope-aware response help--help states the {request_id,error,data} envelope is unwrapped: generated lists → jq '.items[]' (not .data.items[]), curated lists → .[].
  4. Help-reliability fixes from the agent eval (this PR's head):
    • go-flashduty bumped to the merged docs#67 sync: AuditRecordIDRequest (rule-audit-detail id = audit-record id, not rule id), progress enum Triggered/Processing/Closed, export_fields 29-value enum, post-mortem desc — plus the restored toon tag so --output-format toon emits snake_case keys.
    • incident list window < 31 days / --limit ≤ 100; audit search window < 90 days / --limit ≤ 99.
    • command.go WriteRaw streams *export CSV bodies verbatim so > file.csv captures bytes, not the "OK: POST ..." ack.

Validation

Built against the merged go-flashduty main (pseudo-version v0.5.3-0.…-62b37649b2f0, no release). go build + go vet green; toon keys snake_case; the two eval follow-ups verified live (monit rule-audit-detail --help, incident detail --help).

The CLI's full-coverage + help reliability was validated by a 126-question agent eval (real ai-sre sessions → transcript analysis → adversarial judge) that converged: every residual failure is a backend 5xx / env-data / agent self-correction, not a CLI/help defect.

ysyneu added 4 commits June 1, 2026 00:25
Adds internal/cmd/cligen: a generator that emits a cobra command for every
OpenAPI operation not owned by a curated command, so the CLI can cover the
full API surface with help sourced from the API description.

How it works (mirrors go-flashduty's own gen):
- reads the SAME spec from the linked go-flashduty module (always in sync);
- REFLECTS the compiled SDK for authoritative method/request-type names and
  data-return shape (reuses naming.go for service/method mapping);
- joins spec field descriptions/required/enums + request example for help;
- emits typed cobra commands that build the request from scalar flags and/or
  a --data JSON body, call the typed SDK method, and print via the shared
  Printer (TOON/JSON/table) — so timestamps render RFC3339 for free.

Generated commands attach to groups by API path; genAddLeaf makes curated
commands win on any name conflict. This commit generates the Calendars
service (8 commands) as an end-to-end vertical slice: build + vet + tests
green; --help is API-grade; live read calls against dev return correct TOON.

gen_support.go holds the hand-written runtime helpers shared by generated
files. Full generation across all services follows in the next commit.
Generate a cobra command for every one of the 254 public operations so the
CLI covers the entire Flashduty API surface, with schema-grade --help an agent
can call from without guessing.

Command tree is path-is-king: group = first API path segment, verb = the
remaining segments hyphen-joined (`POST /status-page/change/create` ->
`flashduty status-page change-create`). The tree is uniformly two levels and
collision-free — flattening removes the prior prefix-nesting hazard where one
op's path was a parent of another's (e.g. /monit/rule/update vs
/monit/rule/update/fields).

The generator now emits for ALL operations (no curated skiplist). Curated
commands win the exact path-name at registration (they register first), so a
curated command keeps the canonical command while its generated twin is
dropped; curated commands at an ergonomic name coexist as extras. This
guarantees every operation is reachable at its path-name and auto-covers
curated composites/variants (e.g. /incident/reset, /incident/field/reset).

Retire curated groups that were pure renamed duplicates of generated
path-commands (statuspage, mcp, postmortem, escalation-rule); their operations
remain reachable at status-page/safari/incident/channel path-names. Keep
value-add curated commands (incident/alert/team/template ergonomics, oncall
`who`, monit-query/monit-agent diagnostics).

Each generated command's --help renders the operation summary, description,
API path, every request field (type, required, enum, description, JSON-via
--data for complex fields) and a realistic example, all sourced from the spec;
structure is read by reflecting the compiled SDK.

coverage_test.go asserts the core invariant: every spec operation resolves to a
command at its path-name (walking the real cobra tree), and the generator
targets all 254 operations.
Make `fduty <cmd> --help` reliable enough that an SRE agent can call
the API from help alone, without guessing (the eval goal).

- responseSectionList: curated list commands flatten the {items}
  envelope into a top-level array, so their --help now says "this
  command's --json is a TOP-LEVEL array — pipe jq '.[]', NOT
  .items[]"; generated list commands keep the items[] envelope
  wording. Removes the all-null jq guess on list output.
- listEnvelope detection: a list field is the rows array iff every
  other sibling is a scalar (handles {items,limit,p,total} member
  envelopes); type match uses strings.HasPrefix(f.Type,"array").
- Help-quality fixes from the audit: Response-fields blocks, typed
  flags, constraint/enum annotations across the generated surface.
- zz_generated_response_help.go: spec-derived response-help map read
  by curated commands for the same help parity.

Builds against go-flashduty v0.5.2. The toon snake_case output fix
lives in go-flashduty (toon struct tags) and is wired in by a
dependency bump once that change is published.
Bumps go-flashduty to the merged docs#67 sync (AuditRecordIDRequest split,
progress enum, export_fields enum, post-mortem desc) + the restored toon tag,
and lands the CLI-side eval fixes so an SRE agent can call correctly from
`--help` alone:

- cligen (internal/cmd/cligen): list help states the {request_id,error,data}
  envelope is unwrapped — pipe `jq '.items[]'` for generated lists, not
  `.data.items[]`.
- incident.go: `incident list` --since/--until window must be < 31 days, --limit
  max 100. audit.go: `audit search` window < 90 days, --limit max 99.
- command.go: WriteRaw streams *export CSV bodies to the writer verbatim so
  `> file.csv` captures bytes instead of the "OK: POST ..." acknowledgment.
- Regenerated all 254 commands against the new SDK (toon keys snake_case;
  rule-audit-detail --id documented as the audit-record id, not the rule id).

go.mod pins go-flashduty to the merged main pseudo-version (no release).
Build + go vet green.
func exampleHelp(o specOp, verb string, s service) string {
cmdPath := "flashduty " + cliGroup(o.Path) + " " + cliVerb(o.Path)
if o.Example != "" {
return " " + cmdPath + " --data '" + o.Example + "'"
Reconcile the full-OpenAPI-coverage feature with main's #26 (incident
6-char short-id resolution in detail/get + list --nums) and #27 (shell
tab-completion + install.sh auto-setup).

incident.go conflicts resolved to main's design:
- list: keep --nums (#26) plus the eval's "--since→--until window < 31d"
  help text.
- get: adopt #26's resolveIncidentArg→List-by-incident_ids body; the
  full-coverage branch's /incident/info get is superseded by #26's new
  `detail` command (which carries the no-window Info path). All 7
  incident short-id tests pass.

postmortem.go / status_page.go (deleted by the coverage branch in favor
of generated commands) kept deleted; #27's registerEnumFlag additions to
them drop with the files — generated post-mortem/status-page commands
cover those ops. completion.go + install.sh + the curated commands'
registerEnumFlag calls retained.
@ysyneu ysyneu merged commit 8c0c9d6 into main Jun 2, 2026
9 of 11 checks passed
@ysyneu ysyneu deleted the feat/cli-full-coverage branch June 2, 2026 03:37
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.

2 participants