# Tangled: PRs, merges and CI for this repo These are the steps that worked for `demi.dev/spindle` on 2026-11-01 (ENG-469). Only the lead agent does any of this; implementers commit and stop. ## Remote | Remote | URL | | -- | -- | | `origin` | `demi.dev@tangled.org:did:plc:mj26t7lhrwzongl7wg4qtacm` | `git ls-remote origin` proves access. `tang` can't resolve repos that use the new repo records, so every PR step goes through the web UI in the owner's signed-in Chrome. ## Pull requests 1. **Push:** `git push -u origin `. A push to a branch other than `main` starts no pipeline. 2. **Open:** go to `https://tangled.org/demi.dev/spindle/pulls/new?source=branch&sourceBranch=&targetBranch=main`, then fill in **Title** and **Body**. * Click **Create pull request**. The first click often doesn't submit, so click again if the page doesn't redirect to `/pulls`. * Before a third click, check that `/demi.dev/spindle/pulls/` exists, so you don't open a duplicate. 3. **Comment:** in the right-hand **Comments** panel, set the textarea's value and send it. * Set the value with the native value setter followed by an `input` event. * Click into the textarea, then press `cmd+End`, `space`, `BackSpace`, and finally `cmd+Return`. * Reload the page. Then check that the comment shows once in the panel. Searching the page's raw HTML for its heading finds about 3 copies of one comment. 4. **New round:** push to the same branch, then click **Resubmit** next to the round selector (`v0`, `v1`, …). 5. **Merge:** * First check that the page has no **Resubmit** button and that the newest round's head matches `git rev-parse --short `. * Then click **Rebase & merge** at the bottom right of the page. Never use **Manually mark as merged**. * The knot applies the patches as new commits, so afterwards run `git pull --ff-only origin main` and delete the branch and worktree. ## CI on our spindle * **Which spindle runs CI:** repo **Settings → Pipelines → Spindle**. Choose `spindle.demi.dev`, then **Save**. The choice persists, and secrets are stored on the selected spindle. * **Workflows:** `.tangled/workflows/*.yml` with `engine: nixery`. A PR to `main` and a push to `main` each start a pipeline within seconds. * **Reading a run:** * **Pipelines** lists every run. Each run's page is at `/pipelines//workflow/.yml` and shows per-step logs with durations. * The log panel shows "Waiting for the spindle" until the spindle streams the logs. * **Retry** appears on a finished run, and it creates a new pipeline id. * **Cancel (XRPC path drilled 2026-11-01, ENG-469):** the Tangled web UI has no cancel control. In v1.20.0 the web app wires up only Retry and Approve, and the old appview route `POST //pipelines//cancel` returns 404. Cancel through the spindle's own XRPC instead. It needs a service-auth token from the PDS of an account with push access, which here means the owner's. Never print the app password or either token. 1. Create a session on the owner's PDS with an app password (`com.atproto.server.createSession`). This gives an access JWT. 2. Call `GET /xrpc/com.atproto.server.getServiceAuth?aud=did:web:spindle.demi.dev&lxm=org.tangled.ci.cancelPipeline` with that JWT. It returns `{token}`. 3. Call `POST https://spindle.demi.dev/xrpc/org.tangled.ci.cancelPipeline` with `Authorization: Bearer ` and the JSON body `{"repo":"did:plc:mj26t7lhrwzongl7wg4qtacm","pipeline":""}`. * It returns 200 on success. * It returns 404 `PipelineNotFound` if the id doesn't belong to the repo. * The mill then marks the workflow cancelled and tells the executor to destroy it. * Proven on pipeline `3mzbcyk3zfq22`: the page showed "User canceled the workflow", the executor logged `workflow canceled`, and the job container was gone within a second. * **Where a run happened:** * On the VM: `journalctl -u spindle | grep ` shows `workflow started … node_id=spindle-exec-1` and then `workflow finished`. * In the bucket: logs are stored under `logs/.log`.