#+title: Parity Which surface can do what. The CLI is meant to be the complete interface: every capability exists over SSH, and the other surfaces dispatch the same control commands rather than reimplementing them, so they cannot drift. This page is updated in the merge request that changes a row. Three surfaces now: the CLI over SSH, the web, and the iOS client (krz/gitbay-ios). A =no= in the cli column is a defect, not a preference — it means some surface reached around the registry and stranded a capability where only it can reach. * Rule A capability lands over SSH first. If it belongs to the triage/review/respond loop, it lands on the web in the same merge request. Anything whose input is a credential — secrets, mirror tokens, API tokens — stays SSH-only by design: the web dispatcher refuses =SSHOnly= commands outright. Session minting is not one of them: what a browser submits to ask for a login link is a username or an address, and the credential it gets back travels by mail. Rows are one page or one action each. Grouped rows hide gaps, twice now: "browse, log, blame, search" read as covered while blame had no command at all, and "build list, log" read as covered while the job list a trigger can name had none (krz/gitbay#50), leaving the picker browser-only and the iOS build screen unable to say more than the log. * Merge requests | capability | cli | web | ios | |------------------------+-----+-----+-----| | read, diff, commits | yes | yes | yes | | review and check times | yes | yes | yes | | who resolved it, when | yes | yes | yes | | comment | yes | yes | yes | | edit title and body | yes | yes | yes | | review (approve etc.) | yes | yes | yes | | resolve a thread | yes | yes | yes | | comment on a diff line | yes | yes | yes | | merge (all strategies) | yes | yes | yes | | close | yes | yes | yes | | create | yes | yes | yes | | draft, ready | yes | yes | no | | search title and body | yes | yes | no | | create from a fork | yes | yes | yes | | retarget | yes | yes | no | | milestone | yes | yes | yes | | request a review | yes | yes | no | | choose body markup | yes | yes | no | | stacked merge requests | yes | yes | no | Reviews and checks carry the time they last said something, and a =ci/= check carries how long its build ran, so a merge request reads without opening the build. Statuses posted through =status set= have no build and report only the time. A merged or closed merge request names who resolved it and when; a row without that stamp — a =migrate= import, or one merged before krz/gitbay!137 with no =mr.merged= event to backfill from — says neither rather than inventing a time. A merge request whose target is another open merge request's source branch is stacked on it: =mr show= and the page say so both ways, and when the lower one merges, everything stacked on it is retargeted onto what it merged into with its reviews kept. A squash or rebase merge is refused while anything is stacked on the merge request, since it would rewrite the commits the stack builds on. The iOS client renders the retarget but has no stack view yet. A draft merge request is open but not asking: it does not merge, and it does not appear in anyone's review queue. Draft is a flag rather than a fifth state, so every =state = 'open'= rule still means what it did. Batched review and range-diff (krz/gitbay#111) are not built. =mr review request --add = asks a *particular* person, who then carries the merge request in their queue and is notified; =--remove= withdraws the ask. Without one the queue is computed from involvement — what you own, are granted, or reach through an org or team — so a collaborator with write access who has not touched a thread hears nothing until they do (krz/gitbay#145). Creating from a fork works anywhere the source can be typed as =owner/name:branch=. The web's source picker offers the branches of every fork the viewer can push to in that form, and the repository header has the fork control, so the whole path — fork, edit a file, propose — runs in a browser. Retargeting moves an open merge request onto another branch of the same repository and stales the reviews, since an approval was of the diff against the old branch. * Issues | capability | cli | web | ios | |---------------------+-----+-----+-----| | read, list, filter | yes | yes | yes | | filter by label, assignee, author, milestone | yes | yes | no | | search title and body | yes | yes | no | | create | yes | yes | yes | | comment | yes | yes | yes | | edit title and body | yes | yes | yes | | close and reopen | yes | yes | yes | | labels, assignees | yes | yes | yes | | milestone | yes | yes | yes | | milestone list | yes | yes | yes | | labels: list, colour | yes | yes | no | | choose body markup | yes | yes | no | Labels are created on the fly by =issue label --add= and managed by =label list=, =label set