<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Cli on ln --help</title>
    <link>https://blog.mei-home.net/tags/cli/</link>
    <description>Recent content in Cli on ln --help</description>
    <generator>Hugo -- 0.147.2</generator>
    <language>en</language>
    <lastBuildDate>Sun, 16 Nov 2025 18:24:05 +0100</lastBuildDate>
    <atom:link href="https://blog.mei-home.net/tags/cli/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Task Management With Taskwarrior</title>
      <link>https://blog.mei-home.net/posts/taskwarrior-3/</link>
      <pubDate>Sun, 16 Nov 2025 18:24:05 +0100</pubDate>
      <guid>https://blog.mei-home.net/posts/taskwarrior-3/</guid>
      <description>Pretending to keep my life on track, 1500 tasks at a time</description>
      <content:encoded><![CDATA[<p>It was September 2014, and I needed to procrastinate a University exam. Looking
for something I could mentally categorize under &ldquo;useful&rdquo;, I happened upon
<a href="https://taskwarrior.org/">Taskwarrior</a>, and it has been running my life ever
since.</p>
<p>In this post, I will describe what Taskwarrior is and how I&rsquo;m using it, as well
as my migration from Taskwarrior 2.6 and <a href="https://github.com/GothenburgBitFactory/taskserver">Taskd</a>
to the new Taskwarrior 3.4 and <a href="https://github.com/GothenburgBitFactory/taskchampion-sync-server">taskchampion-sync-server</a>.</p>
<p>In short, Taskwarrior is a command line task management system.</p>
<p>Before I go into more detail, allow me to go on a short tangent: Taskwarrior
contains my first open source contribution, in the form of a bug report.
Many years
back in 2016, I found that TW was getting slower and slower while I was setting
up a complex dependency graph for a larger project. Looking into the code, I
found that the circular dependency check did not include a mechanism to make
sure that a task wasn&rsquo;t looked at multiple times. So it could happen that the
same task and all of its dependencies were checked multiple times while checking
whether a single task could be added with a given set of dependencies without
creating a circular graph.</p>
<p>I reported the issue and did some more elaborate explanation, leading to <a href="https://github.com/GothenburgBitFactory/taskwarrior/commit/10018dac18eaee12ab83ffc3a9107b0d9c50b54d">this commit</a>
to the project. I&rsquo;m even still in the <a href="https://github.com/GothenburgBitFactory/taskwarrior/blob/7cba255b3a6769b183498ed428bb1737d05b49f7/AUTHORS#L321">AUTHORS file</a>. &#x1f642;
The original task even still exists <a href="https://github.com/GothenburgBitFactory/taskwarrior/issues/1796">here</a>.
The frustrating thing right now: I cannot remember whether I also provided the
code to fix the implementation or if I just provided the bug report? Argh.</p>
<p>But enough personal Michael lore. To introduce you to what Taskwarrior looks like,
accompany me for a little story.</p>
<h2 id="a-story">A story</h2>
<p>So let&rsquo;s imagine you&rsquo;ve just run the Friday evening host update on a Wednesday for a change and suddenly,
your task management stops working. You were vaguely aware that there was a new
major release, but you decided to ignore it - mucking around with your task
management is scary!</p>
<p>But now, the time has come. You roll back the change, but you know that it&rsquo;s
finally time to take the plunge and update Taskwarrior to the new 3.0 version.
As is so often the case, you start with making sure you don&rsquo;t forget:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ task add <span style="color:#e6db74">&#34;Migrate Taskwarrior to 3.0&#34;</span> project:admin.tools.tw-demo +admin +tools +current +taskwarrior
</span></span><span style="display:flex;"><span>Created task 1.
</span></span></code></pre></div><p>You then go and admire your handiwork:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task proj:admin.tools.tw-demo
</span></span></code></pre></div><p><figure>
    <img loading="lazy" src="first-task.png"
         alt="A screenshot of a terminal showing the output of the previous command. It shows a task table with the following columns: ID, Age, Project, Tag, Description and Urg. The ID is 1, the Age 54s, the Project is &#39;admin.tools.tw-demo&#39;, the Urg &#39;2&#39; and the description is &#39;Migrate Taskwarrior to 3.0&#39;. The task has the following tags: admin, current, tools"/> <figcaption>
            <p>Our first task.</p>
        </figcaption>
</figure>

(I will be using screenshots instead of pasting the content into a code block
because I find that Taskwarrior&rsquo;s CLI is actually quite nice to look at.)</p>
<p>And there we are, the project is basically half done already. &#x1f601;
To celebrate your accomplishment, you go and fetch another coffee. While you prepare
your artisinal brew garnered with herbs from a secret valley deep in the snow-crowned
heights of the Alps, you suddenly realize: You forgot to tag the task with
<code>taskwarrior</code> to make clear which tool it is about!</p>
<p>You remedy it with a quick flourish of confident keystrokes:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task <span style="color:#ae81ff">1</span> mod +taskwarrior
</span></span><span style="display:flex;"><span>Modifying task <span style="color:#ae81ff">1</span> <span style="color:#e6db74">&#39;Migrate Taskwarrior to 3.0&#39;</span>.
</span></span><span style="display:flex;"><span>Modified <span style="color:#ae81ff">1</span> task.
</span></span></code></pre></div><p>A bit rattled by that oversight, you decide to take a closer look at the task
to make sure you did not forget anything else:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task <span style="color:#ae81ff">1</span> info
</span></span></code></pre></div><figure>
    <img loading="lazy" src="first-task-info.png"
         alt="Another terminal screenshot, this time showing detailed info about the task. The base info is still the same as in the previous screenshot, so I will concentrate on the added elements. First, there is now a &#39;status&#39; given as &#39;Pending&#39;. There are also two dates, one for when the task was created and one for when it was last modified. There are also a number virtual tags shown: LATEST, PENDING, PROJECT, READY, TAGGED, UNBLOCKED. In addition to showing the urgency as &#39;2&#39; again, there is also a short table showing how this value was computed. 1 urgency was coming from the fact that the task has a project, and the other 1 urgency from the fact that it has at least one tag set. Then at the bottom of the output is a list of changes done to the task, first showing the initial creation and then the deletion and re-adding of the &#39;taskwarrior&#39; tag. I did a little oopsie there."/> <figcaption>
            <p>More detailed info on the first task.</p>
        </figcaption>
</figure>

<p>Your sense of accomplishment not quite satisfied yet, you decide to create
some more tasks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task add <span style="color:#e6db74">&#34;Migrate data on desktop&#34;</span> proj:admin.tools.tw-demo +taskwarrior +current +tools +admin +desktop
</span></span><span style="display:flex;"><span>task add <span style="color:#e6db74">&#34;Migrate data on laptop&#34;</span> proj:admin.tools.tw-demo +taskwarrior +current +tools +admin +laptop
</span></span><span style="display:flex;"><span>task add <span style="color:#e6db74">&#34;Deploy TaskChampion on k8s cluster&#34;</span> proj:homelab.services.tw +taskwarrior +current +homelab +services
</span></span><span style="display:flex;"><span>task add <span style="color:#e6db74">&#34;Do first TW sync from desktop&#34;</span> proj:admin.tools.tw-demo +taskwarrior +current +admin +tools +desktop
</span></span><span style="display:flex;"><span>task add <span style="color:#e6db74">&#34;Document TaskChampion setup&#34;</span> proj:homelab.docs +taskwarrior +current +homelab +docs
</span></span></code></pre></div><p>Slowly stroking the white cat impatiently waiting for you to return to the
&ldquo;Conquer a small country that can&rsquo;t possibly defend itself&rdquo; part of the evening,
you take stock of your heroic labor:</p>
<figure>
    <img loading="lazy" src="initial-task-list.png"
         alt="I will spare you the preamble from here-on out: All of the pictures in the rest of this post will be terminal screenshots. This one shows a task list with the same columns as the previous ones, showing all of the tasks created up to now. Important here is the order: &#39;Migrate Taskwarrior to 3.0&#39;, &#39;Migrate data on desktop&#39;, &#39;Migrate data on laptop&#39;, &#39;Deploy TaskChampion on k8s cluster&#39;, &#39;Do first TW sync from desktop&#39;, &#39;Document TaskChampion setup&#39;. All of the tasks show an urgency of &#39;2&#39;."/> <figcaption>
            <p>Our current task list</p>
        </figcaption>
</figure>

<p>You stare at the task list. Something is off. Something feels wrong. Like when
the Homelab next to your desk suddenly sounds different because one of your
Fediverse posts went Fedi-viral and all of the fans ramp up to cool down the queues.
There it is: The documentation task really should not be at the bottom of the
pile. Documentation is <em>important</em>!</p>
<p>You launch your favorite editor, Emacs, of course, and point it at <code>~/.taskrc</code>.
You enter the following, to make sure that documentation tasks are always taken
seriously:</p>
<pre tabindex="0"><code>urgency.user.tag.docs.coefficient=+2
</code></pre><p>Now that&rsquo;s looking better:
<figure>
    <img loading="lazy" src="increased-docs-prio.png"
         alt="Again the same task list, but with a slightly changed order: The &#39;Document TaskChampion setup&#39; task is now first in the list, instead of last. Its urgency has changed to &#39;4&#39;, while all other tasks are still at the previous &#39;2&#39;."/> <figcaption>
            <p>Documentation is now the top priority.</p>
        </figcaption>
</figure>
</p>
<p>But there&rsquo;s still some visual distinguishing missing, so you head back to your
terminal to have a look at TW&rsquo;s colors:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task colors
</span></span></code></pre></div><figure>
    <img loading="lazy" src="tw-color-chart.png"
         alt="A screenshot of Taskwarrior&#39;s color chart. It shows the effects available for coloring TW&#39;s output, ranging from effects like underlining or bolding over changing the background color to a 215 color grid for the font and background colors. At the bottom is also a gray-scale."/> <figcaption>
            <p>Taskwarrior&rsquo;s color documentation.</p>
        </figcaption>
</figure>

<p>With a nice dark pink chosen, you open up the <code>.taskrc</code> file again and add the
following line:</p>
<pre tabindex="0"><code>color.tag.docs=rgb205
</code></pre><figure>
    <img loading="lazy" src="docs-task-color.png"
         alt="The same task list as before, but now the &#39;Document TaskChampion&#39; setup task is colored in a dark pink."/> <figcaption>
            <p>Task lists, now with more color!</p>
        </figcaption>
</figure>

<p>So far, so good, you think to yourself. But you&rsquo;re a technical lead in your
secret other life. And you know the drill: A project is not properly setup before
everything is put into a, preferably acyclic, dependency graph.</p>
<pre tabindex="0"><code>task 6 mod dep:4
task 3 mod dep:2,4,5
task 5 mod dep:4,2
task 1 mod dep:2,3,4,5
</code></pre><p>This ends up changing your task list to look like this:
<figure>
    <img loading="lazy" src="tasks-with-deps.png"
         alt="The same task list as before, but now with some coloring and ordering changes. The two tasks at the top are now &#39;Migrate data to desktop&#39; and &#39;Deploy TaskChampion on k8s cluster&#39;, both with an urgency of 10. They also have a white background now, indicating that they&#39;re blocking tasks. There is also a new column in the list now, showing the IDs of the tasks the given task depends on. The next two tasks are &#39;Migrate data on laptop&#39; and &#39;Do first TW sync from desktop&#39;, both with an urgency of 5. They also changed appearance, gaining a gray background. Next comes the documentation task, still in pink, but also with a gray background and an urgency of -1. The last task is the original &#39;Migrate Taskwarrior to 3.0&#39; task, with an urgency of -3."/> <figcaption>
            <p>Tasks with dependencies set.</p>
        </figcaption>
</figure>
</p>
<p>With that, only the two tasks which can actually be started are at the top, now
clearly marked with a white background. All of the tasks which have unfinished
dependency got a gray background.</p>
<p>Alright, that looks pretty acceptable. But there has to be more procrastination
material in here. Taking another look, you decide that you don&rsquo;t really need
the individual tags of the tasks shown in your &rsquo;next&rsquo; report.
You use the <code>task show</code> command to grab the default value for the <code>next</code> report&rsquo;s
columns:</p>
<pre tabindex="0"><code>task show report.next

report.next.columns     id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.relative,until.remaining,description,urgency
report.next.context     1
report.next.description Most urgent tasks
report.next.filter      status:pending -WAITING limit:page
report.next.labels      ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg
report.next.sort        urgency-
</code></pre><p>Then you go back in to add the following lines to <code>.taskrc</code>:</p>
<pre tabindex="0"><code>report.next.columns=id,start.age,entry.age,depends,priority,project,recur,scheduled.countdown,due.relative,until.remaining,description,urgency
report.next.labels=ID,Active,Age,Deps,P,Project,Recur,S,Due,Until,Description,Urg
</code></pre><p>This makes for a neater format of the task list, emphasizing the task description:
<figure>
    <img loading="lazy" src="tags-removed.png"
         alt="Yet again the same task list, but now the &#39;Tag&#39; column is removed, making the overall format a bit neater."/> <figcaption>
            <p>The task list after the tags column has been removed.</p>
        </figcaption>
</figure>
</p>
<p>Better, but there&rsquo;s still some clutter in the form of the tasks which you cannot
work on right now, namely the ones currently blocked. That can be remedied by
using a different Taskwarrior report:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task ready
</span></span></code></pre></div><p>This only shows the tasks which can currently be acted upon:
<figure>
    <img loading="lazy" src="tasks-ready.png"
         alt="Again a task list, but this time only the previously top two tasks are shown, the ones without dependencies: &#39;Migrate data on desktop&#39; and &#39;Deploy TaskChampion&#39; on k8s cluster."/> <figcaption>
            <p>Only showing actionable tasks.</p>
        </figcaption>
</figure>
</p>
<p>But even this little detour has not quieted the siren song of procrastination.
Casting about for more ways to put the actual execution of the project off,
your well-trained brain offers up a rationalization: It&rsquo;s the middle of the week!
You cannot possibly put your fingers into your task management system in the
middle of a work week. A small misstep would wreak havoc upon your routine.</p>
<p>And so you decide to put the project off until Sunday. For good measure, you also
put Sunday as the due date, just to make sure your brain doesn&rsquo;t try to weasel
out again:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task 2,4 mod wait:sunday due:sunday
</span></span></code></pre></div><p>And with that, the <code>task ready</code> report is blissfully empty. Definitely high time
for an eight year Lagavulin after this much highly productive work.</p>
<p>Come Sunday, the tasks appear in the list again, and you start with the first
one:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task <span style="color:#ae81ff">2</span> start
</span></span></code></pre></div><figure>
    <img loading="lazy" src="started-task.png"
         alt="The same list with only the &#39;Migrate data on desktop&#39; and &#39;Deploy TaskChampion on k8s cluster&#39; tasks. But this time, the &#39;Migrate data on desktop&#39; task has an orange instead of a white background, and its urgency shot up to 23.2."/> <figcaption>
            <p>Starting the first task.</p>
        </figcaption>
</figure>

<p>Once you finished the task and migrated the desktop TW install to 3.0, you set
the task to done:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task <span style="color:#ae81ff">2</span> <span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>Now, <code>task ready</code> only shows the k8s TaskChampion setup as still actionable. So
you get into your best robes, have The Acolyte bring in the big &ldquo;How to Deploy a New Service in the k8s Cluster Without Bringing About The End of Creation&rdquo; tome and begin to summon forth reams of YAML from the Void. You also sacrifice
a CPU to the kubectl, an ancient Pentium 1 no less - you really need this
project to go well.</p>
<p>It all goes well. The Acolyte breaths a sigh of relieve. He is weak. You knew
you needed to replace him when he started babbling about how you hadn&rsquo;t updated
Kubernetes in almost two years. He just can&rsquo;t seem to enjoy the sweet nectar
of pressure, worry, anxiety and bliss that is the price to be paid for practicing
the dark arts of YOLO.</p>
<p>After setting that task to done as well, the previously blocked documentation
and first sync tasks show up in <code>task ready</code> now:
<figure>
    <img loading="lazy" src="unblocked-tasks.png"
         alt="A task list containing the &#39;Do first TW sync from desktop&#39; and &#39;Document TaskChampion setup&#39; tasks."/> <figcaption>
            <p>New tasks became available.</p>
        </figcaption>
</figure>
</p>
<p>You decide that The Acolyte needs a bit of practice in forming full sentences
and send him off to write the documentation on the new service deployment.</p>
<p>You yourself type a quick <code>task sync</code>, and lo and behold, it works. The tasks
are flowing off to the servers. Your task migration was successful. You finish
the remaining tasks in a haze of keystrokes, dark black coffees and pipe smoke.</p>
<p>Even The Acolyte finished his task, and he even used punctuation almost everywhere!
You decide not to get rid of him quite so soon. Perhaps corrupting him into
loving the eldritch joys of YOLO might be a fun pastime?</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task ready
</span></span><span style="display:flex;"><span>No matches.
</span></span></code></pre></div><h2 id="writers-note">Writer&rsquo;s note</h2>
<p>Okay, that was pretty amusing for myself. &#x1f601;
But for the deeper technical explanation I will return to my normal writing
style. But I will definitely experiment a bit with getting The Acolyte into my
posts. I have to figure out how to do that without obscuring the technical
info with too much prose first, though. The above tour through Taskwarrior&rsquo;s
usage lend itself nicely to putting a bit of creative writing around it.
Definitely nicer than my first dry &ldquo;Type this, then type this, and if you now type this that happens&rdquo;
draft.</p>
<h2 id="urgency-in-taskwarrior">Urgency in Taskwarrior</h2>
<p>A task&rsquo;s urgency is arguably Taskwarrior&rsquo;s core feature. It determines the
ordering of tasks in a number of reports, and I&rsquo;d like to talk a bit about it.</p>
<p>There are a lot of sources of both urgency increases and decreases in Taskwarrior,
and they&rsquo;re almost entirely configurable to fit your approach to task management.</p>
<p>Let&rsquo;s have a look at one of my tasks, for the regular Homelab host updates. The
<code>task info</code> output contains a detailed table describing how the task&rsquo;s urgency
is computed:</p>
<figure>
    <img loading="lazy" src="urgency-table.png"
         alt="An example urgency table. It shows how the urgency of the task is computed. In this case, the task has an overall urgency of 10.43. The task gets 1 urgency from having a project, -3 from currently being in waiting state, 1 from having at least one tag, 2.4 from having a due date, 0.033 from its age and finally 9 from having the &#39;current&#39; tag."/> <figcaption>
            <p>Example urgency table</p>
        </figcaption>
</figure>

<p>The simplest values are coming from whether the task has certain attributes,
getting 1 urgency from having at least one tag and another 1 from having a
project set.</p>
<p>Then there&rsquo;s the age of the task. This urgency value slowly converges towards
2 while the task&rsquo;s age converges towards a configurable number of days. The
default is 365 days, so when the task is 365 days old, it will get 2 urgency
from age. This value won&rsquo;t increase any more after that.</p>
<p>Urgency can also be reduced, for example when the task is currently waiting
or when it is blocked. This makes sure that even when not filtering for
actionable tasks, as I did in the examples above, the non-actionable tasks will
appear behind actionable tasks in the list.</p>
<p>In addition to these mechanisms, you can also configure specific tags or certain
projects granting an amount of urgency, to prioritize them. I will go into a bit
more detail on my personal setup in the next section.</p>
<p>There is also an urgency change based on the due date, and how close that date
is. Similar to the age-related urgency, a task&rsquo;s urgency increases the closer it
gets to its due date.</p>
<p>And finally, there&rsquo;s also a helper for dependency hierarchies. By default, tasks
which block other tasks get an urgency boost, but its a fixed value. But there&rsquo;s
also an option to increase the urgency of a blocking task by an amount of urgency
depending on the urgency of the tasks it blocks.</p>
<h2 id="how-im-using-taskwarrior">How I&rsquo;m using Taskwarrior</h2>
<p>As TW is a highly configurable tool, I think it&rsquo;s useful to show you how I&rsquo;m
working with it.</p>
<p>To start with, here is my <code>task stats</code> output:
<figure>
    <img loading="lazy" src="task-stats.png"
         alt="A table with statistics information. It shows: 1404 pending tasks, 83 waiting, 60 recurring, 20096 completed, 5124 deleted, 26767 total. There are 31935 annotation, 489 unique tags, 554 projects, 584 blocked tasks, 482 blocking tasks, 5 undo transactions, 66 sync backlog transactions. 99.7% of tasks are tagged. The oldest task is from 2014-09-29-01:15 and the newest task from today. Task was used for 11.1 years, with a task added on average every 3h and completed every 4h. A task is deleted on average every 19 hours, with the average time spend pending being 5 months and an average description length of 36 characters."/> <figcaption>
            <p>My task stats over the last 11 years.</p>
        </figcaption>
</figure>
</p>
<p>I think the average time spend pending for a task, 5 months, shows a bit about
my usage. I usually don&rsquo;t just add tasks for the next week or so, but I tend to
do a planning phase for larger projects and then add pretty specific tasks for
everything that needs to be done. And then I&rsquo;m slowly working my way through
them.</p>
<p>For task creation, I&rsquo;ve got two modes. This is as good a place as any to mention
that while Taskwarrior is a great CLI tool, there was never any Android app I
liked. I tend to have relatively deep project structures and quite a few tags
on my tasks. And I haven&rsquo;t seen any good Taskwarrior app which makes entering
tasks even remotely as comfortable and fast as entering tasks on the terminal
with autocomplete for tags and projects. So when I need to add some task on the
go, I just jot it down in my notes app and enter it into TW the next time I&rsquo;m in front
of a terminal.</p>
<p>But back to new tasks. Most of the time, I&rsquo;m entering them &ldquo;complete&rdquo; already,
setting the right project and tags when creating them. Sometimes, when I just
want to jot something down, I use the <code>capture</code> tag. It&rsquo;s configured like
this:</p>
<pre tabindex="0"><code>urgency.user.tag.capture.coefficient=8.0
color.tag.capture=rgb405
</code></pre><p>This puts it pretty high on the task list and colors it in pink, which I don&rsquo;t
use for any other coloring. This way, I will see the task the next time I&rsquo;m
running <code>task</code> and can then take care of setting the project and tags and
dependencies properly.</p>
<p>I have several systems for prioritizing tasks.
The most important one consists of the three tags <code>current</code>, <code>following</code>, <code>soon</code>. They
are configured like this:</p>
<pre tabindex="0"><code>color.tag.soon=rgb150
color.tag.current=rgb035
color.tag.following=rgb043
urgency.user.tag.current.coefficient=+9
urgency.user.tag.following.coefficient=+6
urgency.user.tag.soon.coefficient=+3
</code></pre><p>These three tags, with <code>current</code> having blue font color, <code>following</code> being dark
green and <code>soon</code> being bright green, do a general categorization of tasks. <code>current</code>
is what I&rsquo;m currently working on, the current project for example, or a blog
post I want to start. In <code>task next</code> output, there are only ever at most three
or four of those, with the rest of the current project&rsquo;s tasks being dependent
on those actionable tasks and not seen in the <code>task next</code> output because they
get their urgency reduced by a lot.
<code>following</code> is then the category of tasks I&rsquo;d like to start next. These might
be new Homelab projects for example. And <code>soon</code> is then the next-lower prio,
which I give to task I don&rsquo;t want to do as the next project, but I also don&rsquo;t
want to completely vanish into the morass of tasks which have none of the three
tags.</p>
<p>For categorization of tasks within one of those rough levels I&rsquo;m using priorities,
which I&rsquo;m configuring like this:</p>
<pre tabindex="0"><code>urgency.uda.priority.H.coefficient=1.5
urgency.uda.priority.M.coefficient=0.0
urgency.uda.priority.L.coefficient=-0.5
</code></pre><p>Note how the three different urgency levels are each 3 apart from each other,
while the priorities here add 1.5 urgency at maximum. This allows me
to prioritize within an urgency level. E.g. even a <code>soon</code> tagged task with
<code>priority:H</code> won&rsquo;t get more urgency than a <code>following</code> tagged task with <code>pri:L</code>.</p>
<p>Another little wrinkle I&rsquo;ve added to my workflow is the <code>short</code> tag. It&rsquo;s configured
like this:</p>
<pre tabindex="0"><code>urgency.user.tag.short.coefficient=1.4
</code></pre><p>Its intention is to highlight short tasks, which in my head is anything up to
about 30 minutes. These are for when I&rsquo;ve only got 30 minutes left before I
need to do something else. Or for days where I want to feel really productive
and want to close a whole bunch of tasks. &#x1f601;</p>
<p>When I&rsquo;m starting to work on a task, I use the <code>task start</code> command, and <code>task stop</code>
when I stop working on it. This increases the urgency of the task so that it
shows up at the very top of the <code>task next</code> list and puts the task&rsquo;s row in
the output onto an orange background. I use the mechanism to indicate which of
the current tasks I&rsquo;m actively working on.
The feature also has a time journalling functionality, as each time <code>task start</code>
or <code>task stop</code> are called on a task, an annotation for the start/stop time is
added to the task. These annotations could theoretically be used to compute the
time spend on the task if you diligently run start/stop each time you start
or stop working on it. I&rsquo;m not using the feature, as I don&rsquo;t start/stop a task
only because I&rsquo;m making dinner, for example. I&rsquo;m using <a href="https://timewarrior.net/">timewarrior</a>
for time tracking.</p>
<p>As I&rsquo;ve mentioned in the introduction, I&rsquo;m using Taskwarrior for personal and
work task, on the same database. To separate the two, I&rsquo;m using Taskwarrior&rsquo;s
context feature. It&rsquo;s configured like this:</p>
<pre tabindex="0"><code>context.work.read=project:work
context.work.write=
context.private.read=project.not:work or +home
context.private.write=
context=private
</code></pre><p>The <code>context=private</code> setting configures the context when running with this
<code>.taskrc</code> file to <code>private</code>. As you can see in the <code>context.private.read</code> filter,
this excludes the <code>work</code> project from all read operations. Meaning that when
I call <code>task next</code> on my private desktop or laptop, I will never get a work
task shown, and it&rsquo;s the other way around at work, where I&rsquo;ve set <code>context=work</code>
and don&rsquo;t get any private tasks shown.</p>
<p>For completeness&rsquo; sake, here is my full <code>.taskrc</code>, with some annotations:</p>
<pre tabindex="0"><code>data.location=~/.task

include ~/.task/dark-256.theme
# Mostly the default value, but I&#39;ve switched it around a bit so that `due.today`
# appears before `overdue`. Otherwise, the coloring rules will mark tasks as
# overdue once their due-time is passed, which I generally don&#39;t want, because
# shorthands like due:tomorrow set the due time to &#34;00:00&#34; for the next day,
# so tasks would immediately show as overdue, instead of &#34;due today&#34;.
rule.precedence.color=deleted,completed,active,keyword.,project.,due.today,overdue,scheduled,due,blocked,tag.,blocking,recurring,tagged,uda

# Enabling task&#39;s &#34;hooks&#34;, although I&#39;m not currently using any
hooks=1
# Configuring tab completion to complete all tags
complete.all.tags=1
list.all.projects=1
# Disabling purging, so my older completed/deleted tasks are kept for historical
# purposes.
purge.on-sync=0

# Some date formatting configs
dateformat=Y.M.D-H:N
dateformat.holiday=Y.M.D
dateformat.report=Y.M.D
dateformat.annotation=Y.M.D-H:N
weekstart=Monday

# Re-jigging the columns and column order in the &#34;next&#34; report a bit to fit
# my personal taste.
report.next.columns=id,project,priority,due,start.active,entry.age,urgency,description.count
report.next.labels=ID,Proj,Pri,Due,A,Age,Urg,Description

# Increasing urgency of &#34;started&#34; tasks
urgency.active.coefficient=6.0
# Setting urgency for annotations to 0, because whether a task has annotations
# or not doesn&#39;t matter.
urgency.annotations.coefficient=0.0
# Sharply reducing blocked task&#39;s urgency, so they go to the bottom of task lists
urgency.blocked.coefficient=-11.0
# Only very slightly increasing urgency of blocking tasks. I use dependencies a
# lot, and I&#39;ve found that whether a task is blocking or not doesn&#39;t always matter
# to me, so they only get a small boost.
urgency.blocking.coefficient=0.5
# Prio configuration
urgency.uda.priority.H.coefficient=1.5
urgency.uda.priority.M.coefficient=0.0
urgency.uda.priority.L.coefficient=-0.5
urgency.user.tag.backlog.coefficient=-10.0
# Increasing bug&#39;s urgency a little bit, so errors get fixed before I start
# something new.
urgency.user.tag.bug.coefficient=2.0
urgency.user.tag.capture.coefficient=8.0
# Tag of FOSS contributions, which also get a small boost in prio
urgency.user.tag.contrib.coefficient=0.5
urgency.user.tag.current.coefficient=+9
# I also note down TV shows, movies and games I&#39;d like to watch/play, but they
# should not show up on the &#34;normal&#34; task list. So they get a special tag with
# a reduced urgency.
urgency.user.tag.entertain.coefficient=-9.0
urgency.user.tag.following.coefficient=+6
# Domestic chores. I should really bite the bullet and get a housekeeper. If
# only that didn&#39;t feel so incredibly decadent.
urgency.user.tag.haushalt.coefficient=0.5
# Bills get a high urgency too, so they don&#39;t get lost in the sea of other tasks
urgency.user.tag.rechnung.coefficient=10.0
urgency.user.tag.short.coefficient=1.4
urgency.user.tag.soon.coefficient=+3
urgency.waiting.coefficient=-3.0

uda.priority.default=M

context.work.read=project:work
context.work.write=
context.private.read=project.not:work or +home
context.private.write=
context=private
</code></pre><p>One last comment I would like to make: Taskwarrior is very much a task
management software, not a project planning tool. I use it together with Emacs'
org mode. It does task management really well, but doesn&rsquo;t really have note
taking capabilities. There are annotations which can be added like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task <span style="color:#ae81ff">123</span> annot <span style="color:#e6db74">&#34;Some note&#34;</span>
</span></span></code></pre></div><p>But these are really only intended for short notes. I only use them if I want
to store relevant links to websites for example.</p>
<h2 id="importexport">Import/Export</h2>
<p>One last important feature to note is Taskwarrior&rsquo;s import/export functionality.
Your tasks are not walled off in Taskwarrior. I will talk a bit more about its
storage formats when I talk about the migration to 3.0, but it has a nice
feature for importing and exporting tasks in JSON format.</p>
<p>For example, let&rsquo;s look at the task for this blog post:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task <span style="color:#ae81ff">1016</span> export
</span></span></code></pre></div><p>It results in this JSON:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#ae81ff">1016</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;description&#34;</span>:<span style="color:#e6db74">&#34;Blog: Taskwarrior&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;entry&#34;</span>:<span style="color:#e6db74">&#34;20230519T165524Z&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;modified&#34;</span>:<span style="color:#e6db74">&#34;20251110T191504Z&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;priority&#34;</span>:<span style="color:#e6db74">&#34;M&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;project&#34;</span>:<span style="color:#e6db74">&#34;blog&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;start&#34;</span>:<span style="color:#e6db74">&#34;20251110T191504Z&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;status&#34;</span>:<span style="color:#e6db74">&#34;pending&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;uuid&#34;</span>:<span style="color:#e6db74">&#34;c3bd5a56-684a-48e8-b54e-fa0267f34247&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;annotations&#34;</span>:[
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;entry&#34;</span>:<span style="color:#e6db74">&#34;20251110T191504Z&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;description&#34;</span>:<span style="color:#e6db74">&#34;Started task&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>],
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;tags&#34;</span>:[<span style="color:#e6db74">&#34;blog&#34;</span>,<span style="color:#e6db74">&#34;current&#34;</span>,<span style="color:#e6db74">&#34;tools&#34;</span>],
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;depends&#34;</span>:[<span style="color:#e6db74">&#34;4511952a-a314-4cae-9b92-9ef15b14a188&#34;</span>,<span style="color:#e6db74">&#34;f771863d-a079-4c33-aeb3-71790ee7272d&#34;</span>],
</span></span><span style="display:flex;"><span><span style="color:#f92672">&#34;urgency&#34;</span>:<span style="color:#ae81ff">19</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>A JSON in similar format can be used to import tasks as well. So switching to
Taskwarrior or switching away from Taskwarrior is doable, with a bit of scripting
to translate the JSON to whatever format the previous/new tool supports.</p>
<h2 id="the-migration-to-taskwarrior-30">The migration to Taskwarrior 3.0</h2>
<p>In March 2024, <a href="https://github.com/GothenburgBitFactory/taskwarrior/releases/tag/v3.0.0">Taskwarrior 3.0 was released</a>.
This happened after a pretty long phase of the project being dormant. That
dormancy wasn&rsquo;t actually a bad thing. It continued working perfectly fine through
all of those years, with perhaps one release per year on average. It&rsquo;s a command
line tool, and it only ever had three dependencies: libc, libgnutls and
libuuid.</p>
<p>The syncing was really the only issue, as it was a bit cumbersome, having been
based on TLS client certificates, and the sync server implementation has had
its last release in 2022, and no really regular releases before that. And that
was potentially a publicly accessible server.</p>
<p>The main change in the 3.0 release was a change of the data storage backend
and sync features. Both of those were rewritten in Rust.</p>
<p>The data storage backend was also changed from plain text files to an SQLite
database. This is, at least from my PoV, a slight step back. I liked the fact
that the data was just stored in text files. I was able to make use of that
fact a while ago when I needed to fix an issue in the data files due to my home
partition filling up and a task update only being written back partially.
At least from my PoV, this wasn&rsquo;t a performance problem. The previous file based
implementation written in C++ was perfectly workable even with a task database
of my size.
The performance definitely got worse with the SQLite DB, especially at work,
where our home directories are stored on NFS mounts.</p>
<p>The rewrite of the sync backend was a pure win, though. Where before it was raw
TCP, it now became HTTP, meaning I was able to put the new sync server behind
Traefik, and I didn&rsquo;t have any issues anymore with reaching the instance in
my Homelab from work, which I had done with a complicated SSH tunnel contraption
because I couldn&rsquo;t set up VPN access to my Homelab from the work machine.</p>
<p>The migration itself was also done very well. Not only is there a tool to
migrate the TW v2 file-based storage to the v3 SQLite DB, but they also left the
frontend entirely untouched, so my day-to-day usage was not impacted at all.</p>
<h3 id="migrating-desktop-data">Migrating desktop data</h3>
<p>My migration was from the 2.6.2 version all the way up to <a href="https://github.com/GothenburgBitFactory/taskwarrior/releases/tag/v3.4.1">v3.4.1</a>.
This isn&rsquo;t the latest release, there&rsquo;s already <a href="https://github.com/GothenburgBitFactory/taskwarrior/releases/tag/v3.4.2">v3.4.2</a>,
but that hasn&rsquo;t made its way into the Gentoo packages yet.</p>
<p>My first step was seeing whether 3.4.1 even build in my work environment, as it
wouldn&rsquo;t have been useful to just migrate my personal use. And it did, due to
its relatively few dependencies. Or well, its relatively few dependencies on the
C++ side. Rust pulled in over a page of dependencies, of course.</p>
<p>With that verified, I backed up my local data and ran the migration script:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>task import-v2
</span></span></code></pre></div><p>That went through without a hitch. Then I cautiously typed in <code>task</code> to show the
default <code>next</code> report. And then I waited. While it wasn&rsquo;t really slow, there was
a measurable delay between me hitting enter and the report appearing.
I then tried a few other things, and while any read operations, like showing
reports, still were fast, any write operations were slow as molasses.
A simple <code>task start 1536</code> took about a minute:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>time task start <span style="color:#ae81ff">1536</span>
</span></span><span style="display:flex;"><span>Starting task <span style="color:#ae81ff">1536</span> <span style="color:#e6db74">&#39;Taskwarrior: Update on Desktop and migrate data&#39;</span>.
</span></span><span style="display:flex;"><span>Started <span style="color:#ae81ff">1</span> task.
</span></span><span style="display:flex;"><span>You have more urgent tasks.
</span></span><span style="display:flex;"><span>Project <span style="color:#e6db74">&#39;admin.tools&#39;</span> is 51% complete <span style="color:#f92672">(</span><span style="color:#ae81ff">13</span> of <span style="color:#ae81ff">27</span> tasks remaining<span style="color:#f92672">)</span>.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>real    0m54,508s
</span></span><span style="display:flex;"><span>user    0m54,460s
</span></span><span style="display:flex;"><span>sys     0m0,027s
</span></span></code></pre></div><p>That was not at all the perf I was used to. That command was instantaneous with
the older versions. After some digging, I luckily came upon <a href="https://github.com/GothenburgBitFactory/taskwarrior/issues/3879">this ticket</a>,
which seemed to describe a similar perf issue, resolved by disabling the project
progress report at the end of the output. I tried that by adding the following
to my <code>.taskrc</code>:</p>
<pre tabindex="0"><code># Removed &#34;project&#34; because the computation was exceedingly expensive
# Added context and removed filter to restore 2.6 behavior
verbose=blank,header,footnote,label,new-id,news,affected,edit,special,sync,override,recur,context
</code></pre><p>Notably, I removed the <code>project</code> entry from the <code>verbose</code> list. After that, the
performance returned to what I was used to. In the same step, I also removed the
<code>filter</code> value from the <code>verbose</code> list, as now every command was showing the
filter I had applied, which isn&rsquo;t something I needed.</p>
<p>I also added this setting:</p>
<pre tabindex="0"><code>purge.on-sync=0
</code></pre><p>This is a setting to prevent automated purging of completed and deleted tasks.
This is supposed to keep performance up by cleaning older tasks which shouldn&rsquo;t
be needed anymore. But to me, my older deleted and completed tasks are a slice
of personal history I would like to keep. I&rsquo;m for example working on a history
series about my Homelab, from before I started this blog. And because that was
also before I had any configs version controlled, my task list is the only record
I have from before approximately 2020 when it comes to the Homelab.</p>
<p>With that done, I looked at the new sync setup.</p>
<h3 id="migrating-sync">Migrating sync</h3>
<p>As I&rsquo;ve said above, the sync backend was also changed. There are now multiple
backend available, Google Cloud, AWS S3 and the <a href="https://github.com/GothenburgBitFactory/taskchampion-sync-server">taskchampion-sync-server</a>.
All options are described in the <a href="https://taskwarrior.org/docs/sync/">TW docs</a>
and the <a href="https://taskwarrior.org/docs/man/task-sync.5/">sync man page</a>.</p>
<p>I initially thought to use S3, but the implementation seems to be geared towards
using AWS S3, as there&rsquo;s for example no config option for enabling path-style
buckets or setting an endpoint URL. In addition, I figure if there are interesting
new sync-related features, they will likely be implemented in the taskchampion-sync-server,
so I went with that.</p>
<p>The server supports both, SQLite and Postgres. I decided to go with the SQLite
option, because having a full Postgres cluster felt a bit like overkill.</p>
<p>The documentation for the sync server can be found <a href="https://gothenburgbitfactory.org/taskchampion-sync-server/">here</a>.
It&rsquo;s not a very complicated piece of software to set up, so I will spare you
most of the YAML manifests.</p>
<p>But here is at least the Deployment:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">tc-sync</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">replicas</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">securityContext</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runAsNonRoot</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">homelab/app</span>: <span style="color:#ae81ff">tc-sync</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">strategy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">type</span>: <span style="color:#e6db74">&#34;Recreate&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">template</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">homelab/app</span>: <span style="color:#ae81ff">tc-sync</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">automountServiceAccountToken</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">securityContext</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">fsGroup</span>: <span style="color:#ae81ff">1000</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">containers</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">tc-sync</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">image</span>: <span style="color:#ae81ff">ghcr.io/gothenburgbitfactory/taskchampion-sync-server:{{ .Values.appVersion }}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">volumeMounts</span>:
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">tc-data</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">mountPath</span>: {{ <span style="color:#ae81ff">.Values.mountDir }}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">200m</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">limits</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">200Mi</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">DATA_DIR</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;{{ .Values.mountDir }}&#34;</span>
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">LISTEN</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;0.0.0.0:{{ .Values.port }}&#34;</span>
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">CLIENT_ID</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;f5ea2f03-2f6b-4fc8-bae9-38fd06c08c65&#34;</span>
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">CREATE_CLIENTS</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">RUST_LOG</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;info&#34;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">livenessProbe</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">httpGet</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">port</span>: {{ <span style="color:#ae81ff">.Values.port }}</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">path</span>: <span style="color:#e6db74">&#34;/&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">initialDelaySeconds</span>: <span style="color:#ae81ff">15</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">tc-sync-http</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">containerPort</span>: {{ <span style="color:#ae81ff">.Values.port }}</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">protocol</span>: <span style="color:#ae81ff">TCP</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">tc-data</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">persistentVolumeClaim</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">claimName</span>: <span style="color:#ae81ff">tc-sync-volume</span>
</span></span></code></pre></div><p>One noteworthy piece of information is the <code>CLIENT_ID</code> list together with
the <code>CREATE_CLIENTS</code> environment variable. Clients are identified by their
UUID, created with the <code>uuidgen</code> tool. &ldquo;Client&rdquo; is a synonym for data set here,
so all clients using the same client ID will work on the same set of tasks.</p>
<p>The <code>CREATE_CLIENTS</code> variable controls whether new client IDs which have not
been seen before lead to new clients being created or are rejected as unknown.
If it is set to <code>false</code>, new clients need to be added to the database manually,
there is currently no tooling around that. But what&rsquo;s possible is to keep the
variable set to true, and then setting <code>CLIENT_ID</code> to a comma-separated list of
allowed IDs. Then, only clients sending those IDs are automatically created when
first seen, and all other IDs are rejected.</p>
<p>Once the server was running, I only needed to add an encryption secret and the
server URL to my <code>.taskrc</code> file. The encryption secret is used for encrypting the
tasks before they get synced, so the server only ever sees encrypted tasks, which
is a very nice touch.
An example of the <code>.taskrc</code> settings would look like this:</p>
<pre tabindex="0"><code>sync.server.url=https://tw.example.com/
sync.server.client_id=f5ea2f03-2f6b-4fc8-bae9-38fd06c08c65
sync.encryption_secret=12345
</code></pre><p>The <code>sync.server.client_id</code> needs to be in the <code>CLIENT_ID</code> list of the server for
this setup to work. Otherwise the server will return a 403 HTTP code.</p>
<p>With this setup above, I started syncing and everything worked pretty nicely,
I was able to sync to both my laptop and my work machine without issue.</p>
<p>But then I made a small mistake: I committed the above lines into my dotfiles
repository, including the <code>sync.encryption_secret</code> value. This was not good, of
course, as that value should be secret. I just deleted the sync server&rsquo;s DB and
created a new UUID for good measure and tried syncing again. But nothing at all
happened, besides the client throwing this error message:</p>
<pre tabindex="0"><code>Failed to synchronize with server: Server Error: https://tw.example.com/v1/client/add-version/789 responded with 500 Internal Server Error
</code></pre><p>I then found that there was no way to reset the syncing on the client side. So
I ended up going into the local SQLite DB and running this SQL command:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">UPDATE</span> operations
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> synced <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span></code></pre></div><p>I tried this just because I saw that the <code>operations</code> table had a <code>synced</code> column
set to <code>1</code> for all entries.
And that solved the issue, and the local client started a completely fresh sync.</p>
<p>This was all honestly a bit scary, because I rely a lot on Taskwarrior to organize
my life, but in the end the team really did a great job with the rewrite in 3.0,
and besides the performance issues I haven&rsquo;t hit any other issues in the one week
I&rsquo;ve now been using the new version.</p>
<p>I hope you enjoyed this post and have perhaps even found a new task management
tool to try.</p>
<p>Just remember: Applying really fancy tagging and coloring schemes to your
task management isn&rsquo;t actually bringing your project forward. &#x1f609;</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
