/ doc / tutorials / tutorial_radicle_patch_workflow.org
tutorial_radicle_patch_workflow.org
  1  #+title: Tutorial: Radicle Patch Workflow — From Issue to Merge
  2  #+author: Xavier Brinon
  3  #+date: [2026-04-06 Mon]
  4  #+startup: indent
  5  #+options: toc:t num:t ^:{}
  6  
  7  * What You Will Learn
  8  
  9  By the end of this tutorial you will have completed the full lifecycle of a
 10  Radicle issue:
 11  
 12  - Created an issue to track a unit of work
 13  - Branched, coded, and committed a fix
 14  - Opened a patch (Radicle's equivalent of a pull request)
 15  - Reviewed and validated the patch
 16  - Merged the patch and closed the issue
 17  
 18  This tutorial uses *MC-003: Configure Project Structure* as a worked example.
 19  The steps apply to any issue in any Radicle repository.
 20  
 21  * Prerequisites
 22  
 23  - A Radicle-initialized repository (=rad init= already run)
 24  - The =rad= CLI installed and working
 25  - A =rad= remote configured (=git remote -v= shows a =rad://= URL)
 26  - Basic familiarity with Git branching and merging
 27  
 28  * Step 1: Create an Issue
 29  
 30  Open a new issue describing the work to be done.
 31  
 32  #+begin_src shell
 33    rad issue open --title "MC-003: Configure Project Structure" \
 34      --description "Create src directory structure for types, components, machines, stores, and server."
 35  #+end_src
 36  
 37  Radicle returns the issue ID — a short hex string like =3085700=. Note it down;
 38  you will reference it throughout.
 39  
 40  Verify the issue exists:
 41  
 42  #+begin_src shell
 43    rad issue list
 44  #+end_src
 45  
 46  You should see your issue in the list with status *open*.
 47  
 48  #+description: Tip Add labels to categorize the issue
 49  #+begin_src shell
 50    rad issue label 3085700 --add "chore" --add "phase-0"
 51  #+end_src
 52  
 53  * Step 2: Create a Working Branch
 54  
 55  Create a branch for your work. A descriptive name helps others understand what
 56  the branch is for.
 57  
 58  #+begin_src shell
 59    git switch -c MC003/configure-project-structure
 60  #+end_src
 61  
 62  * Step 3: Do the Work
 63  
 64  Make your changes. For MC-003 this means creating the directory structure and
 65  adding a barrel export file.
 66  
 67  #+begin_src shell
 68    mkdir -p mission-control/src/{types,components,machines,stores,server}
 69  #+end_src
 70  
 71  Create =src/types/index.ts= as a barrel export (this also ensures Git tracks the
 72  directory — Git does not track empty directories):
 73  
 74  #+begin_src javascript
 75    // src/types/index.ts
 76    // Barrel export for shared types
 77  #+end_src
 78  
 79  Stage and commit:
 80  
 81  #+begin_src shell
 82    git add mission-control/src/types/index.ts
 83    git commit -m "chore: create directory structure for ADR-001 state management"
 84  #+end_src
 85  
 86  * Step 4: Open a Patch
 87  
 88  Push your branch to Radicle's special =refs/patches= reference. This creates a
 89  *patch* — Radicle's collaborative code review object.
 90  
 91  #+begin_src shell
 92    git push rad HEAD:refs/patches
 93  #+end_src
 94  
 95  An editor opens for the patch title and description. Write:
 96  
 97  #+begin_example
 98    MC-003: Configure project structure
 99  
100    Create src directory structure for types, components, machines,
101    stores, and server. Add barrel export for types/.
102  #+end_example
103  
104  Save and close the editor. Radicle prints the patch ID:
105  
106  #+begin_example
107    ✓ Patch 730cd2e opened
108  #+end_example
109  
110  ** What just happened?
111  Radicle created a /Collaborative Object/ (COB) — a Git object that stores the
112  patch metadata, title, description, and revision history. Unlike GitHub PRs
113  which live on a server, this object lives in your local Git repository and syncs
114  to seeds when available. You can work offline.
115  
116  * Step 5: Review the Patch
117  
118  List open patches to confirm yours appears:
119  
120  #+begin_src shell
121    rad patch
122  #+end_src
123  
124  Inspect the patch details:
125  
126  #+begin_src shell
127    rad patch show 730cd2e
128  #+end_src
129  
130  This shows the title, author, status, base branch, and commits. Verify the patch
131  targets =main= and contains the expected commit(s).
132  
133  Review the diff without checking it out:
134  
135  #+begin_src shell
136    rad patch diff 730cd2e
137  #+end_src
138  
139  * Step 6: Validate the Patch
140  
141  Check out the patch to test it locally:
142  
143  #+begin_src shell
144    rad patch checkout 730cd2e
145  #+end_src
146  
147  This creates a local branch =patch/730cd2e= at the patch's latest revision. Now
148  validate:
149  
150  #+begin_src shell
151    # Verify directories exist
152    ls mission-control/src/types mission-control/src/components \
153       mission-control/src/machines mission-control/src/stores \
154       mission-control/src/server
155  
156    # Verify no TypeScript errors
157    cd mission-control && npx tsc --noEmit
158  
159    # Verify dev server starts
160    npm run dev
161  #+end_src
162  
163  If everything passes, the patch is ready to merge.
164  
165  ** If changes are needed:
166  Go back to your working branch, fix the issue, amend, and force-push. Each push
167  creates an immutable /revision/ — the full review history is preserved.
168  
169  #+begin_src shell
170    git checkout MC003/configure-project-structure
171    # ... make fixes ...
172    git commit --amend
173    git push --force
174  #+end_src
175  
176  Optionally include a revision message explaining the update:
177  
178  #+begin_src shell
179    git push -o patch.message="fix: added missing server directory" --force
180  #+end_src
181  
182  * Step 7: Merge the Patch
183  
184  Switch to =main= and merge. You can merge either the patch branch or your
185  original working branch — the effect is the same.
186  
187  #+begin_src shell
188    git switch main
189    git merge patch/730cd2e
190  #+end_src
191  
192  Or equivalently:
193  
194  #+begin_src shell
195    git switch main
196    git merge MC003/configure-project-structure
197  #+end_src
198  
199  Then push =main= to the Radicle remote. This marks the patch as *merged* and
200  syncs the canonical branch with the network.
201  
202  #+begin_src shell
203    git push rad main
204  #+end_src
205  
206  Verify the patch shows as merged:
207  
208  #+begin_src shell
209    rad patch --merged
210  #+end_src
211  
212  ** Key point:
213  There is no "merge button." You merge locally with standard Git commands, then
214  =git push rad main= tells the Radicle network the patch is done. This keeps the
215  canonical branch entirely under your control.
216  
217  * Step 8: Close the Issue
218  
219  Transition the issue state to closed:
220  
221  #+begin_src shell
222    rad issue state --closed 3085700
223  #+end_src
224  
225  Verify:
226  
227  #+begin_src shell
228    rad issue show 3085700
229  #+end_src
230  
231  The status should now read *closed*.
232  
233  * Step 9: Clean Up
234  
235  Delete the local working branch and the patch branch:
236  
237  #+begin_src shell
238    git branch -d MC003/configure-project-structure
239    git branch -d patch/730cd2e
240  #+end_src
241  
242  * Using Magit for the Patch Workflow
243  
244  Because you use Emacs with Magit (obviously), most of the Git steps map directly
245  to Magit commands. Radicle-specific commands (=rad issue=, =rad patch=) still
246  run in a shell — either =M-x shell-command= or =M-x eshell= — but the Git
247  operations are smoother through Magit.
248  
249  ** Branch, Stage, and Commit (Steps 2-3)
250  
251  - =b c= (=magit-branch-and-checkout=) :: create and switch to working branch
252  - Stage files in the Magit status buffer with =s= (stage file) or =S= (stage
253    all)
254  - =c c= (=magit-commit-create=) :: write your commit message in the dedicated
255    buffer
256  
257  ** Open a Patch (Step 4)
258  
259  Magit does not know about =refs/patches=, so use a manual push:
260  
261  - =P= to open the push transient
262  - =e= (=magit-push-elsewhere=) — set the remote to =rad= and the refspec to
263    =HEAD:refs/patches=
264  
265  Or run from the Magit status buffer:
266  
267  =M-!= then type =git push rad HEAD:refs/patches=
268  
269  The editor for the patch title/description opens in your configured =$EDITOR=.
270  If that is =emacsclient= (because of course it is), it opens in Emacs as a
271  commit-message-style buffer.
272  
273  ** Review the Diff (Step 5)
274  
275  From a shell (=M-!=):
276  
277  #+begin_src shell
278    rad patch diff 730cd2e
279  #+end_src
280  
281  Or check out the patch branch and use Magit's diff viewer:
282  
283  - =M-!= then =rad patch checkout 730cd2e=
284  - =d d= (=magit-diff-dwim=) in the Magit status buffer to see changes against
285    =main=
286  
287  ** Merge (Step 7)
288  
289  - =b b= (=magit-checkout=) — switch to =main=
290  - =m m= (=magit-merge=) — select =patch/730cd2e= or your working branch
291  - =P p= (=magit-push-current-to-pushremote=) — push =main= to =rad=
292  
293  If your push remote is not set to =rad=, use =P e= and specify =rad= as the
294  remote.
295  
296  ** Clean Up (Step 9)
297  
298  - =b k= (=magit-branch-delete=) — select branches to delete
299  
300  ** Radicle Commands from Emacs
301  
302  For =rad= commands that have no Magit equivalent, use:
303  
304  - =M-!= (=shell-command=) for one-off commands like =rad issue state --closed 3085700=
305  - =M-x eshell= for an interactive session when you need to run several =rad=
306    commands in sequence
307  
308  ** Quick Reference
309  
310  | Step           | Magit                          | Shell fallback                   |
311  |----------------+--------------------------------+----------------------------------|
312  | Create branch  | =b c=                          |                                  |
313  | Stage + commit | =s= / =S= then =c c=           |                                  |
314  | Push patch     | =P e= (rad, HEAD:refs/patches) | =git push rad HEAD:refs/patches= |
315  | Review diff    | =d d=                          | =rad patch diff <id>=            |
316  | Checkout patch |                                | =rad patch checkout <id>=        |
317  | Merge          | =m m=                          |                                  |
318  | Push main      | =P p= or =P e=                 |                                  |
319  | Close issue    |                                | =rad issue state --closed <id>=  |
320  | Delete branch  | =b k=                          |                                  |
321  
322  * Summary
323  
324  | Step | Command                           | What it does                    |
325  |------+-----------------------------------+---------------------------------|
326  |    1 | =rad issue open=                  | Create a trackable unit of work |
327  |    2 | =git checkout -b <branch>=        | Start a working branch          |
328  |    3 | =git commit=                      | Record your changes             |
329  |    4 | =git push rad HEAD:refs/patches=  | Open a patch for review         |
330  |    5 | =rad patch show / diff=           | Review the code                 |
331  |    6 | =rad patch checkout=              | Test locally                    |
332  |    7 | =git merge= + =git push rad main= | Merge and announce              |
333  |    8 | =rad issue state <id> --closed=   | Close the issue                 |
334  |    9 | =git branch -d=                   | Clean up local branches         |
335  
336  * What to Read Next
337  
338  - [[file:../explanation/explanation_realtime_data_patterns.org][Real-Time Data Patterns]] — context for the data transport work tracked by
339    these issues
340  - [[file:../howto/howto_connect_opensky.org][Connect to OpenSky Network]] — the next piece of work after project setup