# Collaborating Pulls, issues, comments and reviews are records in each participant's own account. Writes to someone else's repository go through a gateway they trust. [Pulls, issues and moderation](../design/collaboration.md) and [Gateway](../design/gateway.md) explain how this works. The examples use `alice.test` as the owner and `bob.test` as a collaborator. ## The appview Pulls and issues on a public repository are records in many accounts, so listing them takes an appview: a `knotless-serve` with an index. knotless uses `https://knotless.dev` unless `KNOTLESS_APPVIEW` names another: ```sh export KNOTLESS_APPVIEW=https://appview.example ``` Set it empty (`KNOTLESS_APPVIEW=`) for no appview at all: commands that list pulls and issues on public repositories then fail. Private repositories need no appview: their pulls and issues are read from the members' repos in the space. ## Give someone write access Only an account can write to its own repo, so collaborators push through a gateway: a `knotless-serve` that the owner has delegated to. Delegate once, then share the repository for writing: ```sh knotless delegate alice.test did:web:git.example.com # as alice knotless share knotless://alice.test/proj bob.test --write ``` `delegate` opens a browser for you to approve the gateway at your PDS. The gateway then holds a session for your account, so run your own or use one you trust. Share for writing before you have delegated, and `share` delegates to the appview's gateway first, in the same way. The collaborator pushes with plain git: ```sh git push knotless://alice.test/proj feature # as bob ``` Their remote helper signs each request with a service token from their own PDS. The gateway checks the token against the repository's collaborators before it writes as the owner. Commits sent to one branch at the same time, with `knotless commit` or the MCP `commit` tool, are queued and land together as one push, rebuilt on the new tip if the branch moved underneath them. To see who has access, or take it away: ```sh knotless members knotless://alice.test/proj knotless unshare knotless://alice.test/proj bob.test ``` ## Protect a branch ```sh knotless protect knotless://alice.test/proj main # forward only, no merge commits knotless protect knotless://alice.test/proj main --merges # forward only, merge commits allowed knotless protect knotless://alice.test/proj main --reviews 1 --code-owners # and reviewed knotless rules knotless://alice.test/proj knotless unprotect knotless://alice.test/proj main ``` Through the gateway, a collaborator can only move a protected branch forward and can never delete it. Unless the rule allows merges, they cannot put merge commits on it either, and pulls merge into it squashed, one commit each. With `--reviews `, a pull merges into the branch only once `` of you and your collaborators, other than its author, approve it (`knotless comment --approve`), and none of you has asked for changes. With `--code-owners` too, each path it changes that the branch's CODEOWNERS file gives owners needs one of them to approve it: ``` # .knotless/CODEOWNERS, CODEOWNERS or .github/CODEOWNERS * @alice.test web/ @bob.test @carol.test docs/ did:plc:abc123 ``` The last line that matches a path names its owners. `--dismiss-stale` counts only approvals of the pull's head as it is now. `knotless merge --check` lists what a pull still lacks, and the owner's `--force` merges past it. The owner writes to their own account without the gateway, so the rules hold the owner only as far as their own client keeps to them. ## Fork, pull, review and merge Anyone can fork a repository and offer changes back: ```sh knotless fork knotless://alice.test/proj # as bob git push knotless://bob.test/proj topic knotless pull knotless://alice.test/proj main knotless://bob.test/proj topic "Fix the parser" -m "Details." ``` `pull` prints the pull's `at://` URI. `-m -` reads the body from stdin, where Markdown's backticks and `$` are safe from the shell: ```sh knotless pull knotless://alice.test/proj main knotless://bob.test/proj topic "Fix the parser" -m - < body.md ``` List the pulls, or show one with its comments: ```sh knotless list knotless://alice.test/proj --pulls knotless show knotless://alice.test/proj ``` The MCP `show_item` tool, on a pull, also says what it brings in and whether it merges cleanly. To review someone else's pull, comment with `--approve` or `--changes`: ```sh knotless comment knotless://alice.test/proj "Looks right" --approve ``` Your latest verdict is the one that stands. An approval is of the head you looked at: push to the pull and it shows as an approval of an earlier version until you review again. To say something of particular lines, add `--line` with the file and the lines, `:old` for lines the pull removes. Each starts a thread: ```sh knotless comment knotless://alice.test/proj "A few things" --changes \ --line src/main.rs:12 "This can panic" \ --line src/main.rs:20-24:old "Why remove this?" ``` `comment` prints each comment's URI. Answer one with its URI as the subject, and once it is dealt with, `knotless resolve knotless://alice.test/proj `. On the pages, the `+` beside a line of the pull's Files tab does the same. The owner or a collaborator merges it: ```sh knotless merge knotless://alice.test/proj ``` `merge` stops on a conflict first, naming the paths (`merging conflicts in `). Otherwise it fast-forwards when it can, and makes a merge commit, `Merge `, a blank line and the pull's URI, by you. Into a protected branch without `--merges`, it squashes the pull into one commit even when it could fast-forward: the head's author and date, you as committer, and the pull's title, body and URI as the message. A commit whose message ends with a pull's URI links to that pull on the web pages. As a collaborator, the merge goes through the gateway, which takes or refuses each ref on its own: the branch update and the head branch's deletion. `merge` fails on the first ref refused. When a pull comes from a branch of the repository itself, merging it deletes that branch in the same push. `--keep-branch` keeps it, and so does another open pull that names it. In a repository that names [runners](workflows.md), `merge` refuses until the pull's checks pass. `--wait <secs>` waits for them, and `--force`, the owner's alone, merges past them. The refusal names what failed, what is still running, or the older head or base the checks are of, and ends with where to read why: `` Their logs: `knotless checks <url> --pull <pull> --log` ``. [The merge gate](../reference/cli.md#the-merge-gate) lists each wording. To point a pull at other branches: ```sh knotless retarget knotless://alice.test/proj <pull at-uri> --base release ``` Only the pull's author can change its head branch, with `--head`. ### Revert a merged pull The owner or a collaborator undoes a merged pull with a pull of its own: ```sh knotless revert knotless://alice.test/proj <pull at-uri> -m "It broke the build" ``` It prints the new pull's URI. The pull brings in one commit, on a new branch `revert/<rkey>` cut from the target branch as it is now, that undoes everything the merge brought in. Its title is `Revert "<title>"`, and its body links the merged pull and says why. Check and merge it, or queue it, as any pull. When the target branch has since changed what the pull changed, `revert` stops, naming the paths (`reverting conflicts in <paths>`). On the web, a merged pull's page has a Revert button that does the same and opens the new pull. ### Use the merge queue When many pulls are ready at once, queue them. A runner started with `--queue` checks queued pulls together, in batches, and lands each batch that passes: ```sh knotless merge knotless://alice.test/proj <pull at-uri> --queue knotless queue knotless://alice.test/proj knotless queue knotless://alice.test/proj --leave <pull at-uri> ``` `merge --queue` returns at once. `queue` lists each queue in order, and the pulls ejected from it with the reason. [The merge queue](../design/merge-queue.md) explains how batches are built and bisected. ## Open and close issues ```sh knotless issue knotless://alice.test/proj "Crashes on empty input" -m "Steps…" knotless list knotless://alice.test/proj --issues knotless close knotless://alice.test/proj <issue at-uri> knotless reopen knotless://alice.test/proj <issue at-uri> ``` A pull whose title or body says `Fixes <issue>` (or `Closes`, `Resolves`), naming the issue by its page URL or its `at://` URI, closes that issue when it merges into the default branch, directly or through the merge queue. See [closing issues](../design/collaboration.md#closing-issues-from-a-pull). The keyword must come right before the issue, in any case, and may carry punctuation before it and one `:` after (`(fixes:`). A word that merely holds a keyword, such as `prefixes`, does not count. The issue may be wrapped in `<>` or `()`, or followed by `.`, `,`, `;`, `:`, `!` or `?`. A page URL may be `https://` or `http://`, on any host, with `%3A` in the DID and a trailing `/`, query or `#fragment`; a Markdown link counts by its target. `#12`, a pull's URI, or an issue on another repository closes nothing. Each issue counts once, however often it is named, and only an open one the merger may close is closed. ## Write from the repository's templates A repository can say what its pulls and issues should hold, in `.knotless/pull.md` and `.knotless/issue.md`. They are Markdown: a pull's is read from its base branch, an issue's from the default branch. Their `<!-- -->` notes say what the title should be, and anything else that belongs to the writer rather than the body. ```sh knotless template knotless://alice.test/proj pull # into the default branch knotless template knotless://alice.test/proj pull release # into release knotless template knotless://alice.test/proj issue ``` With a template, a pull or issue is refused if it has no body, a blank one, one of only the template's notes, or one with none of the template's headings (`## Why`, at any level, in any case). The error names what is missing and carries the template, so whoever opens it, a person or an agent, writes the title and body from it. A body that keeps even one of the headings goes in as written, less any of the template's notes copied into it, so a section with nothing to say can be left out. The MCP `template` tool gives the same, and `open_pull` and `open_issue` say to read it first. ## Label pulls and issues The owner defines the labels; then the owner and collaborators put them on. Of a scope's exclusive labels, such as `priority/high` and `priority/low`, only one stands at a time. ```sh knotless labels knotless://alice.test/proj add kind/bug --color '#d73a4a' --description "Something broken" knotless labels knotless://alice.test/proj add priority/high --exclusive knotless labels knotless://alice.test/proj add priority/low --exclusive knotless label knotless://alice.test/proj <issue at-uri> kind/bug priority/high knotless unlabel knotless://alice.test/proj <issue at-uri> priority/high knotless labels knotless://alice.test/proj remove kind/bug ``` `show` prints an item's labels under its title. See [labels](../design/collaboration.md#labels). ## Edit what you wrote The author of a pull or issue can edit its title and body. The author of a comment can edit it or take it back. `-m ''` drops a pull's or issue's body. ```sh knotless edit knotless://alice.test/proj <pull at-uri> --title "Fix the parser" -m "Updated details." knotless delete-comment knotless://alice.test/proj <comment at-uri> ``` Pulls and issues are closed, not deleted, so what was said about them stays. ## Moderate An owner can hide a pull, issue or comment on their repository, and block an account from one repository or from all of theirs: ```sh knotless hide knotless://alice.test/proj <issue at-uri> knotless block troll.test knotless://alice.test/proj knotless unblock troll.test knotless://alice.test/proj ``` Anyone can report an account, a repository or a record to the appview's moderation service. The reason is one of `spam`, `violation`, `misleading`, `sexual`, `rude` or `other`: ```sh knotless report <issue at-uri> spam -m "the same link in every issue" ``` The service's operators, the accounts its `KNOTLESS_ADMIN_DIDS` names, read reports and take accounts and records down, signed in as themselves. Other appviews can follow those takedowns as labels. ```sh knotless admin reports knotless admin takedown troll.test ``` ## Rename, transfer or delete a repository ```sh knotless rename knotless://alice.test/proj parser # the old name redirects knotless transfer knotless://alice.test/parser carol.test # offer it to carol knotless accept knotless://alice.test/parser # as carol knotless delete knotless://alice.test/parser --yes ``` `knotless transfer <url> --cancel` withdraws an offer. A transfer moves the pulls and issues too. [Naming and identity](../design/identity.md#renames) explains how renames and transfers keep links working.