05 Dec 2025
Planet Grep
Lionel Dricot: Is Pixelfed sawing off the branch that the Fediverse is sitting on?

Is Pixelfed sawing off the branch that the Fediverse is sitting on?
In January 2025, I became aware that there was a real problem with Pixelfed, the "Instagram inspired Fediverse client". The problem is threatening the whole Fediverse. As Pixelfed received a lot of media attention, I choose to wait. In March 2025, I decided that the situation was quieter and wrote an email to Dansup, Pixelfed's maintainer, with an early draft of this post. Dan's promptly replied, in a friendly tone. But didn't want to acknowledge the problem which I've confirmed many times with Pixelfed users. I want to bring the debate on the public place. If I'm wrong, I will at least understand why. If Dan is wrong on this very specific issue, we will at least open the debate.
This post will be shared to my Fediverse audience through my @ploum@mamot.fr Mastodon account. But Pixelfed users will not see it. Even if they follow me, even if many people they follow boost it. Instead, they will see a picture of my broken keyboard that I posted a week ago.
The latest post of Ploum according to Pixelfed.
That's because, despite its name, Pixelfed is NOT a true Fediverse application. It does NOT respect the ActivityPub protocol. Any Pixelfed user following my @ploum@mamot.fr will only see a very small fraction of what I post. They may not see anything from me for months.
But why? Simple! The Pixelfed app has unilaterally decided not to display most Fediverse posts for the arbitrary reason that they do not contain a picture.
This is done on purpose and by design. Pixelfed is designed to mimic Instagram. Displaying text without pictures was deliberately removed from the code (it was possible in previous versions) in order to make the interface prettier.
This is unlike a previous problem where Pixelfed would allow unauthorised users to read private posts from unknowing fediverse users, which was promptly fixed.
In this case, we are dealing with a conscious design decision by the developers. Being pretty is more important than transmitting messages.
Technically, this means that a Pixelfed user P will think that he follows someone but will miss most of the content. On the opposite, the sender, for example a Mastodon user M, will believe that P has received his message because M follows him.
This is a grave abuse of the protocol: messages are silently dropped. It stands against everything the Fediverse is trying to do: allow users to communicate. My experience with open protocols allows me to say that it is a critical problem and that it cannot be tolerated. Would you settle for a mail provider which silently drop all emails you receive if they contain the letter "P"?
The principle behind a communication protocol is to create trust that messages are transmitted. Those messages could, of course, be filtered by the users but those filters should be manually triggered and always removable. If a message is not delivered, the sender should be notified.
In 2025, I've read several articles about people trying the Fediverse but leaving it because "there's not enough content despites following lot of people". Due to the Pixelfed buzz in January, I'm now wondering: "how many of those people were using Pixelfed and effectively missing most of the Fediverse content?"
The importance of respecting the protocol
I cannot stress enough how important that problem is.
If Pixelfed becomes a significant actor, its position will gravely undermine the ActivityPub protocol to the point of making it meaningless.
Imagine a new client, TextFed, that will never display posts with pictures. That makes as much sense as the opposite. Lots of people, like me, find pictures disturbing and some people cannot see pictures at all. So TextFed makes as much sense as Pixelfed. Once you have TextFed, you realise that TextFed and PixelFed users can follow each other, they can comment on post from Mastodon users, they can exchange private messages but they will never be able to see post from each other.
For any normal users, there's no real way to understand that they miss some messages. And even if you do, it is very hard to find that the cause is the absence of pictures in them make them "not pretty enough" to Pixelfed developers. Worse of all : some Mastodon posts do contain a picture but are not displayed in Pixelfed. That's because the picture is from a link preview and was not manually uploaded. Try to explain that to your friends that reluctantly followed you on the Fediverse. Have a look at any Mastodon account and try to guess which posts will we showed to the Pixelfed followers!
That's not something any normal human is supposed to understand. For Pixelfed users, there's no way to see they are missing on some content. For Mastodon users, there's no way to see that some of their audience is missing on some content.
With the trust in the protocol broken, people will revert to create Mastodon accounts to follow Mastodon, Pixelfed accounts to follow Pixelfed and Textfed to follow Textfed. Even if it is not 100% needed, that's the first intuition. It's already happening around me: I've witnessed multiple people with a Mastodon account creating a Pixelfed account to follow Pixelfed users. They do this naturally because they were used to do that with Twitter and Instagram.
Congratulations, you have successfully broken ActivityPub and, as a result, the whole Fediverse. What Meta was not able to do with Threads, the Fediverse did it to itself. Because it was prettier.
Pixelfed will be forced to comply anyway
Now, imagine for a moment that Pixelfed takes off (which is something I wish for and would be healthy for the Fediverse) and that interactions are strong between Mastodon users and Pixelfed users (also something I wish for). I let you imagine how many bug reports developers will receive about "some posts are not appearing in my followers timeline" or "not appearing in my timeline".
This will result in a heavy pressure for Pixelfed devs to implement text-only messages. They will, at some point, be forced to comply, having eroded trust in the Fediverse for nothing.
Once a major actor in a decentralised network starts to mess with the protocol, there are only two possible output: either that actor lose steam or that actor becomes dominant enough to impose its own vision of the protocol. In fact, there's a third option: the whole protocol becomes irrelevant because nobody trust it anymore.
What if Pixelfed becomes dominant?
But imagine that Pixelfed is now so important that they can stick to their guns and refuse to display text messages.
Well, there's a simple answer: every other fediverse software will now add an image with every post. Mastodon will probably gain a configurable "default picture to attach to every post so your posts are displayed in Pixelfed".
And now, without having formerly formalised it, the ActivityPub protocol requires every message to have a picture.
That's how protocol works. It already happened: that's how all mail clients started to implement the winmail.dat bug.
Sysadmins handling storage and bandwidth for the Fediverse thank you in advance.
We are not there yet
Fortunately, we are not there yet. Pixelfed is still brand new. It still can go back to displaying every message an end user expect to see when following another Fediverse user.
I stress out that it should be by default, not a hidden setting. Nearly all Pixelfed users I've asked were unaware of that problem. They thought that if they follow someone on the Fediverse, they should, by default, see all their public posts.
There's no negotiation. No warning on the Pixelfed website will be enough. In a federated communication system, filters should be opt-in. If fact, that's what older versions of Pixelfed were doing.
But, while text messages MUST be displayed by default (MUST as in RFC), they can still me displayed as less important. For example, one could imagine having them smaller or whatever you find pretty as long as it is clear that the message is there. I trust Pixelfed devs to be creative here.
The Fediverse is growing. The Fediverse is working well. The Fediverse is a tool that we urgently need in those trying times. Let's not saw off the branch on which we stand when we need it the most.
UPDATE: Dansup, Pixelfed Creator, replied the following on Mastodon:
We are working on several major updates, and while I believe that Pixelfed should only show photo posts, that decision should be up to each user, which we are working to support.
I'm Ploum, a writer and an engineer. I like to explore how technology impacts society. You can subscribe by email or by rss. I value privacy and never share your adress.
I write science-fiction novels in French. For Bikepunk, my new post-apocalyptic-cyclist book, my publisher is looking for contacts in other countries to distribute it in languages other than French. If you can help, contact me!
05 Dec 2025 7:40am GMT
Dries Buytaert: The freedom to leave is what makes customers stay

When I tell people that Acquia Source will let customers export their entire website and leave our platform anytime, I usually get puzzled looks.
We really mean the entire site: the underlying Drupal code, theme, configuration, content, and data. The export gives you a complete, working Drupal site that you can run on any infrastructure you choose.
Most SaaS platforms do the opposite. They make it hard to leave. When you export, you may get all your content, but never the code.
Why do we want to make it easy for customers to leave?
First, when leaving is easy, customers stay because they want to, not because they are trapped. That accountability pushes us to build better products. It means that at Acquia, we have to earn our customers' business every day by delivering value, not by making it hard to leave.
Second, the ability to leave means teams can start small and scale without hitting a wall. All SaaS products have constraints, and Acquia Source is no exception. When your application reaches a level of complexity that requires deeper customization, you can take your entire site to Acquia Cloud or any other Drupal hosting environment. You never need to start over.
Last but not least, because Acquia Source is built on Drupal, we want it to reflect Drupal's open source freedoms. Full export is how we make those principles real in a SaaS context.
We call this Open SaaS.
We first tried this idea with Drupal Gardens in 2010, which also allowed full exports. I loved that feature then, and I still love it now. I have always believed it was a big deal. More importantly, our customers did too.
One of Acquia's largest customers began on Drupal Gardens more than a decade ago. They used it to explore Drupal, then naturally grew into Acquia Cloud and Site Factory as their needs became more complex. Today they run some of the world's biggest media properties on Drupal and Acquia.
Trust comes from freedom, not lock-in. The exit door you'll never use is exactly what makes you confident enough to stay. It does seem counterintuitive to make leaving easy, but not all SaaS is created equal. With our Open SaaS approach, you get the freedom to grow and the ability to leave whenever you choose.
05 Dec 2025 7:40am GMT
Dries Buytaert: Drupal Canvas 1.0 released

When we launched Drupal CMS 1.0 eleven months ago, I posted the announcement on Reddit. Brave of me, I know. But I wanted non-Drupal people to actually try it.
There were a lot of positive reactions, but there was also honest feedback. The most common? "Wake me up when your new experience builder is ready". The message was clear: make page building easier and editing more visual.
I was not surprised. For years I have heard the same frustration. Drupal is powerful, but not always easy to use. That criticism has been fair. We have never lacked capability, but we have not always delivered the user experience people expect.
Well, wake up.
Today we released Drupal Canvas 1.0, a new visual page builder for Drupal. You can create reusable components that match your design system, drag them on to a page, edit content in place, preview changes across multiple pages, and undo mistakes with ease.
Watch the video below. Better yet, show it to someone who thinks they know what Drupal looks like. I bet their first reaction will be: "Wait, is that Drupal?". That reaction is exactly what we have been working toward. It makes Drupal feel more modern and less intimidating.
I also want to set expectations. Drupal Canvas 1.0 helps us catch up with other page builders more than it helps us leap ahead. We had to start there.
But it helps us catch up in the right way, bringing the ease of modern tools while keeping Drupal's identity intact. This isn't Drupal becoming simpler by becoming less powerful. Drupal Canvas sits on top of everything that makes Drupal so powerful: structured content, fine-grained permissions, scalability, and much more.
Most importantly, it opens new doors. Frontend developers can create components in React without having to learn Drupal first. And as shown in my DrupalCon Vienna keynote, Drupal Canvas will have an AI assistant that can generate pages from natural language prompts.
Drupal Canvas is a remarkable piece of engineering. The team at Acquia and contributors across the community put serious craft into this. You can see it in the result. I'm thankful for the time, care, and skill everyone brought to it.
So what is next? We keep building. Drupal Canvas 1.0 is step one, and this is a good moment for more of the Drupal community to get involved. Now is the time to build on it, test it, and improve it. Especially because Drupal CMS 2.0 ships in less than two months with Drupal Canvas included.
Shipping Drupal Canvas 1.0 is a major milestone. It shows we are listening. And it shows what we can accomplish when we focus on the experience as much as the capability. I cannot wait to see what people build with it.
05 Dec 2025 7:40am GMT
04 Dec 2025
Planet Debian
Colin Watson: Free software activity in November 2025

My Debian contributions this month were all sponsored by Freexian. I had a bit less time than usual, because Freexian collaborators gathered in Marseille this month for our yearly sprint, doing some planning for next year.
You can also support my work directly via Liberapay or GitHub Sponsors.
OpenSSH
I began preparing for the second stage of the GSS-API key exchange package split (some details have changed since that message). It seems that we'll need to wait until Ubuntu 26.04 LTS has been released, but that's close enough that it's worth making sure we're ready. This month I just did some packaging cleanups that would otherwise have been annoying to copy, such as removing support for direct upgrades from pre-bookworm. I'm considering some other package rearrangements to make the split easier to manage, but haven't made any decisions here yet.
This also led me to start on a long-overdue bug triage pass, mainly consisting of applying usertags to lots of our open bugs to sort them by which program they apply to, and also closing a few that have been fixed, since some bugs will eventually need to be reassigned to GSS-API packages and it would be helpful to make them easier to find. At the time of writing, about 30% of the bug list remains to be categorized this way.
Python packaging
I upgraded these packages to new upstream versions:
- aioftp
- basemap (fixing Cython 3.1 compatibility)
- billiard
- black
- django-phonenumber-field
- flask-security
- flufl.i18n
- langtable
- mariadb-connector-python
- peewee (fixing Cython 3.1 compatibility)
- pydantic
- pydantic-core
- pydantic-settings
- pylsqpack
- pymongo
- pymssql
- pyodbc
- python-auditwheel (contributed follow-up fix upstream)
- python-avro
- python-bleach
- python-btrees
- python-certifi
- python-charset-normalizer
- python-colorlog
- python-django-channels
- python-django-constance
- python-django-contact-form
- python-django-hashids
- python-djvulibre (fixing Cython 3.1 compatibility)
- python-evalidate
- python-legacy-cgi
- python-openstep-plist (fixing Cython 3.1 compatibility)
- python-pytest-run-parallel
- python-pytokens
- python-tblib
- python-webargs
- pyupgrade
- sqlfluff
- trove-classifiers
- ttconv
- wcwidth
- zope.hookable
- zope.i18nmessageid (removing run-time dependency on setuptools)
- zope.interface
- zope.proxy
- zope.security (removing run-time dependency on setuptools)
- zope.sqlalchemy
I packaged django-pgtransaction and backported it to trixie, since we plan to use it in Debusine; and I adopted python-certifi for the Python team.
I fixed or helped to fix several other build/test failures:
- kivy
- mypy
- pytest-mypy-testing (contributed upstream)
- python-cytoolz
- python-discord
- python-ncls, fixing some failures in pyranges
- pyxdg
- traitlets
- twine
I fixed a couple of other bugs:
- pmix: B-D on python3-all-dev, but not built for all supported Python3 versions
- python-certifi: Package not compatible with multiarch
Other bits and pieces
- cdebconf: Move to /usr/lib/apt/apt-extracttemplates
- devscripts: Restore dpkg-dev dependency
- groff: Missing information about some utilities in the "contrib" directory
Code reviews
- base-passwd: French translation (merged and uploaded)
- cdebconf: Consider using the standard dh sequence in d/rules (merged and uploaded)
- cdebconf: Multiple select is not right aligned in Hebrew (patch fails to build; asked reporter for an update)
- cdebconf: Allow building without libglib2.0-dev (merged and uploaded)
- pydantic-core (merged and uploaded)
- pymongo (sponsored adoption by Aryan Karamtoth)
- python-maturin: Add missing build dependencies (merged and uploaded)
04 Dec 2025 5:55pm GMT
Planet Lisp
Tim Bradshaw: Literals and constants in Common Lisp
Or, constantp is not enough.
Because I do a lot of things with Štar, and for other reasons, I spend a fair amount of time writing various compile-time optimizers for things which have the semantics of function calls. You can think of iterator optimizers in Štar as being a bit like compiler macros: the aim is to take a function call form and to turn it, in good cases, into something quicker1. One important way of doing this is to be able to detect things which are known at compile-time: constants and literals, for instance.
One of the things this has made clear to me is that, like John Peel, constantp is not enough. Here's an example.
(in-row-major-array a :simple t :element-type 'fixnum) is a function call whose values Štar can use to tell it how to iterate (via row-major-aref) over an array. When used in a for form, its optimizer would like to be able to expand into something involving (declare (type (simple-array fixnum *) ...), so that the details of the array are known to the compiler, which can then generate fast code for row-major-aref. This makes a great deal of difference to performance: array access to simple arrays of known element types is usually much faster than to general arrays.
In order to do this it needs to know two things:
- that the values of the
simpleandelement-typekeyword arguments are compile-time constants; - what their values are.
You might say, well, that's what constantp is for2. It's not: constantp tells you only the first of these, and you need both.
Consider this code, in a file to be compiled:
(defconstant et 'fixnum)
(defun ... ...
(for ((e (in-array a :element-type et)))
...)
...)
Now, constantpwill tell you that et is indeed a compile-time constant. But it won't tell you its value, and in particular nothing says it needs to be bound at compile-time at all: (symbol-value 'et) may well be an error at compile-time.
constantp is not enough! instead you need a function that tells you 'yes, this thing is a compile-time constant, and its value is …'. This is what literal does: it conservatively answers the question, and tells you the value if so. In particular, an expression like (literal '(quote fixnum)) will return fixnum, the value, and t to say yes, it is a compile-time constant. It can't do this for things defined with defconstant, and it may miss other cases, but when it says something is a compile-time constant, it is. In particular it works for actual literals (hence its name), and for forms whose macroexpansion is a literal.
That is enough in practice.
-
Śtar's iterator optimizers are not compiler macros, because the code they write is inserted in various places in the iteration construct, but they're doing a similar job: turning a construct involving many function calls into one requiring fewer or no function calls. ↩
-
And you may ask yourself, "How do I work this?" / And you may ask yourself, "Where is that large automobile?" / And you may tell yourself, "This is not my beautiful house" / And you may tell yourself, "This is not my beautiful wife" ↩
04 Dec 2025 4:23pm GMT
Planet Debian
Ben Hutchings: FOSS activity in November 2025

- Debian packages:
- debian-cd:
- firmware-nonfree:
- Bugs:
- closed #1106074: include potentially non-redistributable C\&M firmware
- replied to and closed #1112208: firmware-nvidia-graphics: Missing gsp symlink for nvidia ad107
- closed #1116997: firmware-amd-graphics: Please update to current VCN firmware on gitlab
- closed #1118195: firmware-misc-nonfree: no longer actually ships Arm Mali firmware, only link to non-existing file
- closed #1118199: firmware-mediatek: mt7921e stops working with latest version (20250917-1)
- Merge requests:
- Uploads:
- uploaded version 20251011-1 to unstable
- uploaded version 20251021-1 to unstable
- uploaded version 20251021-1~bpo13+1 to trixie-backports
- uploaded version 20251111-1 to unstable
- (LTS) Started work on backported packages that would be parallel-installable with the default firmware packages
- Bugs:
- initramfs-tools:
- Bugs:
- closed #894294: Comment in the source of unmkinitramfs is ambiguous
- replied to #944779: initramfs-tools-core: debug setting does not turn on tracing for scripts called by init
- replied to #945854: Please include additional virtio modules used for boot into initramfs for MODULES=most
- closed #1003427: COMPRESS=zstd and COMPRESS=lz4 hard-coded to bad COMPRESSLEVELs
- closed #1020718: should at least suggest the expected compressors
- replied to #1032610: reliably composable initramfs - zero-pad output to allow concatenation
- replied to #1042094: initramfs-tools: Please support systemd-cryptsetup unlocked root filesystem
- closed #1062968: initramfs-tools-core: Use zstdmt instead of zstd by default
- closed #1065698: update-initramfs: -k all stopped working
- closed #1084232: initramfs-tools: update-initramfs fails with "No space left on device" due to 2 temporary initrd copies in /boot
- Merge requests:
- Bugs:
- ktls-utils:
- Merge requests:
- merged !1: Update for version 1.3.0
- opened and merged !2: Move config to new upstream preferred name /etc/tlshd/config on upgrade
- Uploads:
- uploaded version 1.3.0-1 to unstable
- Merge requests:
- linux:
- Bugs:
- replied to #1000966: amdgpu: output to VR headset fails with "*ERROR* dc_stream_state is NULL for crtc '1'!"
- closed #1117256: linux-image-6.12.48+deb13-amd64: Kernel can no longer activate ethernet on Dell WD19S docking station
- closed #1118653: linux-image-6.17.2-amd64: Please restore CONFIG_NETFILTER_XT_TARGET_MASQUERADE
- replied to #1120277: linux: I'm filing this because I have a serious, persistent bug with my Intel AX210 Bluetooth audio on Debian. The problem isn't configuration; it's a kernel driver failure. My Bluetooth headset connects, but I get absolutely no sound.
- replied to #1120854: linux-image-arm64: Enable dma engine for Renesas RZ platform
- Merge requests:
- Uploads:
- uploaded version 6.12.57-1~bpo12+1 to bookworm-backports
- uploaded version 6.16.12-1~bpo13+1 to trixie-backports
- uploaded version 6.17.8-1~bpo13+1 to trixie-backports
- (LTS) Backported necessary CI changes to the bullseye-security branch
- Bugs:
- (LTS) linux-6.1:
- uploaded version 6.1.158-1~deb11u1 to bullseye-security
- linux-base:
- Debian non-package bugs:
- general:
- replied to #1120386: startx error on Trixie
- general:
- Mailing lists:
- debian-devel:
- replied to ORed build profiles
- replied to salsa-ci.yml did not have the desired effect
- debian-kernel:
- debian-lts-announce:
- stable:
- debian-devel:
04 Dec 2025 2:59pm GMT
03 Dec 2025
Planet Debian
Reproducible Builds: Reproducible Builds in November 2025
Welcome to the report for November 2025 from the Reproducible Builds project!
These monthly reports outline what we've been up to over the past month, highlighting items of news from elsewhere in the increasingly-important area of software supply-chain security. As always, if you are interested in contributing to the Reproducible Builds project, please see the Contribute page on our website.
In this report:
- "10 years of Reproducible Build" at SeaGL
- Distribution work
- Tool development
- Website updates
- Miscellaneous news
- Software Supply Chain Security of Web3
- Upstream patches
'10 years of Reproducible Builds' at SeaGL 2025
On Friday 8th November, Chris Lamb gave a talk called 10 years of Reproducible Builds at SeaGL in Seattle, WA.
Founded in 2013, SeaGL is a free, grassroots technical summit dedicated to spreading awareness and knowledge about free source software, hardware and culture. Chris' talk:
[…] introduces the concept of reproducible builds, its technical underpinnings and its potentially transformative impact on software security and transparency. It is aimed at developers, security professionals and policy-makers who are concerned with enhancing trust and accountability in our software. It also provides a history of the Reproducible Builds project, which is approximately ten years old. How are we getting on? What have we got left to do? Aren't all the builds reproducible now?
Distribution work
In Debian this month, Jochen Sprickerhof created a merge request to replace the use of reprotest in Debian's Salsa Continuous Integration (CI) pipeline with debrebuild. Joschen cites the advantages as being threefold: firstly, that "only one extra build needed"; it "uses the same sbuild and ccache tooling as the normal build"; and "works for any Debian release". The merge request was merged by Emmanuel Arias and is now active.
kpcyrd posted to our mailing list announcing the initial release of repro-threshold, which implements an APT transport that "defines a threshold of at least X of my N trusted rebuilders need to confirm they reproduced the binary" before installing Debian packages. "Configuration can be done through a config file, or through a curses-like user interface.
Holger then merged two commits by Jochen Sprickerhof in order to address a fakeroot-related reproducibility issue in the debian-installer, and Jörg Jaspert deployed a patch by Ivo De Decker for a bug originally filed by Holger in February 2025 related to some Debian packages not being archived on snapshot.debian.org.
Elsewhere, Roland Clobus performed some analysis on the "live" Debian trixie images, which he determined were not reproducible. However, in a follow-up post, Roland happily reports that the issues have been handled. In addition, 145 reviews of Debian packages were added, 12 were updated and 15 were removed this month adding to our knowledge about identified issues.
Lastly, Jochen Sprickerhof filed a bug announcing their intention to "binary NMU" a very large number of the R programming language after a reproducibility-related toolchain bug was fixed.
Bernhard M. Wiedemann posted another openSUSE monthly update for their work there.
Julien Malka and Arnout Engelen launched the new hash collection server for NixOS. Aside from improved reporting to help focus reproducible builds efforts within NixOS, it collects build hashes as individually-signed attestations from independent builders, laying the groundwork for further tooling.
Tool development
diffoscope version 307 was uploaded to Debian unstable (as well as version 309). These changes included further attempts to automatically attempt to deploy to PyPI by liaising with the PyPI developers/maintainers (with this experimental feature). […][…][…]
In addition, reprotest versions 0.7.31 and 0.7.32 were uploaded to Debian unstable by Holger Levsen, who also made the following changes:
- Do not vary the architecture personality if the kernel is not varied. (Thanks to Raúl Cumplido). […]
- Drop the
debian/watchfile, as Lintian now flags this as error for 'native' Debian packages. […][…] - Bump
Standards-Versionto 4.7.2, with no changes needed. […] - Drop the
Rules-Requires-Rootheader as it is no longer required.. […]
In addition, however, Vagrant Cascadian fixed a build failure by removing some extra whitespace from an older changelog entry. […]
Website updates
Once again, there were a number of improvements made to our website this month including:
-
Bernhard M. Wiedemann updated the
SOURCE_DATE_EPOCHpage to fix the Lisp example syntax. […] -
Holger Levsen updated a number of pages on our website related to our recent summit in Vienna […][…][…][…][…], and added a link to the YouTube video of his recent talk at Transparency.dev in Gothenburg, Sweden […].
-
James Addison replaced a broken link on the Reproducibility Troubleshooting page with one using Archive.org. […]
-
kpcyrd also updated the Vienna summit page in order to update group picture […] as well as to expand the project list […].
-
Robert Stupp added a new Helm page […][…], and fleshed out some Gradle specifics, etc. on the JVM page […].
Miscellaneous news
-
It was noticed that the Comparison of Linux distributions Wikipedia page now has a "Reproducible builds" column.
-
The popular Ruby on Rails web development framework had a reproducibility-related test failure due to daylight savings time changes.
-
Debian Developer Otto Kekäläinen appeared on the Open Source Security podcast, relating to their blog post about the XZ backdoor. The video, audio, as well as a full transcript of the show are available on the Open Source Security podcast page for this episode.
-
Thomas Weißschuh posted to our mailing list in order to look for feedback on their
CONFIG_MODULE_HASHESpatchset for the Linux kernel, "which aims to enable reproducible kernel packages for Linux distributions". -
kpcyrd also posted our list with a post entitled "Github Actions and the hashFiles incident".
-
Simon Mudd posted to the list as well "looking for reproducible RPM building / rebuilding tooling". Simon had watched a recent talk by Holger Levsen and was trying to ensure that he could rebuild various MySQL
.rpms. -
Lastly, there was a thread related to the hosting of the website powering this very report.
Software Supply Chain Security of Web3
Via our mailing list, Martin Monperrus let us know about their recently-published page on the Software Supply Chain Security of Web3. The abstract of their paper is as follows:
Web3 applications, built on blockchain technology, manage billions of dollars in digital assets through decentralized applications (dApps) and smart contracts. These systems rely on complex, software supply chains that introduce significant security vulnerabilities. This paper examines the software supply chain security challenges unique to the Web3 ecosystem, where traditional Web2 software supply chain problems intersect with the immutable and high-stakes nature of blockchain technology. We analyze the threat landscape and propose mitigation strategies to strengthen the security posture of Web3 systems.
Their paper lists reproducible builds as one of the mitigating strategies. A PDF of the full text is available to download.
Upstream patches
The Reproducible Builds project detects, dissects and attempts to fix as many currently-unreproducible packages as possible. We endeavour to send all of our patches upstream where appropriate. This month, we wrote a large number of such patches, including:
-
Bernhard M. Wiedemann:
SARndbox(race)clamav(rust toolchain)contrast/identity/loupe/mousai(need glib-macros update)cosmic(cosmic* HashMap)dealers-choice(nocheck)falcon(python-falcon date)FreeDoko(date)gnutls(FTBFS-CPU)gods-deluxe(jar mtimes)Kinect(date)libplasma6(qmlcachegen race)llvm(rocm-omp date)rnp(FTBFS-2041)rocsolver(FTBFS-j1)switcheroo(FTBFS-j1)vdrift(date)
-
Arnout Engelen:
ibus(parallelism)qmlcachegen(with Ulf Hermann)
-
Chris Lamb:
- #1120066 filed against
python-gffutils. - #1120068 filed against
python-biom-format. - #1120069 filed against
python-requests-cache. - #1120070 filed against
python-tld. - #1120121 filed against
smart-open. - #1120122 filed against
vanguards. - #1120123 filed against
pycifrw. - #1120124 filed against
golang-github-apptainer-container-library-client. - #1120330 filed against
python-ofxhome. - #1120331 filed against
python-lupa. - #1120332 filed against
mu-editor. - #1120340 filed against
python-spdx-tools. - #1120342 filed against
python-django-waffle. - #1120351 filed against
biosquid. - #1120352 filed against
dateparser. - #1120353 filed against
parsinsert. - #1120357 filed against
rdf2rml. - #1120405 filed against
python-et-xmlfile. - #1120528 filed against
deblur. - #1120529 filed against
ytcc. - #1120530 filed against
pgpainless. - #1120531 filed against
trillian. - #1120532 filed against
pywavelets. - #1120591 filed against
jsonpath-ng. - #1120592 filed against
presto. - #1120593 filed against
python-pyutil. - #1120629 filed against
python-os-apply-config. - #1120631 filed against
pydata-sphinx-theme. - #1120632 filed against
python-ciso8601. - #1120633 filed against
python-pymummer. - #1120634 filed against
qcat. - #1120870 filed against
tkgate. - #1120871 filed against
tkgate. - #1120872 filed against
ruby-gnuplot. - #1120873 filed against
python-nixio. - #1120874 filed against
python-altair. - #1120875 filed against
python-graphene. - #1120876 filed against
python-phabricator. - #1120877 filed against
python-slimmer. - #1120878 filed against
python-kafka. - #1120879 filed against
python-sshsig. - #1120880 filed against
python-babelgladeextractor. - #1120881 filed against
python-genson. - #1120882 filed against
flawfinder. - #1120883 filed against
crasm. - #1121064 filed against
insilicoseq. - #1121065 filed against
pychopper. - #1121066 filed against
pycparser. - #1121067 filed against
whipper. - #1121068 filed against
vt. - #1121069 filed against
pyxnat. - #1121070 filed against
golang-github-kshedden-statmodel. - #1121071 filed against
nim-hts. - #1121072 filed against
golang-github-emicklei-dot. - #1121073 filed against
golang-gonum-v1-plot. - #1121074 filed against
beangulp. - #1121075 filed against
virulencefinder. - #1121076 filed against
ansible-lint. - #1121077 filed against
entropybroker. - #1121078 filed against
namecheap. - #1121141 filed against
spopt. - #1121142 filed against
pyasn. - #1121143 filed against
python-pyvcf. - #1121147 filed against
python-pysaml2.
- #1120066 filed against
-
Jochen Sprickerhof:
-
Vagrant Cascadian:
Finally, if you are interested in contributing to the Reproducible Builds project, please visit our Contribute page on our website. However, you can get in touch with us via:
-
IRC:
#reproducible-buildsonirc.oftc.net. -
Mastodon: @reproducible_builds@fosstodon.org
-
Mailing list:
rb-general@lists.reproducible-builds.org
03 Dec 2025 8:28pm GMT
01 Dec 2025
Planet Lisp
Joe Marshall: Advent of Code 2025
The Advent of Code will begin in a couple of hours. I've prepared a Common Lisp project to hold the code. You can clone it from https://github.com/jrm-code-project/Advent2025.git It contains an .asd file for the system, a package.lisp file to define the package structure, 12 subdirectories for each day's challenge (only 12 problems in this year's calendar), and a file each for common macros and common functions.
As per the Advent of Code rules, I won't use AI tools to solve the puzzles or write the code. However, since AI is now part of my normal workflow these days, I may use it for enhanced web search or for autocompletion.
As per the Advent of Code rules, I won't include the puzzle text or the puzzle input data. You will need to get those from the Advent of Code website (https://adventofcode.com/2025).
01 Dec 2025 12:42am GMT
30 Nov 2025
Planet Lisp
vindarel: Practice for Advent Of Code in Common Lisp
Advent Of Code 2025 starts in a few hours. Time to practice your Lisp-fu to solve it with the greatest language of all times this year!
Most of the times, puzzles start with a string input we have to parse to a meaningful data structure, after which we can start working on the algorithm. For example, parse this:
(defparameter *input* "3 4
4 3
2 5
1 3
3 9
3 3")
into a list of list of integers, or this:
(defparameter *input* "....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...")
into a grid, a map. But how do you represent it, how to do it efficiently, what are the traps to avoid, are there some nice tricks to know? We'll try together.
You'll find those 3 exercises of increasing order also in the GitHub repository of my course (see my previous post on the new data structures chapter).
I give you fully-annotated puzzles and code layout. You'll have to carefully read the instructions, think about how you would solve it yourself, read my proposals, and fill-in the blanks -or do it all by yourself. Then, you'll have to check your solution with your own puzzle input you have to grab from AOC's website!
Table of Contents
- Prerequisites
- Exercise 1 - lists of lists
- Exercise 2 - prepare to parse a grid as a hash-table
- Harder puzzle - hash-tables, grid, coordinates
- Closing words
Prerequisites
You must know the basics, but not so much. And if you are an experienced Lisp developer, you can still find new constraints for this year: solve it with loop, without loop, with a purely-functional data structure library such as FSet, use Coalton, create animations, use the object system, etc.
If you are starting out, you must know at least:
- the basic data structures (lists and their limitations, arrays and vectors, hash-tables, sets...)
- iteration (iterating over a list, arrays and hash-table keys)
- functions
no need of macros, CLOS or thorough error handling (it's not about production-grade puzzles :p ).
Exercise 1 - lists of lists
This exercise comes from Advent Of Code 2024, day 01: https://adventofcode.com/2024/day/1
Read the puzzle there! Try with your own input data!
Here are the shortened instructions.
;;;
;;; ********************************************************************
;;; WARN: this exercise migth be hard if you don't know about functions.
;;; ********************************************************************
;;;
;;; you can come back to it later.
;;; But, you can have a look, explore and get something out of it.
In this exercise, we use:
;;; SORT
;;; ABS
;;; FIRST, SECOND
;;; EQUAL
;;; LOOP, MAPCAR, REDUCE to iterate and act on lists.
;;; REMOVE-IF
;;; PARSE-INTEGER
;;; UIOP (built-in) and a couple string-related functions
;;;
;;; and also:
;;; feature flags
;;; ERROR
;;;
;;; we don't rely on https://github.com/vindarel/cl-str/
;;; (nor on cl-ppcre https://common-lisp-libraries.readthedocs.io/cl-ppcre/)
;;; but it would make our life easier.
;;;
OK, so this is your puzzle input, a string representing two colums of integers.
(defparameter *input* "3 4
4 3
2 5
1 3
3 9
3 3")
We'll need to parse this string into two lists of integers.
If you want to do it yourself, take the time you need! If you're new to Lisp iteration and data structures, I give you a possible solution.
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;; [hiding in case you want to do it...]
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
(defun split-lines (s)
"Split the string S by newlines.
Return: a list of strings."
;; If you already quickloaded the STR library, see:
;; (str:lines s)
;;
;; UIOP comes with ASDF which comes with your implementation.
;; https://asdf.common-lisp.dev/uiop.html
;;
;; #\ is a built-in reader-macro to write a character by name.
(uiop:split-string s :separator '(#\Newline)))
Compile the function and try it on the REPL, or with a quick test expression below a "feature flag".
We get a result like '("3 4" "4 3" "2 5" "1 3" "3 9" "3 3"), that is a list of strings with numbers inside.
#+lets-try-it-out
;; This is a feature-flag that looks into this keyword in the top-level *features* list.
;; The expression below should be highlihgted in grey
;; because :lets-try-it-out doesn't exist in your *features* list.
;;
;; You can compile this with C-c C-c
;; Nothing should happen.
(assert (equal '("3 4" "4 3" "2 5" "1 3" "3 9" "3 3")
(split-lines *input*)))
;; ^^ you can put the cursor here and eval the expression with C-x C-e, or send it to the REPL with C-c C-j.
We now have to extract the integers inside each string.
To do this I'll use a utility function.
;; We could inline it.
;; But, measure before trying any speed improvement.
(defun blank-string-p (s)
"S is a blank string (no content)."
;; the -p is for "predicate" (returns nil or t (or a truthy value)), it's a convention.
;;
;; We already have str:blankp in STR,
;; and we wouldn't need this function if we used str:words.
(equal "" s)) ;; better: pair with string-trim.
#+(or)
(blank-string-p nil)
#++
(blank-string-p 42)
#+(or)
(blank-string-p "")
And another one, to split by spaces:
(defun split-words (s)
"Split the string S by spaces and only return non-blank results.
Example:
(split-words \"3 4\")
=> (\"3\" \"4\")
"
;; If you quickloaded the STR library, see:
;; (str:words s)
;; which actually uses cl-ppcre under the hood to split by the \\s+ regexp,
;; and ignore consecutive whitespaces like this.
;;
(let ((strings (uiop:split-string s :separator '(#\Space))))
(remove-if #'blank-string-p strings)))
#+lets-try-it-out
;; test this however you like.
(split-words "3 4")
I said we wouldn't use a third-party library for this first puzzle. But using cl-ppcre would be so much easier:
(ppcre:all-matches-as-strings "\\d+" "3 6")
;; => ("3" "6")
With our building blocks, this is how I would parse our input string into a list of list of integers.
We loop on input lines and use the built-in function parse-integer.
(defun parse-input (input)
"Parse the multi-line INPUT into a list of two lists of integers."
;; loop! I like loop.
;; We see everything about loop in the iteration chapter.
;;
;; Here, we see one way to iterate over lists:
;; loop for ... in ...
;;
;; Oh, you can rewrite it in a more functional style if you want.
(loop :for line :in (split-lines input)
:for words := (split-words line)
:collect (parse-integer (first words)) :into col1
:collect (parse-integer (second words)) :into col2
:finally (return (list col1 col2))))
#+lets-try-it-out
(parse-input *input*)
;; ((3 4 2 1 3 3) (4 3 5 3 9 3))
The puzzle continues.
"Maybe the lists are only off by a small amount! To find out, pair up the numbers and measure how far apart they are. Pair up the smallest number in the left list with the smallest number in the right list, then the second-smallest left number with the second-smallest right number, and so on."
=> we need to SORT the columns by ascending order.;;;
"Within each pair, figure out how far apart the two numbers are;"
=> we need to compute their relative, absolute distance.
"you'll need to add up all of those distances."
=> we need to sum each relative distance.
"For example, if you pair up a 3 from the left list with a 7 from the right list, the distance apart is 4; if you pair up a 9 with a 3, the distance apart is 6."
Our input data's sum of the distances is 11.
We must sort our lists of numbers. Here's a placeholder function:
(defun sort-columns (list-of-lists)
"Accept a list of two lists.
Sort each list in ascending order.
Return a list of two lists, each sorted."
;; no mystery, use the SORT function.
(error "not implemented"))
;; Use this to check your SORT-COLUMNS function.
;; You can write this in a proper test function if you want.
#+lets-try-it-out
(assert (equal (sort-columns (parse-input *input*))
'((1 2 3 3 3 4) (3 3 3 4 5 9))))
Compute the absolute distance.
;; utility function.
(defun distance (a b)
"The distance between a and b.
Doesn't matter if a < b or b < a."
;;
;; hint: (abs -1) is 1
;;
(error "not implemented")
)
(defun distances (list-of-lists)
"From a list of two lists, compute the absolute distance between each point.
Return a list of integers."
(error "not implemented")
;; hint:
;; (mapcar #'TODO (first list-of-lists) (second list-of-lists))
;;
;; mapcar is a functional-y way to iterate over lists.
)
(defun sum-distances (list-of-integers)
"Add the numbers in this list together."
(error "not implemented")
;; Hint:
;; try apply, funcall, mapcar, reduce.
;; (TODO #'+ list-of-integers)
;; or loop ... sum !
)
Verify.
(defun solve (&optional (input *input*))
;; let it flow:
(sum-distances (distances (sort-columns (parse-input input)))))
#+lets-try-it-out
(assert (equal 11 (solve)))
All good? There's more if you want.
;;;
;;; Next:
;;; - do it with your own input data!
;;; - do the same with the STR library and/or CL-PPCRE.
;;; - write a top-level instructions that calls our "main" function so that you can call this file as a script from the command line, with sbcl --load AOC-2024-day01.lisp
;;;
Exercise 2 - prepare to parse a grid as a hash-table
This exercise is a short and easy, to prepare you for a harder puzzle. This is not an AOC puzzle itself.
Follow the instructions. We are only warming up.
;; Do this with only CL built-ins,
;; or with the dict notation from Serapeum,
;; or with something else,
;; or all three one after the other.
We will build up a grid stored in a hash-table to represent a map like this:
"....#...##....#"
where the # character represents an obstacle.
In our case the grid is in 1D, it is often rather 2D.
This grid/map is the base of many AOC puzzles.
Take a second: shall we represent a 2D grid as a list of lists, or something else, (it depends on the input size) and how would you do in both cases?
Your turn:
;;
;; 1. Define a function MAKE-GRID that returns an empty grid (hash-table).
;;
(defun make-grid ()
;; todo
)
;;
;; Define a top-level parameter to represent a grid that defaults to an empty grid.
;;
;; def... *grid* ...
;;
;; 2. Create a function named CELL that returns a hash-table with those keys:
;; :char -> holds the character of the grid at this coordinate.
;; :visited or :visited-p or even :visited? -> stores a boolean,
;; to tell us if this cell was already visited (by a person walking in the map). It defaults
;; to NIL, we don't use this yet.
;;
(defun cell (char &key visited)
;; todo
)
;;
;; 3. Write a function to tell us if a cell is an obstacle,
;; denoted by the #\# character
;;
(defun is-block (cell)
"This cell is a block, an obstacle. Return: boolean."
;; todo
;; get the :char key,
;; check it equals the #\# char.
;; Accept a cell as NIL.
)
We built utility functions we'll likely re-use on a more complex puzzle.
Let's continue with parsing the input to represent a grid.
If you are a Lisp beginner or only saw the data structures chapter in my course, I give you the layout of the parse-input function with a loop and you only have to fill-in one blank.
In any case, try yourself. Refer to the Cookbook for loop examples.
;;
;; 4. Fill the grid (with devel data).
;;
;; Iterate on a given string (the puzzle input),
;; create the grid,
;; keep track of the X coordinate,
;; for each character in the input create a cell,
;; associate the coordinate to this cell in the grid.
;;
(defparameter *input* ".....#..#.##...#........##...")
(defun parse-grid (input)
"Parse a string of input, fill a new grid with a coordinate number -> a cell (hash-table).
Return: our new grid."
(loop :for char :across input
:with grid := (make-grid)
:for x :from 0
:for cell := (cell char)
:do
;; associate our grid at the X coordinate
;; with our new cell.
;; (setf ... )
:finally (return grid)))
;; try it:
#++
(parse-grid *input*)
That's only a simple example of the map mechanism that comes regurlarly in AOC.
Here's the 3rd exercise that uses all of this.
Harder puzzle - hash-tables, grid, coordinates
This exercise comes from Advent Of Code 2024, day 06. https://adventofcode.com/2024/day/6 It's an opportunity to use hash-tables.
Read the puzzle there! Try with your own input data!
Here are the shortened instructions.
The solutions are in another file, on my GitHub repository.
;;;
;;; ********************************************************************
;;; WARN: this exercise migth be hard if you don't know about functions.
;;; ********************************************************************
;;;
;;; you can come back to it later.
;;; But, you can have a look, explore and get something out of it.
In this exercise, we use:
;;;
;;; parameters
;;; functions
;;; recursivity
;;; &aux in a lambda list
;;; CASE
;;; return-from
;;; &key arguments
;;; complex numbers
;;; hash-tables
;;; the DICT notation (though optional)
;;; LOOPing on a list and on strings
;;; equality for characters
For this puzzle, we make our life easier and we' use the DICT notation.
(import 'serapeum:dict)
If you know how to create a package, go for it.
Please, quickload the STR library for this puzzle.
#++
(ql:quickload "str")
;; Otherwise, see this as another exercise to rewrite the functions we use.
This is your puzzle input:
;;; a string representing a grid, a map.
(defparameter *input* "....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...")
;; the # represents an obstacle,
;; the ^ represents a guard that walks to the top of the grid.
When the guard encounters an obstacle, it turns 90 degrees right, and keeps walking.
Our task is to count the number of distinct positions the guard will visit on the grid before eventually leaving the area.
We will have to: - parse the grid into a data structure - preferably, an efficient data structures to hold coordinates. Indeed, AOC real inputs are large. - for each cell, note if it's an obstacle, if that's where the guard is, if the cell was already visited, - count the number of visited cells.
;; We'll represent a cell "object" by a hash-table.
;; With Serapeum's dict:
(defun cell (char &key guard visited)
(dict :char char
:guard guard
:visited visited))
;; Our grid is a dict too.
;; We create a top-level variable, mainly for devel purposes.
(defvar *grid* (dict)
"A hash-table to represent our grid. Associates a coordinate (complex number which represents the X and Y axis in the same number) to a cell (another hash-table).")
;; You could use a DEFPARAMETER, like I did initially. But then, a C-c C-k (recompile current file) will erase its current value, and you might want or not want this.
For each coordinate, we associate a cell.
What is a coordinate? We use a trick we saw in other people's AOC solution, to use a complex number. Indeed, with its real and imaginary parts, it can represent both the X axis and the Y axis at the same time in the same number.
#|
;; Practice complex numbers:
(complex 1)
;; => 1
(complex 1 1)
;; => represented #C(1 1)
;; Get the imaginary part (let's say, the Y axis):
(imagpart #C(1 1))
;; the real part (X axis):
(realpart #C(1 1))
|#
Look, we are tempted to go full object-oriented and represent a "coordinate" object, a "cell" object and whatnot, but it's OK we can solve the puzzle with usual data structures.
;; Let's remember where our guard is.
(defvar *guard* nil
"The guard coordinate. Mainly for devel purposes (IIRC).")
Task 1: parse the grid string.
We must parse the string to a hash-table of coordinates -> cells.
I'll write the main loop for you. If you feel ready, take a go at it.
(defun parse-grid (input)
"Parse INPUT (string) to a hash-table of coordinates -> cells."
;; We start by iterating on each line.
(loop :for line :in (str:lines input)
;; start another variable that tracks our loop iteration.
;; It it incremented by 1 at each loop by default.
:for y :from 0 ;; up and down on the map, imagpart of our coordinate number.
;; The loop syntax with ... = ... creates a variable at the first iteration,
;; not at every iteration.
:with grid = (dict)
;; Now iterate on each line's character.
;; A string is an array of characters,
;; so we use ACROSS to iterate on it. We use IN to iterate on lists.
;;
;; The Iterate library has the generic :in-sequence clause if that's your thing (with a speed penalty).
:do (loop :for char :across line
:for x :from 0 ;; left to right on the map, realpart of our coordinate.
:for key := (complex x y)
;; Create a new cell at each character.
:for cell := (cell char)
;; Is this cell the guard at the start position?
:when (equal char #\^)
:do (progn
;; Here, use SETF on GETHASH
;; to set the :guard keyword of the cell to True.
(print "we saw the guard")
;; (setf (gethash ... ...) ...)
;; For devel purposes, we will also keep track of
;; where our guard is with a top-level parameter.
(setf *guard* key)
)
:do
;; Normal case:
;; use SETF on GETHAH
;; to associate this KEY to this CELL in our GRID.
(format t "todo: save the cell ~S in the grid" cell)
)
:finally (return grid))
)
;; devel: test and bind a top-level param for ease of debugging/instropection/poking around.
#++
(setf *grid* (parse-grid *input*))
Task 2: walk our guard, record visited cells.
We have to move our guard on the grid, until it exits it.
I'll give you a couple utility functions.
(defun is-block (cell)
"Is this cell an obstacle?"
;; accept a NIL, we'll stop the walk in the next iteration.
(when cell
(equal TODO #\#)))
;; We choose the write the 4 possible directions as :up :down :right :left.
;; See also:
;; exhaustiveness checking at compile-time:
;; https://dev.to/vindarel/compile-time-exhaustiveness-checking-in-common-lisp-with-serapeum-5c5i
(defun next-x (position direction)
"From a position (complex number) and a direction, compute the next X."
(case direction
(:up (realpart position))
(:down (realpart position))
(:right (1+ (realpart position)))
(:left (1- (realpart position)))))
(defun next-y (position direction)
"From a position (complex number) and a direction, compute the next Y."
(case direction
(:up (1- (imagpart position)))
(:down (1+ (imagpart position)))
(:right (imagpart position))
(:left (imagpart position))))
This is the "big" function that moves the guard, records were it went, makes it rotate if it is against a block, and iterates, until the guard goes out of the map.
Read the puzzle instructions carefuly and write the "TODO" placeholders.
(defun walk (&key (grid *grid*) (input *input*)
(position *guard*)
(cell (gethash *guard* *grid*)) ;; todo: *grid* is used here. Fix it so we don't use a top-level variable, but only the grid given as a key argument.
(direction :up)
(count 0)
;; &aux notation: it saves a nested of LET bindings.
;; It's old style.
;; Those are not arguments to the function we pass around,
;; they are bindings inside the function body.
&aux next-cell
next-position
obstacle-coming)
"Recursively move the guard and annotate cells of our grid,
count the number of visited cells."
;; At each iteration, we study a new cell we take on our grid.
;; If we move the guard to a coordinate that doesn't exist in our grid,
;; we stop here.
(unless cell
(return-from walk count))
;; Look in the same direction first and see what we have.
(setf next-position
(complex (next-x position direction) (next-y position direction)))
(setf next-cell (gethash next-position grid))
;; obstacle?
(setf obstacle-coming (is-block next-cell))
;; then change direction.
(when obstacle-coming
(setf direction
(case direction
(:up :right)
(:down :left)
(:right :down)
(:left :up))))
;; Count unique visited cells.
;; TODO
(unless (print "if this CELL is visited...")
(incf count)
;; TODO set this cell as visited.
(print "set this CELL to visited")
)
;; get our next position now.
(setf next-position
(complex (next-x position direction) (next-y position direction)))
;; This next cell may or may not be in our grid (NIL).
(setf next-cell (gethash next-position grid))
(walk :grid grid :input input
:cell next-cell
:position next-position
:direction direction
:count count))
and that's how we solve the puzzle:
(defun part-1 (input)
(walk :grid (parse-grid input)))
#++
(part-1 *input*)
;; 41
;; The right answer for this input.
;; In AOC, you have a bigger, custom puzzle input. This can lead to surprises.
Closing words
Look at other people's solutions too. For example, ak-coram's for our last exercise (using FSet). See how Screamer is used for day 06 by bo-tato (reddit). atgreen (ocicl, cl-tuition, cffi...) solution with a grid as a hash-table with complex numbers. lispm's day 04 solution. Can you read all solutions?
On other days, I used:
- alexandria's
map-permutationsfor day 08 when you want... permutations. It doesn't "cons" (what does that mean you ask? You didn't follow my course ;) ). Read here: https://dev.to/vindarel/advent-of-code-alexandrias-map-permutations-was-perfect-for-day-08-common-lisp-tip-16il. - the library fare-memoization, to help in a recursive solution.
- to write math, use cmu-infix. When you spot 2 equations with 2 unknows, think "Cramer system". This came up last year, so maybe not this year.
- with very large numbers: use double floats, as in
1.24d0 - least common multiple?
lcmis a built-in. - str:match can be a thing to parse strings.
- if you got CIEL (CIEL Is an Extended Lisp), you have Alexandria, cl-str, Serapeum:dict and more libraries baked-in. It's also an easy way to run Lisp scripts (with these dependencies) from the shell.
See you and happy lisping!
Your best resources:
30 Nov 2025 6:12pm GMT
15 Nov 2025
FOSDEM 2026
FOSDEM 2026 Accepted Stands
With great pleasure we can announce that the following project will have a stand at FOSDEM 2026! ASF Community BSD + FreeBSD Project Checkmk CiviCRM Cloud Native Computing Foundation + OpenInfra & the Linux Foundation: Building the Open Source Infrastructure Ecosystem Codeberg and Forgejo Computer networks with BIRD, KNOT and Turris Debian Delta Chat (Sunday) Digital Public Goods Dolibar ERP CRM + Odoo Community Association (OCA) Dronecode Foundation + The Zephyr Project Eclipse Foundation F-Droid and /e/OS + OW2 FOSS community / Murena degooglized phones and suite Fedora Project Firefly Zero Foreman FOSS United + fundingjson (and FLOSS/fund) FOSSASIA Framework舰
15 Nov 2025 11:00pm GMT
13 Nov 2025
FOSDEM 2026
FOSDEM 2026 Main Track Deadline Reminder
Submit your proposal for the FOSDEM main track before it's too late! The deadline for main track submissions is earlier than it usually is (16th November, that's in a couple of days!), so don't be caught out. For full details on submission information, look at the original call for participation.
13 Nov 2025 11:00pm GMT
08 Nov 2025
FOSDEM 2026
FOSDEM Junior Call for Participation
Proposals for FOSDEM JUNIOR can now be submitted! FOSDEM Junior is a specific track to organise workshops and activities for children from age 7 to 17 during the FOSDEM weekend. These activities are for children to learn and get inspired about technology and open source. We are looking for activities for children from age 7 to 17. These activities are for children to learn and get inspired about technology. Last year's activities included microcontrollers, game development, embroidery, python programming, mobile application development, music, and data visualization. If you are still unsure if your activity fits FOSDEM Junior, feel free to舰
08 Nov 2025 11:00pm GMT








