It’s been two years since the last post, so …, what held me back? Primarily, a
wonderful daughter found her way into my life, grounding me and giving a lot
of purpose to life. Who would have thought, right? But that did not mean I have
been slacking. wastebin has been chugging
along, binge my binary installer and
weave my final zk web frontend have seen the
light of day, I finished the meater readout
project, which was a great help for the first half year after birth of my
daughter, etc. I just never came around writing about these things which would
only interest five people in the world anyway.
Now with agentic software development finding its way into people’s lives, that
interest will approach zero. Software has become a commodity and frankly
speaking: it’s alright. I love the puzzle aspects of programming and the good
feeling of coming up with a clean and easy to understand architecture. But I
also enjoy that LLM agents free me of the boring things around software
development and computer usage in general: writing CI pipelines, dealing with
frontend peculiarities, understanding nginx issues, digging into Neovim Lua
plugin details. At the end it makes no difference if I copy paste a
StackOverflow snippet into my config or an agent. Long story short: I’m fine
with AI. More people than ever can make great software with limited time and
money spent. Yes, the fear is real that we are inundated with crap but at the
end of the day, it will also raise the bar for mediocrity. There will always be
a place for really great software.
Because to this day agent coding is still a tool with enough sharp edges, a
human still has to stay in the loop. But as any human can tell: talking to
a bot in a chat interface is not staying in the loop, it’s a disgrace. Reviewing
either means understanding and approving every little or huge chunk of generated
code (remember King-Size Homer and his drinking
bird?) which just does not scale.
Or reviewing means going full auto-mode and checking the diff on disk. However,
that quickly becomes annoying because telling Claude what to change where is a
chore and will still miss important details. Another popular method is opening
(draft) PRs on GitHub or your-forge-of-choice but it feels schizophrenic and
wastes tokens on unnecessary round trips. And it depends on GitHub not having one
of its outages again.
With that said: margin tries to address the
annoying review by simply annotating lines of diffs and handing those over to an
agent “in the box”. Here is a screenshot to give you a rough idea:
The interface is as simple as it gets: you go through the diff of changes, and
either on a line or after selecting multiple lines with v, hit a or Enter
to enter required changes. Behind the scenes, margin manages an annotation log
in the hidden .margin directory that the agent can read and modify itself.
Once done either hit C to hand over all annotations to a headless Claude
session or call the /margin-review skill to address them in your open session.
The skill will open the annotations, find what to change where and resolve them.
The top toolbar can be reached with Shift-Tab and allows to switch between the
commit list, the file list and all annotations. Of course, there are the usual
goodies you find in a “modern” diff-based TUI tool like showing more/less context
with +/-, split view, syntax highlighting, intra-line highlighting, shelling
out to $EDITOR etc.
However, I see your eyes rolling because the tool is mostly generated code but
that’s where we are today. I could have written that thing myself but it would
have taken orders of magnitude more time that I rather spend with my daughter.
Nevertheless, I took the time to type all these words. See you in two years!
This is a personal note on how to inspect async streams in order to hash the
contents without storing and reading the stream once more.
This might become the
start of a a series of little code snippets that might be interesting to others
as well.
Code
As an example fetch a URL and compute the SHA256 hash on it:
useanyhow::anyhow;usefutures::StreamExt;usesha2::Digest;usestd::path::PathBuf;#[tokio::main(flavor ="current_thread")]async fnmain()->Result<(), anyhow::Error>{let arg =std::env::args().skip(1).next().ok_or(anyhow!("no URL given"))?;let url =url::Url::parse(&arg)?;let path =PathBuf::try_from(url.path())?;let filename = path
.file_name().ok_or(anyhow!("URL does not contain a filename"))?;letmut hasher =sha2::Sha256::new();let stream =reqwest::get(url).await?.bytes_stream().inspect(|bytes|{ifletOk(bytes)= bytes { hasher.update(bytes);}}).map(|chunk|chunk.map_err(|err|std::io::Error::new(std::io::ErrorKind::Other, err)));letmut reader =tokio_util::io::StreamReader::new(stream);letmut file =tokio::io::BufWriter::new(tokio::fs::File::create(filename).await?);tokio::io::copy(&mut reader,&mut file).await?;let sum = hasher.finalize();println!("{}{:?}",hex::encode(&sum), filename);Ok(())}
OpenCL has been with me for more than a decade, back when we decided to use it
in our research project to make it the foundation for accelerating synchrotron
imaging. Now, as history has shown, OpenCL never really took off, partially
because Apple (the initial sponsor) dropped it but more importantly NVIDIA being
very successful in locking in people with their proprietary CUDA solution.
Nevertheless, support by all major GPU vendors is there to some degree, so
software can be accelerated in a somewhat portable way. The degree AMD has
taken is somewhat questionable though: they do support OpenCL either via their
open ROCm stack but just for select GPUs and short support windows or via their
proprietary amdgpu-pro packages. The latter is what I use today to enable OpenCL
in Darktable but it is a hack because it involves downloading Debian packages
from their website and extracting them correctly.
Fast forward to 2022, Rust is on its way to become the premier systems language
and heroes like Karol Herbst start writing OpenCL mesa
drivers completely alleviating the need for
the crap AMD is offering (well almost). Because building and using it is not
very straightforward at the moment, here are some hints how to do that. I am
assuming an older Ubuntu 20.04 box, so some things could be in the 22.04 repos
already.
Installing tools and dependencies
Add the LLVM apt repos
deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main
deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main
to /etc/apt/sources.list and run apt update. Install
For some reason, rusticl won’t build with the LLVM 15 libraries as is and we
have to add clangSupport to src/gallium/targets/opencl/meson.build
as yet another clang library to link against in order to find some RISCV
symbols. It’s time to configure the build with meson
Note that meson does not check for existence for Valgrind on Ubuntu and enables
it by default causing build errors when the development libraries are not
installed. Time to build and install using ninja
$ ninja build && ninja install
Running OpenCL programs
I tend to install mesa into a custom prefix and pre-load it with my old shell
script. In
order to have the system-wide ICD loader find the ICD that points to rusticl, we
have to set the OPENCL_VENDOR_PATH environment variable to the directory
containing the .icd, i.e. <some-prefix>/etc/OpenCL/vendors. Also we have to
set the RUSTICL_ENABLE environment variable to radeonsi because it is not
enabled by default yet. With that set clinfo should show a platform with the
name rusticl.
Setting up rust-analyzer
If you intend to dig into rusticl itself you will notice that this is not your
bog standard Cargo project but intertwined with meson which takes care of
building the majority of the C and C++ sources. Because of this rust-analyzer is
not able to figure out the structure of the rusticl project. Luckily, meson 0.64
produces a rust-project.json file that describes the structure but
unfortunately the paths in there seem to be a bit messed up. After symlinking
from the root of the Git repo (so rust-analyzer can find it) and changing the
paths to point to existing directories, rust-analyzer was able to make sense of
the project.
It has been almost a month already since I released the first major breaking
release of my minimalist pastebin. The main
reason to bump the major version was due to streamlining routes especially
dropping the /api ones and adding query parameters where it made sense. In
between my last post and version two, there have been many other non-breaking
changes like correct caching (of course …), more keybinds, better looking user
interface, minor fixes and a demo site hosted here.
Currently, I am preparing everything to make the move to the upcoming breaking
0.6 release of axum. But more importantly, I am investigating ideas how to get
rid of syntect, the syntax highlighting
library. My main issue with that library is that themes have to be in Sublime
Text theme format which leaves a lot of nice light/dark themes on the table. My
current approach is a tree-sitter based
library that bundles a bunch of
tree-sitter grammars and uses helix
themes to highlight the parsed names. While it works alright, distributing it as
a crate is a pain in the ass because only a fraction keeps publishing updated
grammars on crates.io. So, next idea is perhaps bundling it via Git submodules.
Let’s see.
Pastebins are the next step in the
evolution of a software developer, right after finishing hello worlds and static
site generators. They are limited terms of features (or not … ahem) but require
some form of dynamisms in order to receive and store user input and make it
available upon request. Of course, everyone has different ideas what a pastebin
should do and in what language it should be written. And because I am in no way
different, I had to write my own: the
wastebin pastebin that ticks the following
boxes:
Written in Rust for ease of deployment.
SQLite instead of a full-fledged database server or flat files.
Paste expiration.
Minimalist appearance.
Syntax highlighting.
Line numbers.
bin – from which wastebin takes huge
inspiration in terms of UI – was almost there but the lack of expiration and
flat-file storage was a no-go. Moreover, I sincerely think
axum has a more solid foundation than
Rocket. Enough reasons to do it myself.