feat: full OpenAPI coverage via spec-driven generator + agent help-reliability#28
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Expands
fdutyto cover the entire Flashduty OpenAPI surface (254 commands across 27 services), generated from the go-flashduty SDK + spec, and hardens--helpso an SRE agent can call correctly from the description alone — the goal being that--helpis as reliable as the API reference, not something the agent has to guess around.What's here (4 commits)
cligenspec-driven generator — path-derived commands reflected from the go-flashduty SDK + OpenAPI spec; rich--helpwith per-field request descriptions (— desc [enum]) and a response-fields block.--helpstates the{request_id,error,data}envelope is unwrapped: generated lists →jq '.items[]'(not.data.items[]), curated lists →.[].AuditRecordIDRequest(rule-audit-detail id = audit-record id, not rule id), progress enumTriggered/Processing/Closed,export_fields29-value enum, post-mortem desc — plus the restoredtoontag so--output-format toonemits snake_case keys.incident listwindow < 31 days /--limit≤ 100;audit searchwindow < 90 days /--limit≤ 99.command.goWriteRawstreams*exportCSV bodies verbatim so> file.csvcaptures 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 vetgreen; 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.