Repository navigation
[MER-7781] add experimental decisions commands - #49
Conversation
Commands for internal dogfooding go behind the experimental Go build tag, so no release (stable or nightly) includes them. config.Experimental gates smaller changes inside existing commands. CI builds, tests, and smoke-tests both variants, and golangci-lint runs with the tag on.
Current Aviator status
This PR was merged using Aviator (commit 4a60283).
See the real-time status of this PR on the
Aviator webapp.
Use the Aviator Chrome Extension
to see the status of your PR within GitHub.
|
9740166 to
0637c73
Compare
✅ FlexReview StatusCommon Owner:
Review SLO: |
tulioz
left a comment
There was a problem hiding this comment.
a lot of the size here comes from the API in aviator-co/mergeit#15148 splitting apart things that belong in one request, so the CLI rebuilds them on the client. i left comments there. once that's reshaped this can be one command per request, and it gets a lot smaller.
|
|
||
| // Experimental reports whether this binary was built with the experimental | ||
| // tag. No release sets it; only local `just build-experimental` builds do. | ||
| const Experimental = true |
There was a problem hiding this comment.
lets drop the experimental build tag and just keep Hidden: true, which already keeps these out of help and completion, same as the hooks commands. the tag isn't free: CI and just test now run the whole suite twice, there's a separate build recipe and lint config, and since no release includes it nobody can dogfood from nightly, they have to build from source. this const has no callers either. if we want a convention for experiments, that should be its own discussion and not ride in with this feature.
| } | ||
|
|
||
| var decisionsApplyCmd = &cobra.Command{ | ||
| Use: "apply <id>", |
There was a problem hiding this comment.
lets drop apply and applyBatch and have each command make one request. applyBatch splits a batch into a create, a PATCH per decision and a comments POST per decision, with no transaction. say an agent applies a file with three decisions and the second PATCH gets a 409: the creates and the first patch are recorded, the rest aren't, and the only record of what landed is the error text. if it reruns the file, every create and comment gets posted again. every other command goes through applyBatch too, so the whole PR is shaped around this.
There was a problem hiding this comment.
The API is now built around batching better, so this should be resolved in the new normal flow.
| "path/filepath" | ||
|
|
||
| "emperror.dev/errors" | ||
| "go.yaml.in/yaml/v3" |
There was a problem hiding this comment.
i don't want to add a direct yaml dependency just for this file. lets make it actors.json and use hujson like settingshook.go does for the claude settings, so we have one way of editing json config files.
There was a problem hiding this comment.
Actors removed. No longer relevant.
| "remote actors live in Aviator. You can also describe actors locally in\n" + | ||
| "~/.config/aviator/actors.yaml (or $XDG_CONFIG_HOME/aviator/actors.yaml),\n" + | ||
| "by hand or with `aviator actors edit`. The CLI never sends that file to\n" + | ||
| "the API; the server reconciles it with the remote actors.\n" + |
There was a problem hiding this comment.
the CLI never sends this file and nothing on the backend reconciles it, so this tells people something that doesn't happen. lets just say it's a local list of known names, and the server finds or creates actors by role and name on each write.
There was a problem hiding this comment.
Actors removed. No longer relevant.
| // resolveActors fills each actor's role from the local actors file, refusing | ||
| // any name that isn't there so a typo is caught before the batch is sent. The | ||
| // built-in person actor needs no entry and is sent without a name. | ||
| func resolveActors(refs []*api.ActorRef) error { |
There was a problem hiding this comment.
this refuses any name that isn't in the file. the file is there to steer agents toward names we already use, not to gate them, so a new name should go through as long as it comes with --role.
There was a problem hiding this comment.
Actors removed. No longer relevant.
| // DecisionComment is one entry in a decision's thread. | ||
| type DecisionComment struct { | ||
| ID int `json:"id"` | ||
| Author DecisionAuthor `json:"author"` |
There was a problem hiding this comment.
the backend sends this as actor, so this always decodes empty and show prints every comment with no author. the fixtures in decisions_test.go use author too, which is why the tests pass. it can also be null, same for raised_by.
|
|
||
| // buildAnchor turns the anchor flags into an anchor, or nil when none were | ||
| // passed. | ||
| func buildAnchor(path, lines, commit string) (*api.DecisionAnchor, error) { |
There was a problem hiding this comment.
the backend replaces the anchor as a whole, so edit 7 --commit abc to bump the SHA also wipes the path and lines. lets merge the flags into the current anchor before patching.
| type DecisionAuthor struct { | ||
| Role string `json:"role"` | ||
| Name string `json:"name,omitempty"` | ||
| AgentMeta *AgentMeta `json:"agent_meta,omitempty"` |
There was a problem hiding this comment.
the backend never sends agent_meta, so model in the --json output is always empty. lets drop it until the backend returns it.
There was a problem hiding this comment.
The backend sends this now, so it should not be empry anymore
aviator decisions list, show, create, edit, comment (and comment --edit), settle, reopen, and apply record decisions on a Verify session's pull request through POST /decisions/batch. Every command sends one batch, so a change across several decisions is all or nothing; apply sends its JSON file as the batch body. Each write is recorded under a --role (person, coding_agent, review_agent, external_bot), with --model, --harness, and --skill describing the agent when there is one. Hidden from help and completion.
0637c73 to
f119edb
Compare
Drop the experimental build tag: decisions is Hidden, which keeps it out of help and completion, so CI, just test, and lint run once again and no separate build is needed. edit now lays --path, --lines, and --commit over each decision's current anchor, since the server replaces an anchor as a whole and a bare --commit used to wipe the path and lines. show and --json leave out a null raised_by, author, or comment user instead of printing it blank.
| Author authorFlags | ||
| } | ||
|
|
||
| var decisionsCreateCmd = &cobra.Command{ |
There was a problem hiding this comment.
now that the backend takes one atomic batch, lets ship just list, show and apply, and drop create, edit, comment (including --edit), settle and reopen. yeah they're nicer to type, but each one rebuilds part of the backend's rules on the client and gets some wrong.
settle without --resolution always 400s with "Settling needs a resolution", and reopen without --body always 400s with "Reopening needs a comment saying why". settle --summary turns a settle anyone can do into an edit only the raiser can, so a reviewer settling someone else's decision gets a 403 and the whole batch is refused. comment edits should move into the batch too, i left that on aviator-co/mergeit#15404.
| // readDecisionBatch reads apply's file, which is the API's batch body. Unknown | ||
| // keys are refused, so a typo, or a via or batch-level model_metadata from the | ||
| // old format, is caught before anything is sent. | ||
| func readDecisionBatch(path string) (*api.DecisionBatchRequest, error) { |
There was a problem hiding this comment.
apply should send the file's bytes as the body unchanged and let the server's strict models validate it. decoding into Go structs and re-encoding drops things along the way. "summary": null or "" goes out as nothing, so clearing a summary comes back as "Nothing to change", and "id": 0 turns an edit into a create. unknown keys inside anchor also get dropped silently, because Nullable.UnmarshalJSON skips DisallowUnknownFields, so "anchor": {"file_path": "a.go", "line": 40} records an anchor with no line. with that and the commands above gone, Nullable, DecisionBatchItem, DecisionCommentInput and the old format tests can go too.
| "to all of them, and apply records a whole JSON batch. Each command sends\n" + | ||
| "one request, so either all of it is recorded or none of it is.\n" + | ||
| "\n" + | ||
| "Writes act as the caller, so they need a user access token. Comments are\n" + |
There was a problem hiding this comment.
lets drop this, nothing a user can see shows where a comment came from.
| a personal token to `api`. | ||
| - `PATCH /<id>/comments/<comment_id>` — `{body}`. | ||
| - Writes need a user token on an account with Verify. Only the user who | ||
| raised a decision can change it (403); anyone in the account can comment. |
There was a problem hiding this comment.
anyone can settle or reopen, only changes to type, title, summary and anchor are limited to the raiser. this should also say settling needs a resolution and reopening needs a comment. the api.Nullable and via lines above are CLI internals, lets keep this block about the size of the other endpoints.
| } | ||
|
|
||
| type decisionListJSON struct { | ||
| Session string `json:"session"` |
There was a problem hiding this comment.
lets call this id like dismiss does. session is a new key for the same thing.
The backend now takes one atomic POST /decisions batch, so the per-verb commands (create, edit, comment, settle, reopen) are gone: each rebuilt part of the server's rules client-side and got some wrong. apply sends the file's bytes unchanged and lets the server's strict models validate them, so nulls, zero ids, and unknown keys are no longer dropped in a Go round trip. Comment edits move into the batch. list follows the paged API with --status, --page, and --limit and reads comment_count. The JSON list key is id instead of session, and list output leaves comments out rather than printing [].
Adds
aviator decisionsfor internal dogfooding. It ships in regular builds but isHidden: true, so it's left out of--help,aviator helpand shell completion. It runs when called by name.Commands (
/api/v1/verify/<n>/decisions)list <id>: one page of a session's decisions in id order, with--status open|settled,--pageand--limit. Shows each decision'scomment_count; threads aren't included.show <id> <decision-id>: one decision with its comment thread,raised_by(the user),author(the latest change's role and model), andresolution.apply <id> --file <path|->: sends the file toPOST /decisionsas one all-or-nothing batch. A batch can create, change, settle, reopen and comment on decisions, and edit comments.applysends the file's bytes unchanged and leaves validation to the server's strict models. Decoding into Go structs and re-encoding dropped"summary": null, turned"id": 0into a create, and silently dropped unknown keys insideanchor.There are no per-verb write commands. The
create,edit,comment,settleandreopencommands from earlier commits are removed, because each one rebuilt part of the server's rules on the client and got some wrong. For example,settle --summaryturned a settle anyone can do into an edit only the raiser can.--jsonbuilds its output from CLI-owned structs. The session is underid(matchingdismiss), andlistaddshas_moreand leavescommentsout instead of printing[].Commits
list,showandapplyfor the new API: singlePOST /decisions, comment edits in the batch, paged list withcomment_count,external_botrole dropped, and thePATCH .../comments/<id>route no longer used.Testing
applysends the file unchanged (nulls,"id": 0, unknown anchor keys), the--jsonshape, the text formatting, and that the command stays hidden.list,showandapplyhaven't been run end to end against the new API yet.