Recent changes to this wiki:

add news item for scroll 1.20150323
diff --git a/code/scroll/news/version_1.20150323.mdwn b/code/scroll/news/version_1.20150323.mdwn
new file mode 100644
index 0000000..c2087b5
--- /dev/null
+++ b/code/scroll/news/version_1.20150323.mdwn
@@ -0,0 +1,4 @@
+scroll 1.20150323 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Fix a minor bug when moving over punctuation that was not swallowed.
+     (This didn't affect gameplay.)"""]]
\ No newline at end of file

trim
diff --git a/pics/misc/brain.png b/pics/misc/brain.png
index f0cdf57..d5f62f6 100644
Binary files a/pics/misc/brain.png and b/pics/misc/brain.png differ

add
diff --git a/pics/misc/brain.png b/pics/misc/brain.png
new file mode 100644
index 0000000..f0cdf57
Binary files /dev/null and b/pics/misc/brain.png differ

review
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index f5cae78..308d642 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -52,6 +52,8 @@ and then run:
 > great game once you get the hang of it  
 > I <3 Haskell :D -- Anon
 
+> addictive .. seems like the birth of a classic -- simonkwmichael
+
 ## coda
 
 [[game_designer_thoughts|scroll/thoughts]]

add news item for scroll 1.20150314
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index e57bc94..f5cae78 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -34,7 +34,7 @@ and then run:
 
 ## blog
 
-[[!inline pages="blog/entry/7drl_2015_*" archive=yes]]
+[[!inline pages="blog/entry/7drl_2015_* or code/scroll/news/*" archive=yes]]
 
 ## reviews
 
diff --git a/code/scroll/news/version_1.20150314.mdwn b/code/scroll/news/version_1.20150314.mdwn
new file mode 100644
index 0000000..287d6af
--- /dev/null
+++ b/code/scroll/news/version_1.20150314.mdwn
@@ -0,0 +1,9 @@
+scroll 1.20150314 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * This is a post-7drl release that does not change any gameplay.
+   * Support use as a pager, with input piped in on stdin, as long
+     as the unix module is available.
+   * Rename Setup.hs since cabal uses that name.
+     Thanks to Sergei Trofimovich of Gentoo, which has packaged scroll!
+   * Don't crash on exceedingly small input files when being used as a
+     pager."""]]
\ No newline at end of file

response
diff --git a/blog/entry/shell_monad/comment_2_08df15c64a43affe03a6ee4e77dc7a30._comment b/blog/entry/shell_monad/comment_2_08df15c64a43affe03a6ee4e77dc7a30._comment
new file mode 100644
index 0000000..f113de5
--- /dev/null
+++ b/blog/entry/shell_monad/comment_2_08df15c64a43affe03a6ee4e77dc7a30._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2015-03-14T17:34:23Z"
+ content="""
+Thanks for reporting, I'll fix this in 0.6.3.
+
+The best way to report bugs in shell-monad currently is to email me.
+"""]]

img
diff --git a/blog/entry/7drl_2015_day_7_scroll_success.mdwn b/blog/entry/7drl_2015_day_7_scroll_success.mdwn
index bc6b54d..0a41ec6 100644
--- a/blog/entry/7drl_2015_day_7_scroll_success.mdwn
+++ b/blog/entry/7drl_2015_day_7_scroll_success.mdwn
@@ -18,4 +18,6 @@ code and features a playable character.
 I have six hours until my 7drl is officially over, but I'm done! Success!
 You can download the code, play, etc, at [[Scroll's homepage|code/scroll]]
 
+<a href="http://joeyh.name/code/scroll/"><img src="https://joeyh.name/code/scroll/title.png"></a>
+
 [[!tag 7drl code/scroll]]

scroll fin
diff --git a/blog/entry/7drl_2015_day_7_scroll_success.mdwn b/blog/entry/7drl_2015_day_7_scroll_success.mdwn
new file mode 100644
index 0000000..bc6b54d
--- /dev/null
+++ b/blog/entry/7drl_2015_day_7_scroll_success.mdwn
@@ -0,0 +1,21 @@
+A frantic last day of work on [[code/Scroll]].
+
+Until 3 am last night, I was working on adding a new procedurally generated
+level.
+
+This morning, fixed two major bugs reported by playesters overnight. Also
+fixed crashes on small screens and got the viewport to scroll. Added a victory
+animation in time for lunch.
+
+After lunch, more level generation work. Wasted an entire hour tracking
+down a bug in level gen I introduced last night, when I was bad and didn't
+create a data type to express an idea. Added a third type of generated
+level, with its own feel.
+
+Finished up with a level selection screen, which needed just 47 lines of
+code and features a playable character.
+
+I have six hours until my 7drl is officially over, but I'm done! Success!
+You can download the code, play, etc, at [[Scroll's homepage|code/scroll]]
+
+[[!tag 7drl code/scroll]]
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index 3f9c906..e57bc94 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -1,3 +1,5 @@
+[[scroll/title.png]]
+
 In `scroll`, you're a bookworm that's stuck on a scroll. You have to
 dodge between words and use spells to make your way down the page as the
 scroll is read. Go too slow and you'll get wound up in the scroll and
@@ -18,7 +20,17 @@ For a quick play on the web, there are two demo servers up!
 For deeper exploration, I recommend using telnet instead. Login and
 password for telnet are both "scroll".
 
-Code coming soon for local play...
+## install `scroll`
+
+[Git repository](http://git.joeyh.name/?p=scroll.git;a=summary)
+
+Requires ncurses, on Linux or Similar.
+
+To build, first install the [Haskell Platform](http://www.haskell.org/platform/),
+and then run:
+
+	cabal update
+	cabal install scroll --bindir=$HOME/bin
 
 ## blog
 
@@ -30,8 +42,16 @@ Code coming soon for local play...
 > for ten minutes, you start thinking you're a worm looking for holes....
 > :-) -- Anna
 
+> Entertaining and addictive !  Thanks ! -- Anon
+
 > I really enjoyed munching my way through gplv2 -- Chris
 
+> this has been possibly the first game I've played using telnet that didn't
+> make me want to shoot my face off -- Anon
+
+> great game once you get the hang of it  
+> I <3 Haskell :D -- Anon
+
 ## coda
 
 [[game_designer_thoughts|scroll/thoughts]]
diff --git a/code/scroll/thoughts.mdwn b/code/scroll/thoughts.mdwn
index 70bb39a..8ce1599 100644
--- a/code/scroll/thoughts.mdwn
+++ b/code/scroll/thoughts.mdwn
@@ -1,6 +1,16 @@
 HERE BE SPOLIERS
 
-A few random bits about the game design. 
+A few random bits about the game design etc.
+
+* This is my first roguelike and my first 7drl, and the first game I wrote
+  since days of BASIC. (Not counting some MOO engines.) Haskell made this
+  a much different development experience than I remember. It was a blast!
+
+* Some players find the limitations on movement when the worm has swallowed
+  letters to be confusing. There is not a great rationalle for it that fits
+  into the game world. It was necessary to make the game sufficiently hard.
+  It might have been better to have letters be things you can swallow and
+  move around with, but for whatever reason, you don't want to.
 
 * The way that the end-game and winning plays out was a not-quite accident.
   At first I had the View contain a list of status messages, but when I
@@ -12,19 +22,52 @@ A few random bits about the game design.
 * It's possible for two spells to share overlapping components, or even
   for a single spell component to cast multiple spells at once. This was
   an accident, but it simplified the UI and data structires, and I liked
-  the possibilities,  so I kept it. It may allow for NEW farming in some
-  situations.
+  the possibilities,  so I kept it.
+  
+* NEW farming may be possible in some situations. Call it a feature.
 
 * The scroll is not physically right when flipped; the ragged bits should
   move to the other side. It's just too difficult to make semi-procedurally
   generated content that is interesting to read and that would fit in
-  such a scroll geometry, for me to be able to do that. (I might have
-  been able to do it if the back of the scroll were entirely limited to
-  Joyce, but that seemed too cruel and I added the Joyce content generator
-  too late.)
+  such a scroll geometry, for me to be able to do that. 
+  
+  (I might have been able to do it if the back of the scroll were entirely
+  limited to Joyce, but that seemed too cruel and I added the Joyce content
+  generator too late. Also, a true inversion would make thinking about
+  where to dive harder.)
 
-* Some players find the limitations on movement when the worm has swallowed
-  letters to be confusing. There is not a great rationalle for it that fits
-  into the game world. It was necessary to make the game sufficiently hard.
-  It might have been better to have letters be things you can swallow and
-  move around, but for whatever reason, you don't want to.
+  Also, it's physically impossible for a rolling up scroll to crush you
+  inside itself from both sides. Once side would really be safe if this
+  were real life. ;)
+
+* I went in with a basic game design, but I thought of diving to the other
+  side during early development. The nice thing about it is that if one
+  side gets the player irrepairably stuck, the other side probably offers a
+  way past. Since the level generation doesn't ensure a path through a
+  level, this is a very good thing!
+
+* I also didn't think up the crushed by the scroll mechanic to drive the
+  player onward until I had a scroll drawn onscreen with the player
+  moving in it. I was very happy that this gave a pace to the game,
+  similar to how hunger works in other roguelikes, but more visual, and
+  that the final "scroll rolls up" endgame fell out for nearly free.
+
+* The level generation, such as it is, was a series of happy accidents.
+  All I was sure about was the GPL would be in the mix. Given how classic
+  the source material used is, I wanted to keep close to it, rather than
+  scrambling it completely, so that it could still be enjoyed as the game
+  was played. At the same time, there is a fair amount of subtle randomness
+  going on, so there are many possible different games of scroll.
+  
+  Different texts admit different methods of randomization.
+
+  Kafka's _Metamorphasis_ would have been **utterly perfect** material,
+  but it was too linear to work well.
+
+> One morning, when Gregor Samsa woke from troubled dreams, he found himself
+> transformed in his bed into a horrible vermin. He lay on his armour-like
+> back, and if he lifted his head a little he could see his brown belly, slightly
+> domed and divided by arches into stiff sections. The bedding was hardly able to
+> cover it and seemed ready to slide off any moment. His many legs, pitifully thin
+> compared with the size of the rest of him, waved about helplessly as he
+> looked.
diff --git a/code/scroll/title.png b/code/scroll/title.png
new file mode 100644
index 0000000..07c77d8
Binary files /dev/null and b/code/scroll/title.png differ

format
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index 96c8230..3f9c906 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -12,16 +12,11 @@ BTW, `scroll` is also a functional unix file pager, like `less` or `more`.
 
 For a quick play on the web, there are two demo servers up!
 
-* US <http://us.scroll.joeyh.name:4242/>
-* EU <http://eu.scroll.joeyh.name:4242/>
+* US <http://us.scroll.joeyh.name:4242/>  -or-  `telnet us.scroll.joeyh.name`
+* EU <http://eu.scroll.joeyh.name:4242/>  -or-  `telnet eu.scroll.joeyh.name`
 
-For deeper exploration, I recommend using telnet instead.
-
-	telnet us.scroll.joeyh.name
-
-	telnet eu.scroll.joeyh.name
-
-(Login and Password are both "scroll")
+For deeper exploration, I recommend using telnet instead. Login and
+password for telnet are both "scroll".
 
 Code coming soon for local play...
 

fix
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index 6ded2fa..96c8230 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -17,8 +17,9 @@ For a quick play on the web, there are two demo servers up!
 
 For deeper exploration, I recommend using telnet instead.
 
-> telnet us.scroll.joeyh.name
-> telnet eu.scroll.joeyh.name
+	telnet us.scroll.joeyh.name
+
+	telnet eu.scroll.joeyh.name
 
 (Login and Password are both "scroll")
 

fmt
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index 6802d4a..6ded2fa 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -3,7 +3,7 @@ dodge between words and use spells to make your way down the page as the
 scroll is read. Go too slow and you'll get wound up in the scroll and
 crushed.
 
-Scroll was developed in 7 days during the 2015 [7drl](http://7drl.org/)
+`scroll` was developed in 7 days during the 2015 [7drl](http://7drl.org/)
 challenge. It is written in Haskell.
 
 BTW, `scroll` is also a functional unix file pager, like `less` or `more`.
@@ -38,4 +38,4 @@ Code coming soon for local play...
 
 ## coda
 
-[[game_designer_thoughts|spell/thoughts]]
+[[game_designer_thoughts|scroll/thoughts]]

typo
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index dca511b..6802d4a 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -17,7 +17,7 @@ For a quick play on the web, there are two demo servers up!
 
 For deeper exploration, I recommend using telnet instead.
 
-> telnet us.scroll.joeyh.name`
+> telnet us.scroll.joeyh.name
 > telnet eu.scroll.joeyh.name
 
 (Login and Password are both "scroll")

blog and srcoll website update
diff --git a/blog/entry/7drl_2015_day_6_must_add_more.mdwn b/blog/entry/7drl_2015_day_6_must_add_more.mdwn
new file mode 100644
index 0000000..912054e
--- /dev/null
+++ b/blog/entry/7drl_2015_day_6_must_add_more.mdwn
@@ -0,0 +1,33 @@
+Last night I put up a telnet server and web interface to play a demo of
+scroll and send me playtester feedback, and I've gotten that *almost* solid
+today. **[Try it!](https://joeyh.name/code/scroll/)**
+
+Today was a scramble to add more features to [[code/Scroll]] and fix bugs.
+The game still needs some balancing, and generally seems a little too hard,
+so added a couple more spells, and a powerup feature to make it easier.
+
+Added a way to learn new spells. Added a display of spell inventory on 'i'.
+For that, I had to write a quick windowing system (20 lines of code).
+
+Added a system for ill effects from eating particular letters.
+Interestingly, since such a letter is immediately digested, it doesn't
+prevent the worm from moving forwards. So, the ill effects
+can be worth it in some situations. Up to the player to decide.
+
+I'm spending a lot of time now looking at letter frequency historgrams to
+decide which letter to use for a new feature. Since I've several times
+accidentially used the same letter for two different things (most
+amusingly, I assigned 'k' to a spell, forgetting it was movement), I
+refactored all the code to have a single `charSet` which defines every
+letter and what it's used for, be that movement, control, spell casting,
+or ill effects. I'd *like* to use that to further randomize which letters
+are used for spell components, out of a set that have around the same
+frequency. However, I doubt that I'll have time to do that.
+
+In the final push tonight/tomorrow, I hope to add an additional kind of
+level or two, make the curses viewport scroll when necessary instead of
+crashing, and hopefully work on game balance/playtester feedback.
+
+I've written ~2800 lines of code so far this week!
+
+[[!tag 7drl code/scroll]]
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index 8f1564c..dca511b 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -1,5 +1,41 @@
-My entry in the 2015 [7drl](http://7drl.org/) challenge.
+In `scroll`, you're a bookworm that's stuck on a scroll. You have to
+dodge between words and use spells to make your way down the page as the
+scroll is read. Go too slow and you'll get wound up in the scroll and
+crushed.
 
-Code and online demo coming soon...
+Scroll was developed in 7 days during the 2015 [7drl](http://7drl.org/)
+challenge. It is written in Haskell.
 
-[[!inline pages="blog/entry/7drl_2015_*"]]
+BTW, `scroll` is also a functional unix file pager, like `less` or `more`.
+
+## play `scroll`
+
+For a quick play on the web, there are two demo servers up!
+
+* US <http://us.scroll.joeyh.name:4242/>
+* EU <http://eu.scroll.joeyh.name:4242/>
+
+For deeper exploration, I recommend using telnet instead.
+
+> telnet us.scroll.joeyh.name`
+> telnet eu.scroll.joeyh.name
+
+(Login and Password are both "scroll")
+
+Code coming soon for local play...
+
+## blog
+
+[[!inline pages="blog/entry/7drl_2015_*" archive=yes]]
+
+## reviews
+
+> When you start reading on your kindle after playing your game
+> for ten minutes, you start thinking you're a worm looking for holes....
+> :-) -- Anna
+
+> I really enjoyed munching my way through gplv2 -- Chris
+
+## coda
+
+[[game_designer_thoughts|spell/thoughts]]
diff --git a/code/scroll/thoughts.mdwn b/code/scroll/thoughts.mdwn
new file mode 100644
index 0000000..70bb39a
--- /dev/null
+++ b/code/scroll/thoughts.mdwn
@@ -0,0 +1,30 @@
+HERE BE SPOLIERS
+
+A few random bits about the game design. 
+
+* The way that the end-game and winning plays out was a not-quite accident.
+  At first I had the View contain a list of status messages, but when I
+  realized it would be neat for the bookworm to be able to interact with
+  them, I had to rework the data structure to let the victory condition work.
+
+  [[!img endgame.png]]
+
+* It's possible for two spells to share overlapping components, or even
+  for a single spell component to cast multiple spells at once. This was
+  an accident, but it simplified the UI and data structires, and I liked
+  the possibilities,  so I kept it. It may allow for NEW farming in some
+  situations.
+
+* The scroll is not physically right when flipped; the ragged bits should
+  move to the other side. It's just too difficult to make semi-procedurally
+  generated content that is interesting to read and that would fit in
+  such a scroll geometry, for me to be able to do that. (I might have
+  been able to do it if the back of the scroll were entirely limited to
+  Joyce, but that seemed too cruel and I added the Joyce content generator
+  too late.)
+
+* Some players find the limitations on movement when the worm has swallowed
+  letters to be confusing. There is not a great rationalle for it that fits
+  into the game world. It was necessary to make the game sufficiently hard.
+  It might have been better to have letters be things you can swallow and
+  move around, but for whatever reason, you don't want to.

thread
diff --git a/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn b/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
index 85d6e46..24c0a63 100644
--- a/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
+++ b/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
@@ -87,3 +87,7 @@ interesting ideas, and implement them quickly.
 Just what I need when writing a roguelike in just 7 days!
 
 [[!tag haskell 7drl code/scroll]]
+
+----
+
+[Fun comment thread on reddit](https://www.reddit.com/r/haskell/comments/2yqwgi/type_directed_spell_system_development/)

remove haskell tag from not very haskell posts
diff --git a/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn b/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn
index 94815f9..24af9db 100644
--- a/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn
+++ b/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn
@@ -1,4 +1,4 @@
-[[!tag haskell 7drl code/scroll]]
+[[!tag 7drl code/scroll]]
 
 Much as I want to get my `@` displayed and moving around screen,
 last night and today have focused on level generation instead.
diff --git a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
index 22deff0..5c7fddf 100644
--- a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
+++ b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
@@ -39,4 +39,4 @@ to give that surprise away.
 But, the other thing about Scroll is that it's
 `scroll`, a completely usable (if rather difficult..) Unix pager!
 
-[[!tag haskell 7drl code/scroll]]
+[[!tag 7drl code/scroll]]

add
diff --git a/code/scroll/endgame.png b/code/scroll/endgame.png
new file mode 100644
index 0000000..69f79e1
Binary files /dev/null and b/code/scroll/endgame.png differ

blog
diff --git a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
index b419688..22deff0 100644
--- a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
+++ b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
@@ -34,7 +34,9 @@ explore in writing Scroll.
 As to the text that fills the scroll, it's broadly procedurally generated,
 in what I hope are unusual and repeatedly surprising (and amusing)
 ways. I'm not showing any screenshots of the real text, because I don't want
-to give that surprise away. But, the other thing about Scroll is that it's
+to give that surprise away. 
+
+But, the other thing about Scroll is that it's
 `scroll`, a completely usable (if rather difficult..) Unix pager!
 
 [[!tag haskell 7drl code/scroll]]
diff --git a/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn b/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
new file mode 100644
index 0000000..85d6e46
--- /dev/null
+++ b/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
@@ -0,0 +1,89 @@
+I want my [[7drl game Scroll|code/Scroll]] to have lots of interesting spells.
+So, as I'm designing its spell system, I've been looking at the types, and
+considering the whole universe of possible spells that fit within the
+constraints of the types.
+
+My first throught was that a spell would be a function from `World -> World`.
+That allows any kind of spell that manipulates the game map. Like, for
+instance a "whiteout" that projects a stream of whitespace from the
+player's mouth.
+
+Since Scroll has a state monad, I quickly generalized that; making spell
+actions a state monad `M ()`, which lets spells reuse other monadic actions, 
+and affect the whole game state, including the player. Now I could write
+a spell like "teleport", or "grow".
+
+But it quickly became apparent this was too limiting: While spells could change
+the World map, the player, and even change the list of supported spells, they
+had no way to prompting for input.
+
+I tried a few types of the `Event -> M ()` variety, but they were all too
+limiting. Finally, I settled on this type for spell actions:
+`M NextStep -> M NextStep`. 
+
+And then I spent 3 hours exploring the universe of spells that type allows!
+To understand them, it helps to see what a NextStep is:
+
+[[!format haskell """
+type Step = Event -> M NextStep
+data NextStep = NextStep View (Maybe Step)
+"""]]
+
+Since `NextStep` is a continuation, spells take the original continuation,
+and can not only modify the game state, but can return an altered
+continuation. Such as one that prompts for input before performing the
+spell, and then calls the original continuation to get on with the game.
+
+That let me write "new", a most interesting spell, that lets the player
+add a new way to cast an existing spell. Spells are cast using ingredients, 
+and so this prompts for a new ingredient to cast a spell. (I hope that
+"new farming" will be one mode of play to possibly win [[code/Scroll]].)
+
+And, it lets me write spells that fail in game-ending ways. (Ie, "genocide @").
+A spell can cause the game to end by returning a continuation that
+has `Nothing` as its next step.
+
+Even better, I could write spells that return a continuation that contains a
+forked background task, using the 66 line contiuation based threading system I
+built in [[day_3|7drl_2015_day_3_movement_at_last]]. This allows writing lots
+of fun spells that have an effect that lasts for a while. Things like letting
+the player quickly digest letters they eat, or slow down the speed of
+events.
+
+And then I thought of "dream". This spell stores the input continuation and
+game state, and returns a modified continuation that lets the game continue
+until it ends, and then restores from the point it saved. So, the player dreams
+they're playing, and wakes back up where they cast the spell. A wild spell,
+which can have different variants, like precognitive dreams where the same
+random numbers are used as will be used upon awaking, or dreams where
+knowledge carries back over to the real world in different ways.
+(Supports Inception too..) 
+
+Look how easy it was to implement dreaming,
+in this game that didn't have any notion of "save" or "restore"!
+
+[[!format haskell """
+runDream :: M NextStep -> M NextStep -> (S -> S) -> M NextStep
+runDream sleepcont wakecont wakeupstate = go =<< sleepcont
+   where
+         go (NextStep v ms) = return $ NextStep v $ Just $
+	 	maybe wake (go <=<) ms
+         wake _evt = do
+                 modify wakeupstate
+                 wakecont
+"""]]
+
+I imagine that, if I were not using Haskell, I'd have just made the spell
+be an action, that can do IO in arbitrary ways. Such a spell system can
+of course do everything I described above and more. But, I think that
+using a general IO action is so broad that it hides the interesting
+possibilities like "dream".
+
+By starting with a limited type for spells, and exploring toward
+more featureful types, I was able to think about the range of
+possibilities of spells that each type allowed, be inspired with
+interesting ideas, and implement them quickly.
+
+Just what I need when writing a roguelike in just 7 days!
+
+[[!tag haskell 7drl code/scroll]]

wording
diff --git a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
index 92f8919..b419688 100644
--- a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
+++ b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
@@ -9,12 +9,12 @@ learning spells, and to balance out the game difficulty.
 
 I haven't quite said what [[code/Scroll]] is about yet, let's fix that:
 
-In Scroll, you're a bookworm that's marooned on a scroll. You have to
+In Scroll, you're a bookworm that's stuck on a scroll. You have to
 dodge between words and use spells to make your way down the page as the
 scroll is read. Go too slow and you'll get wound up in the scroll and
 crushed.
 
-So, the player is multiple chars in size (size is the player's only stat),
+The character is multiple chars in size (size is the worm's only stat),
 and the worm interacts with the scroll in lots of ways, like
 swallowing letters, or diving through a hole to the other side of the
 scroll. While it can swallow some letters, if it gets too full, it can't

blog
diff --git a/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
new file mode 100644
index 0000000..92f8919
--- /dev/null
+++ b/blog/entry/7drl_2015_day_4_coding_through_exhaustion.mdwn
@@ -0,0 +1,40 @@
+Slow start today; I was pretty exhausted after yesterday and last night's
+work. Somehow though, I got past the burn and made major progress today.
+
+All the complex movement of both the player and the scroll is finished now,
+and all that remains is to write interesting spells, and a system for
+learning spells, and to balance out the game difficulty.
+
+----
+
+I haven't quite said what [[code/Scroll]] is about yet, let's fix that:
+
+In Scroll, you're a bookworm that's marooned on a scroll. You have to
+dodge between words and use spells to make your way down the page as the
+scroll is read. Go too slow and you'll get wound up in the scroll and
+crushed.
+
+So, the player is multiple chars in size (size is the player's only stat),
+and the worm interacts with the scroll in lots of ways, like
+swallowing letters, or diving through a hole to the other side of the
+scroll. While it can swallow some letters, if it gets too full, it can't
+move forward anymore, so letters are mostly consumed to be used as spell
+components.
+
+I think that I will manage to get away without adding any kind of monsters
+to the game; the scroll (and whoever is reading it) is the antagonist.
+
+As I'm writing this very post, I'm imagining the worm wending its way through
+my paragraphs. This dual experience of text, where you're both reading its
+content and hyper-aware of its form, is probably the main thing I wanted to
+explore in writing Scroll.
+
+[[!img code/scroll/1st_screenshot.png]]
+
+As to the text that fills the scroll, it's broadly procedurally generated,
+in what I hope are unusual and repeatedly surprising (and amusing)
+ways. I'm not showing any screenshots of the real text, because I don't want
+to give that surprise away. But, the other thing about Scroll is that it's
+`scroll`, a completely usable (if rather difficult..) Unix pager!
+
+[[!tag haskell 7drl code/scroll]]
diff --git a/code/scroll/1st_screenshot.png b/code/scroll/1st_screenshot.png
new file mode 100644
index 0000000..62b2638
Binary files /dev/null and b/code/scroll/1st_screenshot.png differ

Added a comment: Issue tracker anywhere?
diff --git a/blog/entry/shell_monad/comment_1_d71ef4bed2b7ddfc92db8f0ff08464c0._comment b/blog/entry/shell_monad/comment_1_d71ef4bed2b7ddfc92db8f0ff08464c0._comment
new file mode 100644
index 0000000..78befed
--- /dev/null
+++ b/blog/entry/shell_monad/comment_1_d71ef4bed2b7ddfc92db8f0ff08464c0._comment
@@ -0,0 +1,23 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawmdoZwTJoqBSrwWtAgFfNoD3zU8h7a0nzI"
+ nickname="Ville"
+ subject="Issue tracker anywhere?"
+ date="2015-03-10T12:52:47Z"
+ content="""
+Do you have an issue tracker for shell monad? It seems that 
+
+     ifCmd (test (TDirExists (\"MCQ/.git\"))) .. ..
+
+generates code 
+
+     if test \(-d 'MCQ/.git'\)
+
+instead of the working
+
+    if test \( -d 'MCQ/.git' \)
+
+or 
+
+    if [ -d 'MCQ/.git' ]
+
+"""]]

typo
diff --git a/blog/entry/7drl_2015_day_3_movement_at_last.mdwn b/blog/entry/7drl_2015_day_3_movement_at_last.mdwn
index dcb0fa4..169b4b1 100644
--- a/blog/entry/7drl_2015_day_3_movement_at_last.mdwn
+++ b/blog/entry/7drl_2015_day_3_movement_at_last.mdwn
@@ -67,7 +67,7 @@ endThread = nextStep Nothing
 background :: M NextStep -> M NextStep
 background job = fork job continue
 
-demo :: M.NextStep
+demo :: M NextStep
 demo = do
 	showMessage "foo"
 	background $ next $ const $

blog
diff --git a/blog/entry/7drl_2015_day_3_movement_at_last.mdwn b/blog/entry/7drl_2015_day_3_movement_at_last.mdwn
new file mode 100644
index 0000000..dcb0fa4
--- /dev/null
+++ b/blog/entry/7drl_2015_day_3_movement_at_last.mdwn
@@ -0,0 +1,78 @@
+[[!tag haskell 7drl code/scroll]]
+
+Got the player moving in the map! And, got the map to be deadly in its own
+special way.
+
+[[!format haskell """
+        HeadCrush -> do
+                showMessage "You die."
+                endThread
+"""]]
+
+Even winning the game is implemented. The game has a beginning, a middle,
+and an end.
+
+I left the player movement mostly unconstrained, today, while I was working
+on things to do with the end of the game, since that makes it easier to
+play through and test them. Tomorrow, I will turn on fully constrained
+movement (an easy change), implement inventory (which is very connected to
+movement constraints in [[code/Scroll]]), and hope to 
+start on the spell system too.
+
+----
+
+At this point, Scroll is 622 lines of code, including content.
+Of which, I notice, fully 119 are types and type classes.
+
+Only 4 days left! Eep! I'm very glad that scroll's central antagonist is
+already written. I don't plan to add other creatures, which will save some
+time.
+
+----
+
+Last night as I was drifting off to sleep, it came to me a way
+to implement my own threading system for my roguelike. Since
+time in a roguelike happens in discrete ticks, as the player takes
+each action, normal OS threads are not suitable. And in my case,
+I'm doing everything in pure code anyway and certianly cannot fork
+off a thread for some background job.
+
+But, since I'm using continuation passing style, I can just write
+my own `fork`, that takes two continuations and combines them,
+causing both to be run on each tick, and recursing to handle combining
+the resulting continuations.
+
+It was really quite simple to implement. Typechecked on the first try even!
+
+[[!format haskell """
+fork :: M NextStep -> M NextStep -> M NextStep
+fork job rest = do
+        jn <- job
+        rn <- rest
+        runthread jn rn
+  where
+        runthread (NextStep _ (Just contjob)) (NextStep v (Just contr)) =
+                return $ NextStep v $ Just $ \i -> do
+                        jn <- contjob i
+                        rn <- contr i
+                        runthread jn rn
+        runthread (NextStep _ Nothing) (NextStep v (Just contr)) =
+                return $ NextStep v (Just contr)
+        runthread _ (NextStep v Nothing) =
+                return $ NextStep v Nothing
+
+endThread :: M NextStep
+endThread = nextStep Nothing
+
+background :: M NextStep -> M NextStep
+background job = fork job continue
+
+demo :: M.NextStep
+demo = do
+	showMessage "foo"
+	background $ next $ const $
+		clearMessage >> endThread
+"""]]
+
+That has some warts, but it's good enough for my purposes, and pretty
+awesome for a threading system in 66 LOC.

wip
diff --git a/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn b/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn
new file mode 100644
index 0000000..94815f9
--- /dev/null
+++ b/blog/entry/7drl_2015_day_2_level_generation_and_game_concept.mdwn
@@ -0,0 +1,62 @@
+[[!tag haskell 7drl code/scroll]]
+
+Much as I want to get my `@` displayed and moving around screen,
+last night and today have focused on level generation instead.
+
+Scroll has kinda strange level generation method, compared to how
+I suppose most roguelikes do it. There are only 3 calls to `rand`
+in all of Scroll. Just a 3 random parameters, but that's enough to
+ensure a different level each time.
+
+[[!format haskell """
+-- Random level generation function.
+level :: Bool -> StdGen -> [String]
+level randomize r = concat
+        [ final (length tutorial + extra) $ concat $ rand mariner1body
+        , concat $ rand mariner1end
+        , concatMap rand kubla
+        ]
+  where
+	-- here be spoilers
+"""]]
+
+You could say there are two influences in Scroll's level generation method:
+Nick Montfort and Samuel Taylor Coleridge.
+
+----
+
+I have thought some about [[code/Scroll]] before starting the 7drl week,
+but my idea for the game was missing some key concepts. There was nothing
+to keep the player engaged in moving forward, an unclear victory condition,
+no clue how to generate appropriate random levels, a large potential for
+getting stuck, and no way to lose the game. This is all a problem for a
+roguelike. 
+
+But, I had an idea finally last night, about a single unified thing that
+all of that stuff falls out from. And it's right there in the name of the game!
+
+  
+
+Now that I understand Scroll better, I wrote the tutorial level.
+It's a very meta tutorial level that sets the scene well and serves
+more purposes than are at first apparent. I count a total of 6 things
+that this "tutorial level" will let the user do.
+
+And interestingly, while the tutorial level is static, it interacts with the
+rest of the game in a way that will make it be experienced differently
+every time through.
+
+----
+
+The strangest line of code I wrote today is:
+
+	import GPL
+
+Somehow, I have never before, in all my time programming, written a line
+like that one.
+
+----
+
+Finally, after 7 hours of nonstop coding, I got ncurses to display the
+generated game world, scrolling around in the display viewport. No `@`
+yet; that will need to wait for tonight or tomorrow!

Added a comment: `vector` has bounds
diff --git a/blog/entry/7drl_2015_day_1_groundwork/comment_1_2c1e425ccc14bd59f4d191bb26873fa7._comment b/blog/entry/7drl_2015_day_1_groundwork/comment_1_2c1e425ccc14bd59f4d191bb26873fa7._comment
new file mode 100644
index 0000000..4f5f8e5
--- /dev/null
+++ b/blog/entry/7drl_2015_day_1_groundwork/comment_1_2c1e425ccc14bd59f4d191bb26873fa7._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="http://www.joachim-breitner.de/"
+ nickname="nomeata"
+ subject="`vector` has bounds"
+ date="2015-03-09T15:54:51Z"
+ content="""
+Hi Joey,
+
+the vectors in the `vector` package keep track of their length and bounds. For unboxed vectors, you might want to look at `Data.Vector.Unboxed.Mutable`.
+"""]]

update
diff --git a/blog/entry/7drl_2015_day_1_groundwork.mdwn b/blog/entry/7drl_2015_day_1_groundwork.mdwn
index 33ef663..38a5dca 100644
--- a/blog/entry/7drl_2015_day_1_groundwork.mdwn
+++ b/blog/entry/7drl_2015_day_1_groundwork.mdwn
@@ -69,7 +69,7 @@ Finally, I wrote some ncurses display code, which is *almost* working.
 
 ----
 
-Hours spent so far: 8
+Start time: After midnight last night. Will end by midnight next Friday.
 
 Lines of code written today: 368
 
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index 1b84c18..8f1564c 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -2,4 +2,4 @@ My entry in the 2015 [7drl](http://7drl.org/) challenge.
 
 Code and online demo coming soon...
 
-[[!inline pages="blog/entry/7drl_2015/*"]]
+[[!inline pages="blog/entry/7drl_2015_*"]]

post
diff --git a/blog/entry/7drl_2015_day_1_groundwork.mdwn b/blog/entry/7drl_2015_day_1_groundwork.mdwn
new file mode 100644
index 0000000..33ef663
--- /dev/null
+++ b/blog/entry/7drl_2015_day_1_groundwork.mdwn
@@ -0,0 +1,91 @@
+[[!tag haskell 7drl code/scroll]]
+
+[[code/Scroll]] is a roguelike, with a twist, which I won't reveal until I've
+finished building it. I'll just say: A playable roguelike pun, set in a
+filesystem near you.
+
+I'm creating Scroll as part of the [7DRL Challange](http://7drl.org/).
+If all goes well, I'll have a usable roguelike game finished in 7 days.
+
+This is my first time developing a roguelike, and my first time writing a
+game in Haskell, and my first time writing a game to a time limit. Wow!
+
+----
+
+First, some groundwork. I'm writing Scroll in Haskell, so let's get the
+core data types and monads and IO squared away. Then I can spend days 2-7
+writing entirely pure functional code, in the Haskell happy place.
+
+To represent the current level, I'm using a Vector of Vectors of Chars.
+Actually, MVectors, which can be mutated safely by pure code running inside
+the ST monad, so it's fast and easy to read or write any particular
+location on the level.
+
+[[!format haskell """
+-- Writes a Char to a position in the world.
+writeWorld :: Pos -> Char -> M ()
+writeWorld (x, y) c = modWorld $ \yv -> do
+	xv <- V.read yv y
+	V.write xv x c
+
+showPlayer :: M ()
+showPlayer = writeWorld (5,8) '@'
+"""]]
+
+(I wish these Vectors had their size as part of their types. There are
+vector libraries on hackage that do, but not the standard vector library,
+which has mutable vectors. As it is, if I try to access outside the bounds
+of the world, it'll crash at runtime.)
+
+Since the game will need some other state, I'm using the state monad. The
+overall monad stack is `type M = StateT S (ST RealWorld)`. (It could be
+`forall s. StateT S (ST s)`, but I had some trouble getting that to type
+check, so I fixed `s` to `RealWorld`, which is ok since it'll be run using
+`stToIO`.
+
+Next, a concept of time, and the main event loop. I decided to use a
+continutation passing style, so the main loop takes 
+the current continuation, and runs it to get a snapshot of the state to
+display, and a new continutation. The advantage of using continuations
+this way is that all the game logic can be handled in the pure code.
+
+I should probably be using the Cont monad in my monad stack, but 
+I've not learned it and lack time. For now I'm handling the
+continuations by hand, which seems ok.
+
+[[!format haskell """
+updateWorld :: Step
+updateWorld (Just 'Q') = do
+        addMessage "Are you sure you want to quit? [yn]"
+        next $ \i -> case i of
+                Just 'y' -> quit
+                _ -> continue
+updateWorld input = do
+        addMessage ("pressed " ++ show input)
+        continue
+"""]]
+
+Finally, I wrote some ncurses display code, which is *almost* working.
+
+----
+
+Hours spent so far: 8
+
+Lines of code written today: 368
+
+Craziest type signature today: `writeS :: forall a. ((Vec2 a -> ST RealWorld ()) -> M ()) -> Pos -> a -> M ()`
+
+----
+
+By the way, there's a whole
+[LambdaHack](http://hackage.haskell.org/package/LambdaHack) library for
+Haskell, targeted at just this kind of roguelike construction. 
+It looks excellent. I'm not using it for two reasons:
+
+1. Scroll is going to be unusual in a lot of ways, and LambdaHack probably
+   makes some assumptions that don't fit.
+2. `mainSer :: (MonadAtomic m, MonadServerReadRequest m) => [String] -> COps -> (m () -> IO ()) -> (COps -> DebugModeCli -> ((FactionId -> ChanServer ResponseUI RequestUI -> IO ()) -> (FactionId -> ChanServer ResponseAI RequestAI -> IO ()) -> IO ()) -> IO ()) -> IO ()`  
+   That's a lot of stuff to figure out! I only have a week, so it's
+   probably easier to build my own framework, and this gives me an
+   opportunity to learn more generally useful stuff, like how to use
+   mutable Vectors.
diff --git a/code.mdwn b/code.mdwn
index a624f5c..52cddd3 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -14,6 +14,7 @@ The stuff that's swapped into my local cache at the moment.
 [[ikiwiki-hosting]]
 [[github-backup]]
 [[shell-monad]]
+[[scroll]]
 
 ## Less active projects
 
diff --git a/code/scroll.mdwn b/code/scroll.mdwn
new file mode 100644
index 0000000..1b84c18
--- /dev/null
+++ b/code/scroll.mdwn
@@ -0,0 +1,5 @@
+My entry in the 2015 [7drl](http://7drl.org/) challenge.
+
+Code and online demo coming soon...
+
+[[!inline pages="blog/entry/7drl_2015/*"]]

cleanup
diff --git a/index.mdwn b/index.mdwn
index 3547430..4cccc7e 100644
--- a/index.mdwn
+++ b/index.mdwn
@@ -28,7 +28,7 @@ input#searchbox {
 .archivepagedate {
 	font-size: 0.5em;
 }
-#actions {
+.actions {
 	display: none;
 }
 </style>

cleanup
diff --git a/index.mdwn b/index.mdwn
index 3b550fa..3547430 100644
--- a/index.mdwn
+++ b/index.mdwn
@@ -28,6 +28,9 @@ input#searchbox {
 .archivepagedate {
 	font-size: 0.5em;
 }
+#actions {
+	display: none;
+}
 </style>
 </div>
 

year
diff --git a/links/interviews.mdwn b/links/interviews.mdwn
index 35dcbfe..f14ba84 100644
--- a/links/interviews.mdwn
+++ b/links/interviews.mdwn
@@ -1,5 +1,5 @@
 ### interviews
 
-[2013: The Setup](http://joey.hess.usesthis.com)  
+[2012: The Setup](http://joey.hess.usesthis.com)  
 [2015: Life after Debian](https://zgrimshell.github.io/interviews-with-floss-developers-joey-hess/)  
 > "I want to build worthwhile things that might last."

interviews
diff --git a/boxen.mdwn b/boxen.mdwn
index 14ab7fd..70be202 100644
--- a/boxen.mdwn
+++ b/boxen.mdwn
@@ -40,7 +40,7 @@ Mostly birds.
 * [[pell]] {*}
 * orca
 * [[clam]] {*}
-* [[diatom]] {*}
+* [[diatom]]
 * [[elephant]] {*}
 
 ## small machines
diff --git a/index.mdwn b/index.mdwn
index fbe185a..3b550fa 100644
--- a/index.mdwn
+++ b/index.mdwn
@@ -39,6 +39,8 @@ input#searchbox {
 </tr>
 </table>
 
+[[!inline feeds=no template=bare pages=links/interviews]]
+
 [[!sidebar content="""
 ### [[blog|blog]] posts
 [[!inline pages="blog/entry/* and !blog/entry/*/* and !link(blog/foo) and !link(unfinished)"
diff --git a/links/interviews.mdwn b/links/interviews.mdwn
new file mode 100644
index 0000000..35dcbfe
--- /dev/null
+++ b/links/interviews.mdwn
@@ -0,0 +1,5 @@
+### interviews
+
+[2013: The Setup](http://joey.hess.usesthis.com)  
+[2015: Life after Debian](https://zgrimshell.github.io/interviews-with-floss-developers-joey-hess/)  
+> "I want to build worthwhile things that might last."

Added a comment: chart error
diff --git a/blog/entry/unicode_eye_chart/comment_2_721ae659831b4eee47c8fd456c01f3f7._comment b/blog/entry/unicode_eye_chart/comment_2_721ae659831b4eee47c8fd456c01f3f7._comment
new file mode 100644
index 0000000..52d3719
--- /dev/null
+++ b/blog/entry/unicode_eye_chart/comment_2_721ae659831b4eee47c8fd456c01f3f7._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawmQfxu4nNsNvBTKTRcnuCyHRVGUlhZRZ4Y"
+ nickname="Robert"
+ subject="chart error"
+ date="2015-02-08T21:41:39Z"
+ content="""
+Missing descriptor: KANNADA LETTER A
+"""]]

Added a comment
diff --git a/blog/entry/a_bug_in_my_ear/comment_8_4fbfcebf84d149227d883d27eb8e691d._comment b/blog/entry/a_bug_in_my_ear/comment_8_4fbfcebf84d149227d883d27eb8e691d._comment
new file mode 100644
index 0000000..265251e
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_8_4fbfcebf84d149227d883d27eb8e691d._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawl_g6WPKJf26fbRnLq8HUoFNdYdnqr-BZU"
+ nickname="Dale"
+ subject="comment 8"
+ date="2015-02-02T02:23:43Z"
+ content="""
+Another trick is to shine a torch in your ear for a few minutes.  Moth like bugs like to seek out and light and will often find their own way out :)
+"""]]

update blogroll
diff --git a/blog.mdwn b/blog.mdwn
index 86c6f0b..ed4c232 100644
--- a/blog.mdwn
+++ b/blog.mdwn
@@ -26,9 +26,9 @@ Posts per month:
 
 My other blogs:
 
-* [git-annex dev blog](http://git-annex.branchable.com/design/assistant/blog)
+* [git-annex dev blog](https://git-annex.branchable.com/devblog/)
 * [olduse.net blog](http://olduse.net/blog/)
-* [after 1 minute on my modem](http://1-minute-modem.branchable.com)
+* [after 1 minute on my modem](https://1-minute-modem.branchable.com)
 
 Other feeds:
 

comment
diff --git a/blog/entry/dealing_with_dialup/comment_2_4bc8ba277f519f3d04da199bd0a97ecb._comment b/blog/entry/dealing_with_dialup/comment_2_4bc8ba277f519f3d04da199bd0a97ecb._comment
new file mode 100644
index 0000000..a37efeb
--- /dev/null
+++ b/blog/entry/dealing_with_dialup/comment_2_4bc8ba277f519f3d04da199bd0a97ecb._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2015-01-28T16:28:11Z"
+ content="""
+I made that blog post in 2007. What do you think?
+
+... Yeah, of course I'm back on dialup!
+
+See also: 
+
+* [[slow]] (why dialup helps you think)
+* [[roundtrip_latency_from_a_cabin_with_dialup_in_2011]]
+* [after 1 minute on my modem](http://1-minute-modem.branchable.com)
+
+If you feel stuck in a rut or unable to concentrate, go spend a week or a
+month in a cabin with dialup and git and a haskell compiler. It's worked
+great for me.
+"""]]

Added a comment: curious
diff --git a/blog/entry/dealing_with_dialup/comment_1_9a52e2f46fd64302f7ae46e7687e8fb9._comment b/blog/entry/dealing_with_dialup/comment_1_9a52e2f46fd64302f7ae46e7687e8fb9._comment
new file mode 100644
index 0000000..15c7238
--- /dev/null
+++ b/blog/entry/dealing_with_dialup/comment_1_9a52e2f46fd64302f7ae46e7687e8fb9._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="jmz"
+ subject="curious"
+ date="2015-01-28T07:21:20Z"
+ content="""
+Do you still use dial up? I've been meaning to learn a great deal of things but I'm horribly unproductive and find myself torrenting, watching videos, talks, podcasts, tutorials on youtube, etc but never really accomplishing much myself. I find it both amazing and inspiring that you accomplish so much and are so prolific on a dialup connection. Just curious if you've still stuck with dialup all these years later? I might be ditching expensive broadband in favor of something more frugal and trying to develop some self-discipline and a work ethic like yours. Thanks for all the work you've done on Debian and your other projects!
+"""]]

thread
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families/comment_1_c9c34bfe36d96691bd5a1c8da77aac5c._comment b/blog/entry/making_propellor_safer_with_GADTs_and_type_families/comment_1_c9c34bfe36d96691bd5a1c8da77aac5c._comment
new file mode 100644
index 0000000..7d5ffdd
--- /dev/null
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families/comment_1_c9c34bfe36d96691bd5a1c8da77aac5c._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2015-01-25T19:57:30Z"
+ content="""
+The reddit comment thread was very interesting:
+<http://www.reddit.com/r/haskell/comments/2tl8v1/making_propellor_safer_with_gadts_and_type/>
+"""]]

context link
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index 0a61c53..5fedf87 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -1,4 +1,5 @@
-Since July, I have been aware of an ugly problem with propellor. Certain
+Since July, I have been aware of an ugly problem with 
+[propellor](https://propellor.branchable.com/). Certain
 propellor configurations could have a bug. I've tried to solve the problem at
 least a half-dozen times without success; it's eaten several weekends. 
 

Added a comment: Special remote for Dropbox
diff --git a/blog/entry/case_study:_adding_box.com_support_to_git-annex/comment_4_24d6e3cbc3075f3cd8dd96f2ce5df5b0._comment b/blog/entry/case_study:_adding_box.com_support_to_git-annex/comment_4_24d6e3cbc3075f3cd8dd96f2ce5df5b0._comment
new file mode 100644
index 0000000..ecc54eb
--- /dev/null
+++ b/blog/entry/case_study:_adding_box.com_support_to_git-annex/comment_4_24d6e3cbc3075f3cd8dd96f2ce5df5b0._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawmFiUAk6J121dyb5bzx2Ga6STjpn9YPHFk"
+ nickname="Salim"
+ subject="Special remote for Dropbox"
+ date="2015-01-25T13:23:53Z"
+ content="""
+Hi Joey
+A bit late the party, but I'm really enthused about what you've done with git-annex!
+
+I've migrated my main machine to use only open-source software, but am still locked into Dropbox because that's what everyone in my company uses. Since Dropbox insists on installing proprietary software in its client, that's a no-go for me. (My experiments with OwnCloud and a proxy machine runnnig OC and DB failed because Dropbox was too unreliable at handling conflicts, and OC was a CPU hog.)
+
+I bet you've seen https://github.com/TobiasTheViking/dropboxannex by now. (Surprised it wasn't listed in the walkthrough.) I'm about to give it a spin. 
+
+If you're request for a Dropbox account or sponsorship still stands, please let me know.  I think it'd be a great stepping stone for a lot of locked in Dropbox users to be able leave the Dark Side.
+"""]]

format
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index e287ee7..0a61c53 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -95,7 +95,11 @@ Now I was able to write 4 instances of `Combines`, for each combination
 of 2 Properties with HasInfo or NoInfo.
 
 It type checked. But, type inference was busted. A simple expression
-like <pre>foo `requires` bar</pre> blew up:
+like 
+
+	foo `requires` bar
+
+blew up:
 
 	   No instance for (Requires (Property HasInfo) (Property HasInfo) r0)
 	      arising from a use of `requires'
@@ -106,7 +110,7 @@ like <pre>foo `requires` bar</pre> blew up:
 	                 (Property HasInfo) (Property HasInfo) (Property HasInfo)
 	        -- Defined at Propellor/Types.hs:167:10
 
-To avoid that, it needed <pre>(foo `requires` bar) :: Property HasInfo</pre> --
+To avoid that, it needed ":: Property HasInfo" appended --
 I didn't want the user to need to write that.
 
 I got stuck here for an long time, well over a month.

format
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index 3268552..e287ee7 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -106,7 +106,7 @@ like <pre>foo `requires` bar</pre> blew up:
 	                 (Property HasInfo) (Property HasInfo) (Property HasInfo)
 	        -- Defined at Propellor/Types.hs:167:10
 
-To avoid that, it needed <pre(foo `requires` bar) :: Property HasInfo</pre> --
+To avoid that, it needed <pre>(foo `requires` bar) :: Property HasInfo</pre> --
 I didn't want the user to need to write that.
 
 I got stuck here for an long time, well over a month.

format
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index 9addbf0..3268552 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -95,7 +95,7 @@ Now I was able to write 4 instances of `Combines`, for each combination
 of 2 Properties with HasInfo or NoInfo.
 
 It type checked. But, type inference was busted. A simple expression
-like <tt>foo `requires` bar</tt> blew up:
+like <pre>foo `requires` bar</pre> blew up:
 
 	   No instance for (Requires (Property HasInfo) (Property HasInfo) r0)
 	      arising from a use of `requires'
@@ -106,7 +106,7 @@ like <tt>foo `requires` bar</tt> blew up:
 	                 (Property HasInfo) (Property HasInfo) (Property HasInfo)
 	        -- Defined at Propellor/Types.hs:167:10
 
-To avoid that, it needed <tt>(foo `requires` bar) :: Property HasInfo</tt> --
+To avoid that, it needed <pre(foo `requires` bar) :: Property HasInfo</pre> --
 I didn't want the user to need to write that.
 
 I got stuck here for an long time, well over a month.

format
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index 813d5b9..9addbf0 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -95,7 +95,7 @@ Now I was able to write 4 instances of `Combines`, for each combination
 of 2 Properties with HasInfo or NoInfo.
 
 It type checked. But, type inference was busted. A simple expression
-like `foo \`requires\` bar` blew up:
+like <tt>foo `requires` bar</tt> blew up:
 
 	   No instance for (Requires (Property HasInfo) (Property HasInfo) r0)
 	      arising from a use of `requires'
@@ -106,7 +106,7 @@ like `foo \`requires\` bar` blew up:
 	                 (Property HasInfo) (Property HasInfo) (Property HasInfo)
 	        -- Defined at Propellor/Types.hs:167:10
 
-To avoid that, it needed `(foo \`requires\` bar) :: Property HasInfo` --
+To avoid that, it needed <tt>(foo `requires` bar) :: Property HasInfo</tt> --
 I didn't want the user to need to write that.
 
 I got stuck here for an long time, well over a month.

format
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index ad3ac24..813d5b9 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -95,7 +95,7 @@ Now I was able to write 4 instances of `Combines`, for each combination
 of 2 Properties with HasInfo or NoInfo.
 
 It type checked. But, type inference was busted. A simple expression
-like "foo `requires` bar" blew up:
+like `foo \`requires\` bar` blew up:
 
 	   No instance for (Requires (Property HasInfo) (Property HasInfo) r0)
 	      arising from a use of `requires'

format
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
index 46e59bc..ad3ac24 100644
--- a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -106,7 +106,7 @@ like "foo `requires` bar" blew up:
 	                 (Property HasInfo) (Property HasInfo) (Property HasInfo)
 	        -- Defined at Propellor/Types.hs:167:10
 
-To avoid that, it needed "(foo `requires` bar) :: Property HasInfo" --
+To avoid that, it needed `(foo \`requires\` bar) :: Property HasInfo` --
 I didn't want the user to need to write that.
 
 I got stuck here for an long time, well over a month.

blog update
diff --git a/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
new file mode 100644
index 0000000..46e59bc
--- /dev/null
+++ b/blog/entry/making_propellor_safer_with_GADTs_and_type_families.mdwn
@@ -0,0 +1,198 @@
+Since July, I have been aware of an ugly problem with propellor. Certain
+propellor configurations could have a bug. I've tried to solve the problem at
+least a half-dozen times without success; it's eaten several weekends. 
+
+Today I finally managed to fix propellor so it's impossible to write code that
+has the bug, bending the Haskell type checker to my will with the power of
+GADTs and type-level functions.
+
+## the bug
+
+Code with the bug looked innocuous enough. Something like this:
+
+[[!format haskell """
+foo :: Property
+foo = property "foo" $
+	unlessM (liftIO $ doesFileExist "/etc/foo") $ do
+		bar <- liftIO $ readFile "/etc/foo.template"
+		ensureProperty $ setupFoo bar
+"""]]
+
+The problem comes about because some properties in propellor have Info
+associated with them. This is used by propellor to introspect over the
+properties of a host, and do things like set up DNS, or decrypt
+private data used by the property.
+
+At the same time, it's useful to let a Property internally decide to
+run some other Property. In the example above, that's the `ensureProperty`
+line, and the `setupFoo` Property is run only sometimes, and is
+passed data that is read from the filesystem.
+
+This makes it very hard, indeed probably impossible for Propellor to
+look inside the monad, realize that `setupFoo` is being used, and add
+its Info to the host.
+
+Probably, `setupFoo` doesn't have Info associated with it -- most
+properties do not. But, it's hard to tell, when writing such a Property
+if it's safe to use ensureProperty. And worse, `setupFoo` could later
+be changed to have Info.
+
+Now, in most languages, once this problem was noticed, the solution would
+probably be to make `ensureProperty` notice when it's called on a Property
+that has Info, and print a warning message. That's Good Enough in a sense.
+
+But it also really stinks as a solution. It means that building propellor
+isn't good enough to know you have a working system; you have to let it
+run on each host, and watch out for warnings. Ugh, no!
+
+## the solution
+
+This screams for GADTs. (Well, it did once I learned how what GADTs are
+and what they can do.) 
+
+With GADTs, `Property NoInfo` and `Property HasInfo` can be separate data
+types. Most functions will work on either type (`Property i`) but
+`ensureProperty` can be limited to only accept a `Property NoInfo`.
+
+[[!format haskell """
+data Property i where
+	IProperty :: Desc -> ... -> Info -> Property HasInfo
+	SProperty :: Desc -> ... -> Property NoInfo
+
+data HasInfo
+data NoInfo
+
+ensureProperty :: Property NoInfo -> Propellor Result
+"""]]
+
+Then the type checker can detect the bug, and refuse to compile it.
+
+Yay!
+
+Except ...
+
+## Property combinators
+
+There are a lot of Property combinators in propellor. These combine
+two or more properties in various ways. The most basic one is `requires`,
+which only runs the first Property after the second one has successfully
+been met.
+
+So, what's it's type when used with GADT Property?
+
+[[!format haskell """
+requires :: Property i1 -> Property i2 -> Property ???
+"""]]
+
+It seemed I needed some kind of type class, to vary the return type.
+
+[[!format haskell """
+class Combine x y r where
+	requires :: x -> y -> r
+"""]]
+
+Now I was able to write 4 instances of `Combines`, for each combination
+of 2 Properties with HasInfo or NoInfo.
+
+It type checked. But, type inference was busted. A simple expression
+like "foo `requires` bar" blew up:
+
+	   No instance for (Requires (Property HasInfo) (Property HasInfo) r0)
+	      arising from a use of `requires'
+	    The type variable `r0' is ambiguous
+	    Possible fix: add a type signature that fixes these type variable(s)
+	    Note: there is a potential instance available:
+	      instance Requires
+	                 (Property HasInfo) (Property HasInfo) (Property HasInfo)
+	        -- Defined at Propellor/Types.hs:167:10
+
+To avoid that, it needed "(foo `requires` bar) :: Property HasInfo" --
+I didn't want the user to need to write that.
+
+I got stuck here for an long time, well over a month.
+
+## type level programming
+
+Finally today I realized that I could fix this with a little type-level
+programming.
+
+[[!format haskell """
+class Combine x y where
+	requires :: x -> y -> CombinedType x y
+"""]]
+
+Here `CombinedType` is a type-level function, that calculates the type that
+should be used for a combination of types x and y. This turns out to be really
+easy to do, once you get your head around type level functions.
+
+[[!format haskell """
+type family CInfo x y
+type instance CInfo HasInfo HasInfo = HasInfo
+type instance CInfo HasInfo NoInfo = HasInfo
+type instance CInfo NoInfo HasInfo = HasInfo
+type instance CInfo NoInfo NoInfo = NoInfo
+type family CombinedType x y
+type instance CombinedType (Property x) (Property y) = Property (CInfo x y)
+"""]]
+
+And, with that change, type inference worked again! \o/
+
+(Bonus: I added some more intances of CombinedType for combining
+things like RevertableProperties, so propellor's property
+combinators got more powerful too.)
+
+Then I just had to make a massive pass over all of Propellor,
+fixing the types of each Property to be Property NoInfo or Property HasInfo.
+I frequently picked the wrong one, but the type checker was able to detect 
+and tell me when I did.
+
+A few of the type signatures got slightly complicated, to provide the type
+checker with sufficient proof to do its thing...
+
+[[!format haskell """
+before :: (IsProp x, Combines y x, IsProp (CombinedType y x)) => x -> y -> CombinedType y x
+before x y = (y `requires` x) `describe` (propertyDesc x)
+
+onChange
+	:: (Combines (Property x) (Property y))
+	=> Property x
+	=> Property y
+	=> CombinedType (Property x) (Property y)
+onChange = -- 6 lines of code omitted
+
+fallback :: (Combines (Property p1) (Property p2)) => Property p1 -> Property p2 -> Property (CInfo p1 p2)
+fallback = -- 4 lines of code omitted
+"""]]
+
+.. This mostly happened in property combinators, which is an acceptable
+tradeoff, when you consider that the type checker is now being used to prove
+that propellor can't have this bug.
+
+Mostly, things went just fine. The only other annoying thing was that some
+things use a `[Property]`, and since a haskell list can only contain a
+single type, while Property Info and Property NoInfo are two different
+types, that needed to be dealt with. Happily, I was able to extend
+propellor's existing `(&)` and `(!)` operators to work in this situation,
+so a list can be constructed of properties of several different types:
+
+[[!format haskell """
+propertyList "foos" $ props
+	& foo
+	& foobar
+	! oldfoo	
+"""]]
+
+## conclusion
+
+The resulting 4000 lines of changes will be in the next release of
+propellor. Just as soon as I test that it always generates the same Info
+as before, and perhaps works when I run it. (eep)
+
+These uses of GADTs and type families are not new; this is merely the first
+time I used them. It's another Haskell leveling up for me. 
+
+Anytime you can identify a class of bugs that can impact a complicated code

(Diff truncated)
add news item for moreutils 0.55
diff --git a/code/moreutils/news/version_0.49.mdwn b/code/moreutils/news/version_0.49.mdwn
deleted file mode 100644
index e507d68..0000000
--- a/code/moreutils/news/version_0.49.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-moreutils 0.49 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * ts: Fix timezone used with -i
-   * errno, isutf8: Fix zero-termination of option list, which could lead
-     to getopt crash. Closes: #[715867](http://bugs.debian.org/715867)
-     (Thanks, Mayhem Team)"""]]
\ No newline at end of file
diff --git a/code/moreutils/news/version_0.55.mdwn b/code/moreutils/news/version_0.55.mdwn
new file mode 100644
index 0000000..dfe7d60
--- /dev/null
+++ b/code/moreutils/news/version_0.55.mdwn
@@ -0,0 +1,6 @@
+moreutils 0.55 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * parallel: serialise output through internal pipe. Closes: #[704125](http://bugs.debian.org/704125)
+     Thanks, Nicolas Schier.
+   * sponge: add append option '-a'. Closes: #[623197](http://bugs.debian.org/623197)
+     Thanks, Nicolas Schier."""]]
\ No newline at end of file

correction
diff --git a/code/bin.mdwn b/code/bin.mdwn
index 31b7669..a4ff3e5 100644
--- a/code/bin.mdwn
+++ b/code/bin.mdwn
@@ -42,7 +42,7 @@ This is the script that I use for releasing packages.
 ## wikiannounce
 
 This is a script that release calls, which converts a changelog into
-a news item for a package on this wiki.
+a news item for a package on its wiki.
 
 ## foto
 

more
diff --git a/code/bin.mdwn b/code/bin.mdwn
index cdd6dab..31b7669 100644
--- a/code/bin.mdwn
+++ b/code/bin.mdwn
@@ -12,6 +12,12 @@ there still.
 
 You can browse my bin at <http://git.kitenet.net/?p=joey/home.git;a=tree;f=bin>.
 
+## async
+
+Passed a mount point and a command, it remounts the drive at that point
+async, runs the command, and then remounts it back sync. Useful with
+external drives that are mounted sync by default for safety.
+
 ## blog
 
 This brings up a text editor for writing a
@@ -38,12 +44,10 @@ This is the script that I use for releasing packages.
 This is a script that release calls, which converts a changelog into
 a news item for a package on this wiki.
 
-## dq
-
-This is my Download Queue program. Uses rsync to synchronise files, very
-handy if you're on a slow modem link.
+## foto
 
-(Now obsoleted by [[code/git-annex]])
+Pulls photos off a camera. Either from a DCIM directory of a mounted SD
+card, or using gphoto2 over USB. The photos are added to a git-annex repo.
 
 ## loadcron
 
@@ -55,6 +59,11 @@ Supports some handy features.
 Given a list of architectures on command line or stdin, 
 outputs any of the released debian arches not in the list
 
+## lifeday
+
+Prints out the number of days since I was born. Useful for a different
+perspective.
+
 ## logview
 
 What I use for tailing logfiles. Determines dynamically what files are
@@ -67,11 +76,20 @@ which sucks on 3 line xterms).
 Sshs into a machine and starts a screen session. Auto-reconnects.
 Goto considered wonderful!
 
-## scancd
+## out
 
-I keep a file that records all the files on archival CDs that I burn; this
-updates the file. Uses vim folding markers to make it easy to browse the
-file in vim; the file is also always stored compressed. (It's big.)
+For use in a git-annex repository containing files that are being consumed
+in some way (eg, videos or podcasts), where I want to keep a copy of the
+file but get it out of the way when done with it. Moves specified files to
+some "out" directory located inside the repository, and has git-annex drop
+their content if possible.
+
+## rec
+
+Simple sound recorder. Streams the mic to a fifo and in the background,
+encodes it to an ogg file. This can be easily started up, and runs until
+ctrl-C'd. Then the finished ogg file is stashed away in a git-annex
+repository.
 
 ## sunrise
 
@@ -85,3 +103,10 @@ It does have to have LAT/LONG hardcoded into it though.
 This makes su actually run the the sudo command, if sudo is present. Nice
 to not have to type the password every time when suing, although I wrote it
 before I was aware of "`sudo su`".
+
+## z
+
+Blocks suspend on lid close while a command runs. Useful for eg, listening
+to a podcast, or keeping the laptop running until a download completes.
+
+Requires some systemd setup documented in comments.

update
diff --git a/code/bin.mdwn b/code/bin.mdwn
index ab0f31f..cdd6dab 100644
--- a/code/bin.mdwn
+++ b/code/bin.mdwn
@@ -22,20 +22,20 @@ with pyblosxom instead.
 
 ## build
 
-This is the script that I use for building Debian packages.
+This is the script that I use for building packages.
 
 ## review
 
-This is the script I use for running lintian and other checks on a Debian
+This is the script I use for running lintian and other checks on a
 package I've built. It displays all sorts of useful info in a pager.
 
 ## release
 
-This is the script that I use for releasing Debian packages.
+This is the script that I use for releasing packages.
 
 ## wikiannounce
 
-This is a script that release calls, which converts a debian changelog into
+This is a script that release calls, which converts a changelog into
 a news item for a package on this wiki.
 
 ## dq

reorg
diff --git a/code.mdwn b/code.mdwn
index 1701230..a624f5c 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -28,35 +28,42 @@ In maintenance mode mostly, but I still have my hands in it somewhat.
 
 ## Past projects
 
-I'm no longer working on these projects.
+I'm no longer working on these projects, but thankfully, other
+people have taken them on.
 
 [[Debian]]
 [[debian-installer]]
 [[debhelper]]
 [[tasksel]]
-[[debmirror]]
-[[shoop]]
-[[apt-src]]
-[[pentium-builder]]
-[[perlmoo]]
-[[TermStool]]
-[[base-config]]
 [[flashybrid]]
-[[satutils]]
-[[mooix]]
 [[wmbattery]]
-[[sleepd]]
-[[pristine-tar]]
-[[alien]]
 [[debconf]]
 [[dpkg-repack]]
 [[os-prober]]
 [[devscripts]]
+[[pentium-builder]]
+[[apt-src]]
+
+These need new maintainers, stat!
+
+[[debmirror]]
+[[sleepd]]
+[[pristine-tar]]
+[[alien]]
 [[jetring]]
 [[nslu2-utils]]
-[[rootsync]]
 [[ticker]]
 
+These are dead and buried.
+
+[[shoop]]
+[[perlmoo]]
+[[TermStool]]
+[[base-config]]
+[[satutils]]
+[[mooix]]
+[[rootsync]]
+
 ## Misc
 
 Other stuff..
diff --git a/code/debian-installer.mdwn b/code/debian-installer.mdwn
index 5573352..dcf0905 100644
--- a/code/debian-installer.mdwn
+++ b/code/debian-installer.mdwn
@@ -8,5 +8,5 @@ Its website is formally <http://www.debian.org/devel/debian-installer>, but
 <http://wiki.debian.org/DebianInstaller> is perhaps more interesting.
 
 The [[dilab]] was my own rack of d-i test
-machines. This runs on `digress`, the d-i regression suite, which is the
+machines. This ran on `digress`, the d-i regression suite, which is the
 most obscure part of d-i I know of, and which I wrote.

update re oprhanings and etc
diff --git a/code.mdwn b/code.mdwn
index a331b58..1701230 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -28,12 +28,7 @@ In maintenance mode mostly, but I still have my hands in it somewhat.
 
 ## Past projects
 
-Still theoretically maintained by me, but new maintainers needed for
-continued life:
-
-[[ticker]]
-
-Passed on to others, or dead and buried:
+I'm no longer working on these projects.
 
 [[Debian]]
 [[debian-installer]]
@@ -60,6 +55,7 @@ Passed on to others, or dead and buried:
 [[jetring]]
 [[nslu2-utils]]
 [[rootsync]]
+[[ticker]]
 
 ## Misc
 
diff --git a/code/debhelper.mdwn b/code/debhelper.mdwn
index 75feb55..b0dabf3 100644
--- a/code/debhelper.mdwn
+++ b/code/debhelper.mdwn
@@ -29,9 +29,7 @@ maintaining support for said obscure thing.
 
 My first rule of thumb is that at least ten packages need to be obvious
 benefactors of a feature or command before it's added to debhelper. That's
-an easy bar; after that I have to do some thinking and made the hard call.
-
-TODO: discuss wishlisted commands here
+an easy bar; after that I have to do some thinking and make the hard call.
 
 [[!sidebar content="""
 <a href="http://flattr.com/thing/39941/Debhelper" target="_blank">
diff --git a/code/debhelper/news/no_longer_maintaining_debhelper.mdwn b/code/debhelper/news/no_longer_maintaining_debhelper.mdwn
new file mode 100644
index 0000000..46ba21b
--- /dev/null
+++ b/code/debhelper/news/no_longer_maintaining_debhelper.mdwn
@@ -0,0 +1,2 @@
+I've passed maintanence for debhelper on to others. This page is provided
+only for historical reasons.

orphaned ailen
diff --git a/code/alien/news/no_longer_maintaining_alien.mdwn b/code/alien/news/no_longer_maintaining_alien.mdwn
new file mode 100644
index 0000000..692aa0d
--- /dev/null
+++ b/code/alien/news/no_longer_maintaining_alien.mdwn
@@ -0,0 +1 @@
+I am no longer maintaining alien. A new maintainer is wanted.

mooix archive link etc
diff --git a/code/mooix.mdwn b/code/mooix.mdwn
index ede096d..1375e85 100644
--- a/code/mooix.mdwn
+++ b/code/mooix.mdwn
@@ -1,9 +1,13 @@
 Mooix is a MOO that is implemented on top of unix and which can be
-programmed online in any language. Its website is <http://mooix.net/>.
+programmed online in any language.
 
-I'm not currently using or developing mooix; a few other people make
-commits from time to time. I have some [[grandiose_plans|ng]] for the next
-generation of mooix, which I might get around to fleshing out some day.
+I stopped developing mooix around 2001; a few other people made
+commits from time to time for the next decade, but now it is done.
+
+I have some [[grandiose_plans|ng]] for the next generation of mooix,
+which I might get around to fleshing out some day, but probably not.
 
 Some thoughts on mooix in my
 [[blog|blog/entry/ten_years_of_free_software_--_part_17_mooix]].
+
+Mooix is archived: <https://archive.org/details/MooixArchive>

Added a comment: bug in my ear
diff --git a/blog/entry/a_bug_in_my_ear/comment_7_a4ea38bfa03050cfb39bb084a247eb85._comment b/blog/entry/a_bug_in_my_ear/comment_7_a4ea38bfa03050cfb39bb084a247eb85._comment
new file mode 100644
index 0000000..860ae44
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_7_a4ea38bfa03050cfb39bb084a247eb85._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawkjqspuRFhqOXA8hoESjDm0I45gYL9v9Lk"
+ nickname="Peter"
+ subject="bug in my ear"
+ date="2015-01-06T10:31:28Z"
+ content="""
+Never EVER use a cotton bud!
+1. Your parents probably taught you that and it's really bad.
+2. Cotton buds generally fill the ear canal and then like loading a cannon (That's apt?) push all that crap further down your ear like a ram rod.
+
+Some people use hair grips a bit like a scraper but you need to be dead careful not to damage the ear drum. Best option warm olive oil and pour that in your lughole (Urban Dictionary) after a couple of minutes drain it out. The warmer the better to melt any minging ear wax. :-)
+"""]]

Added a comment: RC?
diff --git a/blog/entry/a_bug_in_my_ear/comment_6_2c4011193d9dd2497ba6aea159bb237c._comment b/blog/entry/a_bug_in_my_ear/comment_6_2c4011193d9dd2497ba6aea159bb237c._comment
new file mode 100644
index 0000000..2d70802
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_6_2c4011193d9dd2497ba6aea159bb237c._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://www.joachim-breitner.de/"
+ nickname="nomeata"
+ subject="RC?"
+ date="2015-01-06T10:00:52Z"
+ content="""
+So you are saying that while this bug was not critical for the release, the release of this bug was critical for you?
+"""]]

tag
diff --git a/blog/entry/hledger.mdwn b/blog/entry/hledger.mdwn
index 85d8ae3..324f48c 100644
--- a/blog/entry/hledger.mdwn
+++ b/blog/entry/hledger.mdwn
@@ -221,3 +221,5 @@ Of course, a Haskell programmer can skip the monads entirely, or use others
 they prefer. And could do arbitrarily complicated stuff during imports,
 including building split transactions, and combining together multiple
 related CVS lines into a single transaction.
+
+[[!tag haskell]]

foo
diff --git a/blog/entry/a_bug_in_my_ear/comment_5_e62e8977fc1f67c4d44654b483c30adb._comment b/blog/entry/a_bug_in_my_ear/comment_5_e62e8977fc1f67c4d44654b483c30adb._comment
new file mode 100644
index 0000000..66de52e
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_5_e62e8977fc1f67c4d44654b483c30adb._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 5"""
+ date="2015-01-06T05:29:38Z"
+ content="""
+I loled at the bug report comment!
+
+JFTR, the cue-tips I used are the big-ended safe kind that intentionally cannot
+be jammed all the way down the canal to the eardrum.
+
+A few years ago, I had a strange earache, that came on without any swimming
+or other activity that might lead to an ear infection. Now I'm wondering if
+that one was also caused by some little bug, perhaps while I was asleep.
+
+Ear canals.. what was evolution thinking?
+"""]]

Added a comment
diff --git a/blog/entry/a_bug_in_my_ear/comment_4_b7d72d7147ddb255492007389e09f1da._comment b/blog/entry/a_bug_in_my_ear/comment_4_b7d72d7147ddb255492007389e09f1da._comment
new file mode 100644
index 0000000..80af23a
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_4_b7d72d7147ddb255492007389e09f1da._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawndhZ128v4P20FCgptVRqrP0bOwPpi2EQA"
+ nickname="Patrick"
+ subject="comment 4"
+ date="2015-01-06T03:20:08Z"
+ content="""
+Happened to me when I was around 10 years old.  I remember thinking it was going to flap in there forever.
+"""]]

Added a comment
diff --git a/blog/entry/a_bug_in_my_ear/comment_3_fa24ff02463a7396ef768cdff7e5d04c._comment b/blog/entry/a_bug_in_my_ear/comment_3_fa24ff02463a7396ef768cdff7e5d04c._comment
new file mode 100644
index 0000000..e7d9dd6
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_3_fa24ff02463a7396ef768cdff7e5d04c._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawlYPDgJVIC9eiG2E7I480vFHmmWXn0Nauc"
+ nickname="Piruthiviraj"
+ subject="comment 3"
+ date="2015-01-06T03:05:15Z"
+ content="""
+People, never try earbuds to remove the living foreign body from the ear. First stop panicking! Rest the head on a bed and always use fluids eg. lukewarm water to fill the ear canal and usually after few mins, bugs usually comes out or at least dies and helps in easier extraction at the hospital.
+If you have been diagnosed to have previous ear infection or a persistant ear perforation, always seek medical help, before manipulating yourself.
+The more the manipulation the more likely the bug or the earbud is going to injure the cavity and later leading a ear infection.
+I am an ER Physcian and hence you can depend on the info.
+"""]]

Added a comment
diff --git a/blog/entry/a_bug_in_my_ear/comment_2_0f4d749540e955b707a07f57284db9ca._comment b/blog/entry/a_bug_in_my_ear/comment_2_0f4d749540e955b707a07f57284db9ca._comment
new file mode 100644
index 0000000..44039f1
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_2_0f4d749540e955b707a07f57284db9ca._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawn8AYPzWP-WL-z-EOr_EBp7Kd44DxEYNxE"
+ nickname="Benjamin"
+ subject="comment 2"
+ date="2015-01-06T02:23:54Z"
+ content="""
+The exact same thing happened to my girlfriend about 10 years ago. She treated it with ear-drops just like you did, and she doesn't suffer any drawback except from being afraid by those kind of bugs. So my guess is that you should be alright too :-)
+"""]]

removed
diff --git a/blog/entry/a_bug_in_my_ear/comment_2_5872742ed8e7cce48993cbbfb923d7f4._comment b/blog/entry/a_bug_in_my_ear/comment_2_5872742ed8e7cce48993cbbfb923d7f4._comment
deleted file mode 100644
index ca03ca7..0000000
--- a/blog/entry/a_bug_in_my_ear/comment_2_5872742ed8e7cce48993cbbfb923d7f4._comment
+++ /dev/null
@@ -1,7 +0,0 @@
-[[!comment format=mdwn
- username="http://bret.io/"
- subject="comment 2"
- date="2015-01-06T01:58:58Z"
- content="""
-Thanks for your bug report.  ;)
-"""]]

Added a comment
diff --git a/blog/entry/a_bug_in_my_ear/comment_2_5872742ed8e7cce48993cbbfb923d7f4._comment b/blog/entry/a_bug_in_my_ear/comment_2_5872742ed8e7cce48993cbbfb923d7f4._comment
new file mode 100644
index 0000000..ca03ca7
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_2_5872742ed8e7cce48993cbbfb923d7f4._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="http://bret.io/"
+ subject="comment 2"
+ date="2015-01-06T01:58:58Z"
+ content="""
+Thanks for your bug report.  ;)
+"""]]

Added a comment
diff --git a/blog/entry/a_bug_in_my_ear/comment_1_51853f770fe2e217878b582dbab3e701._comment b/blog/entry/a_bug_in_my_ear/comment_1_51853f770fe2e217878b582dbab3e701._comment
new file mode 100644
index 0000000..d205f08
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear/comment_1_51853f770fe2e217878b582dbab3e701._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="http://bret.io/"
+ subject="comment 1"
+ date="2015-01-06T01:58:50Z"
+ content="""
+Thanks for your bug report.  ;)
+"""]]

blog update
diff --git a/blog/entry/a_bug_in_my_ear.mdwn b/blog/entry/a_bug_in_my_ear.mdwn
new file mode 100644
index 0000000..1b6e171
--- /dev/null
+++ b/blog/entry/a_bug_in_my_ear.mdwn
@@ -0,0 +1,18 @@
+True story: Two days ago, as I was about to drift off to sleep at 2 am, a tiny
+little bug flew into my ear. Right down to my eardrum, which it fluttered
+against with its wings.
+
+It was a tiny little moth-like bug, the kind you don't want to find in a bag of
+flour, and it had been beating against my laptop screen a few minutes before.
+
+This went on for 20 minutes, in which I failed to get it out with a cue tip and
+shaking my head. It is very weird to have a bug flapping in your head.
+
+I finally gave up and put in eardrops, and stopped the poor thing flapping. I
+happen to know these little creatures mass almost nothing, and rapidly break up
+into nearly powder when dead. So while I've not had any bug bits come out, I'm
+going by the way my ear felt a little stopped up yesterday, and just fine
+today, and guessing it'll be ok. Oh, and I've been soaking it in the tub and
+putting in eardrops for good measure.
+
+If I've seemed a little distracted lately, now you know why!

calendar update
diff --git a/blog/archives/2015.mdwn b/blog/archives/2015.mdwn
new file mode 100644
index 0000000..801aa04
--- /dev/null
+++ b/blog/archives/2015.mdwn
@@ -0,0 +1 @@
+[[!calendar type=year year=2015 pages="blog/entry/* and !*/Discussion"]]
diff --git a/blog/archives/2015/01.mdwn b/blog/archives/2015/01.mdwn
new file mode 100644
index 0000000..55e62f8
--- /dev/null
+++ b/blog/archives/2015/01.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=01 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(01) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/02.mdwn b/blog/archives/2015/02.mdwn
new file mode 100644
index 0000000..47ca1c8
--- /dev/null
+++ b/blog/archives/2015/02.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=02 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(02) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/03.mdwn b/blog/archives/2015/03.mdwn
new file mode 100644
index 0000000..6e9a783
--- /dev/null
+++ b/blog/archives/2015/03.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=03 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(03) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/04.mdwn b/blog/archives/2015/04.mdwn
new file mode 100644
index 0000000..05a8508
--- /dev/null
+++ b/blog/archives/2015/04.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=04 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(04) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/05.mdwn b/blog/archives/2015/05.mdwn
new file mode 100644
index 0000000..775c0e1
--- /dev/null
+++ b/blog/archives/2015/05.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=05 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(05) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/06.mdwn b/blog/archives/2015/06.mdwn
new file mode 100644
index 0000000..5bea3ab
--- /dev/null
+++ b/blog/archives/2015/06.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=06 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(06) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/07.mdwn b/blog/archives/2015/07.mdwn
new file mode 100644
index 0000000..6e1a33f
--- /dev/null
+++ b/blog/archives/2015/07.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=07 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(07) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/08.mdwn b/blog/archives/2015/08.mdwn
new file mode 100644
index 0000000..cfafb75
--- /dev/null
+++ b/blog/archives/2015/08.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=08 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(08) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/09.mdwn b/blog/archives/2015/09.mdwn
new file mode 100644
index 0000000..68e4d24
--- /dev/null
+++ b/blog/archives/2015/09.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=09 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(09) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/10.mdwn b/blog/archives/2015/10.mdwn
new file mode 100644
index 0000000..01c1f46
--- /dev/null
+++ b/blog/archives/2015/10.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=10 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(10) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/11.mdwn b/blog/archives/2015/11.mdwn
new file mode 100644
index 0000000..3f5f6e2
--- /dev/null
+++ b/blog/archives/2015/11.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=11 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(11) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2015/12.mdwn b/blog/archives/2015/12.mdwn
new file mode 100644
index 0000000..6ca3ef0
--- /dev/null
+++ b/blog/archives/2015/12.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=12 year=2015 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(12) and creation_year(2015) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]

update with gpg key
not currently using jabber
diff --git a/contact.mdwn b/contact.mdwn
index 79d9fee..1d7d141 100644
--- a/contact.mdwn
+++ b/contact.mdwn
@@ -1,4 +1,4 @@
-* email: <id@joeyh.name> or <joey@kitenet.net> or <joeyh@debian.org>
-* jabber: [joey@kitenet.net](xmpp:joey@kitenet.net)
+* email: <id@joeyh.name>
+* gpg key: [E85A 5F63 B31D 24C1 EBF0  D81C C910 D922 2512 E3C7](http://pgp.cs.uu.nl/stats/2512E3C7.html)
 * identi.ca: <http://identi.ca/joeyh>
 * some ways to send me a "thank you" for my work, if you're so inclined: [[thanks]]

cia is missed
diff --git a/code.mdwn b/code.mdwn
index ccdab05..a331b58 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -72,7 +72,7 @@ Other stuff..
 [[sigprogs]]
 
 If you want to keep up with new projects, [[WhatsNew]] is a feed of any new
-pages I add to this site, and [[grep]] aggregates my recent commits.
+pages I add to this site.
 
 Also, [Ohloh](http://www.ohloh.net/accounts/6835) tracks and mines my commits to many projects 
 for interesting metrics.

update
diff --git a/code/sleepd.mdwn b/code/sleepd.mdwn
index 7faeae8..e9d9bb3 100644
--- a/code/sleepd.mdwn
+++ b/code/sleepd.mdwn
@@ -7,6 +7,8 @@ must be used to actually put the system to sleep.
 There is a Debian `sleepd` package, or you can download it from git
 (`git://git.kitenet.net/sleepd`).
 
+I am not longer maintaining sleepd.
+
 ## News
 
 [[!inline pages="code/sleepd/news/* and !*/Discussion" show="3"]]

add shell-monad page
diff --git a/blog/entry/shell_monad.mdwn b/blog/entry/shell_monad.mdwn
index 1f4d5d7..7ccf112 100644
--- a/blog/entry/shell_monad.mdwn
+++ b/blog/entry/shell_monad.mdwn
@@ -75,3 +75,5 @@ There's a lot of things that could be added to this library
 (`if`, `while`, redirection, etc), but I can already see using
 it in various parts of propellor and git-annex that need to generate
 shell code.
+
+[[!tag code/shell-monad]]
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index b1e1ec1..2fde83b 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -229,3 +229,4 @@ all 99 lines of it.
 	set -p; _handler () { :; 	_v=; 	read _v; 	case "${_v%%\ *}" in FOO) :; 		echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2; 	: ;; BAR) :; 		echo 'yay, I got a Bar' >&2; 	: ;; BAZ) :; 		for _x in $(seq 1 "${_v#[!\ ]*[\ ]}"); 		do :; 			echo 'yay, I got a Baz' "$_x" >&2; 		done; 	: ;; *) :; 		echo 'unknown protocol command' "${_v%%\ *}" >&2; 		false; 	: ;; esac; }; echo 'FOO starting up'; _handler; echo 'BAR '; _handler
 
 [[!tag haskell]]
+[[!tag code/shell-monad]]
diff --git a/code.mdwn b/code.mdwn
index bd7835a..ccdab05 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -13,6 +13,7 @@ The stuff that's swapped into my local cache at the moment.
 [[moreutils]]
 [[ikiwiki-hosting]]
 [[github-backup]]
+[[shell-monad]]
 
 ## Less active projects
 
@@ -23,7 +24,6 @@ In maintenance mode mostly, but I still have my hands in it somewhat.
 [[pdmenu]]
 [[filters]]
 [electrum-mnemonic](http://hackage.haskell.org/package/electrum-mnemonic)
-[shell-monad](http://hackage.haskell.org/package/shell-monad)
 [brainfuck-monad](http://hackage.haskell.org/package/brainfuck-monad)
 
 ## Past projects
diff --git a/code/shell-monad.mdwn b/code/shell-monad.mdwn
new file mode 100644
index 0000000..2c89bf5
--- /dev/null
+++ b/code/shell-monad.mdwn
@@ -0,0 +1,3 @@
+Haskell DSL that generates shell scripts.
+
+[shell-monad](http://hackage.haskell.org/package/shell-monad)

tagfix
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index f6aebc4..b1e1ec1 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -228,4 +228,4 @@ all 99 lines of it.
 	
 	set -p; _handler () { :; 	_v=; 	read _v; 	case "${_v%%\ *}" in FOO) :; 		echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2; 	: ;; BAR) :; 		echo 'yay, I got a Bar' >&2; 	: ;; BAZ) :; 		for _x in $(seq 1 "${_v#[!\ ]*[\ ]}"); 		do :; 			echo 'yay, I got a Baz' "$_x" >&2; 		done; 	: ;; *) :; 		echo 'unknown protocol command' "${_v%%\ *}" >&2; 		false; 	: ;; esac; }; echo 'FOO starting up'; _handler; echo 'BAR '; _handler
 
-[[!meta tag=haskell]]
+[[!tag haskell]]

re-rendered code after a bug fix
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index e0f2a64..f6aebc4 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -207,17 +207,17 @@ all 99 lines of it.
 		read _v
 		case "${_v%%\ *}" in FOO) :
 			echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2
-		;; BAR) :
+		: ;; BAR) :
 			echo 'yay, I got a Bar' >&2
-		;; BAZ) :
+		: ;; BAZ) :
 			for _x in $(seq 1 "${_v#[!\ ]*[\ ]}")
 			do :
 				echo 'yay, I got a Baz' "$_x" >&2
 			done
-		;; *) :
+		: ;; *) :
 			echo 'unknown protocol command' "${_v%%\ *}" >&2
 			false
-		;; esac
+		: ;; esac
 	}
 	echo 'FOO starting up'
 	_handler
@@ -226,6 +226,6 @@ all 99 lines of it.
 	
 ## the one-liner shell program
 	
-	set -p; _handler () { :; 	_v=; 	read _v; 	case "${_v%%\ *}" in FOO) :; 		echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2; 	;; BAR) :; 		echo 'yay, I got a Bar' >&2; 	;; BAZ) :; 		for _x in $(seq 1 "${_v#[!\ ]*[\ ]}"); 		do :; 			echo 'yay, I got a Baz' "$_x" >&2; 		done; 	;; *) :; 		echo 'unknown protocol command' "${_v%%\ *}" >&2; 		false; 	;; esac; }; echo 'FOO starting up'; _handler; echo 'BAR '; _handler
+	set -p; _handler () { :; 	_v=; 	read _v; 	case "${_v%%\ *}" in FOO) :; 		echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2; 	: ;; BAR) :; 		echo 'yay, I got a Bar' >&2; 	: ;; BAZ) :; 		for _x in $(seq 1 "${_v#[!\ ]*[\ ]}"); 		do :; 			echo 'yay, I got a Baz' "$_x" >&2; 		done; 	: ;; *) :; 		echo 'unknown protocol command' "${_v%%\ *}" >&2; 		false; 	: ;; esac; }; echo 'FOO starting up'; _handler; echo 'BAR '; _handler
 
 [[!meta tag=haskell]]

tag
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index 9ae14d3..e0f2a64 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -227,3 +227,5 @@ all 99 lines of it.
 ## the one-liner shell program
 	
 	set -p; _handler () { :; 	_v=; 	read _v; 	case "${_v%%\ *}" in FOO) :; 		echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2; 	;; BAR) :; 		echo 'yay, I got a Bar' >&2; 	;; BAZ) :; 		for _x in $(seq 1 "${_v#[!\ ]*[\ ]}"); 		do :; 			echo 'yay, I got a Baz' "$_x" >&2; 		done; 	;; *) :; 		echo 'unknown protocol command' "${_v%%\ *}" >&2; 		false; 	;; esac; }; echo 'FOO starting up'; _handler; echo 'BAR '; _handler
+
+[[!meta tag=haskell]]

link
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index f0f687f..9ae14d3 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -193,6 +193,11 @@ javascript.
 I'm getting the feeling that I won't be writing too many nontrivial shell
 scripts by hand anymore! :)
 
+## the complete haskell program
+
+[Is here](http://git.joeyh.name/?p=shell-monad.git;a=blob;f=examples/protocol.hs;h=23a7c83d777b0c69e1a3f9ee6079f394d89cc561;hb=925b5fefe0a4c7b8e1fa5b56b90b26317adafebd),
+all 99 lines of it.
+
 ## the pretty-printed shell program
 
 	#!/bin/sh

foo
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index a7f1830..f0f687f 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -76,9 +76,10 @@ speaks the protocol in arbitrary ways. For example:
 		resp <- protoExchangeIO (Foo "starting up")
 		-- etc
 
-But that's trivial and uninteresting. The interesting part is making the
-shell program speak the protocol, including doing various things when it
-receives the commands.
+But that's trivial and uninteresting. Anyone who has read to here certianly
+knows how to write haskell code in the IO monad. The interesting part is
+making the shell program speak the protocol, including doing various things
+when it receives the commands.
 
 	foo :: Script ()
 	foo = do

foo
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index ab72b3a..a7f1830 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -58,7 +58,8 @@ protocol:
 
 While the IO version reads and deserializes a line back to a Proto,
 the shell script version of this returns a Var, which has the newly
-read line in it, not yet deserialized.
+read line in it, not yet deserialized. Why the difference? Well, Haskell
+has data types, and shell does not ...
 
 ## speaking the protocol
 

indent
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
index 3dc7351..ab72b3a 100644
--- a/blog/entry/shell_monad_day_3.mdwn
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -193,29 +193,29 @@ scripts by hand anymore! :)
 
 ## the pretty-printed shell program
 
-#!/bin/sh
-set -x
-_handler () { :
-	_v=
-	read _v
-	case "${_v%%\ *}" in FOO) :
-		echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2
-	;; BAR) :
-		echo 'yay, I got a Bar' >&2
-	;; BAZ) :
-		for _x in $(seq 1 "${_v#[!\ ]*[\ ]}")
-		do :
-			echo 'yay, I got a Baz' "$_x" >&2
-		done
-	;; *) :
-		echo 'unknown protocol command' "${_v%%\ *}" >&2
-		false
-	;; esac
-}
-echo 'FOO starting up'
-_handler
-echo 'BAR '
-_handler
+	#!/bin/sh
+	set -x
+	_handler () { :
+		_v=
+		read _v
+		case "${_v%%\ *}" in FOO) :
+			echo 'yay, I got a Foo' "${_v#[!\ ]*[\ ]}" >&2
+		;; BAR) :
+			echo 'yay, I got a Bar' >&2
+		;; BAZ) :
+			for _x in $(seq 1 "${_v#[!\ ]*[\ ]}")
+			do :
+				echo 'yay, I got a Baz' "$_x" >&2
+			done
+		;; *) :
+			echo 'unknown protocol command' "${_v%%\ *}" >&2
+			false
+		;; esac
+	}
+	echo 'FOO starting up'
+	_handler
+	echo 'BAR '
+	_handler
 	
 ## the one-liner shell program
 	

blog update
diff --git a/blog/entry/shell_monad_day_3.mdwn b/blog/entry/shell_monad_day_3.mdwn
new file mode 100644
index 0000000..3dc7351
--- /dev/null
+++ b/blog/entry/shell_monad_day_3.mdwn
@@ -0,0 +1,222 @@
+I have been hard at work on the
+[shell-monad](http://hackage.haskell.org/package/shell-monad/)
+ever since it was born on Christmas Eve. It's now up to 820 lines of code,
+and has nearly comprehensive coverage of all shell features.
+
+Time to use it for something interesting! Let's make a shell script and a
+haskell program that both speak a simple protocol. This kind of thing could
+be used by [[code/propellor]] when it's deploying itself to a new host. The
+haskell program can ssh to a remote host and run the shell program, and
+talk back and forth over stdio with it, using the protocol they both speak.
+
+[[!toc]]
+
+## abstract beginnings
+
+First, we'll write a data type for the commands in the protocol.
+
+	data Proto
+		= Foo String
+		| Bar
+		| Baz Integer
+		deriving (Show)
+
+Now, let's go type class crazy! 
+
+	class Monad t => OutputsProto t where
+		output :: Proto -> t ()
+	
+	instance OutputsProto IO where
+		output = putStrLn . fromProto
+	
+So far, nothing interesting; this makes the IO monad an instance of the
+`OutputsProto` type class, and gives a simple implementation to output a
+line of the protocol.
+
+	instance OutputsProto Script where
+		output = cmd "echo" . fromProto
+
+Now it gets interesting. The Script monad is now also a member of the
+`OutputsProto`. To output a line of the protocol, it just uses echo.
+Yeah -- shell code is a member of a haskell type class. Awesome -- most
+abstract shell code evar!
+
+Similarly, we can add another type class for monads that can input the
+protocol:
+
+	class Monad t => InputsProto t p where
+		input :: t p
+	
+	instance InputsProto IO Proto where
+		input = toProto <$> readLn
+	
+	instance InputsProto Script Var where
+		input = do
+			v <- newVar ()
+			readVar v
+			return v
+
+While the IO version reads and deserializes a line back to a Proto,
+the shell script version of this returns a Var, which has the newly
+read line in it, not yet deserialized.
+
+## speaking the protocol
+
+Now we have enough groundwork to write haskell code in the IO monad that
+speaks the protocol in arbitrary ways. For example:
+
+	protoExchangeIO :: Proto -> IO Proto
+	protoExchangeIO p = do
+		output p
+		input
+	
+	fooIO :: IO ()
+	fooIO = do
+		resp <- protoExchangeIO (Foo "starting up")
+		-- etc
+
+But that's trivial and uninteresting. The interesting part is making the
+shell program speak the protocol, including doing various things when it
+receives the commands.
+
+	foo :: Script ()
+	foo = do
+		stopOnFailure True
+		handler <- func (NamedLike "handler") $
+			handleProto =<< input
+		output (Foo "starting up")
+		handler
+		output Bar
+		handler
+	
+	handleFoo :: Var -> Script ()
+	handleFoo v = toStderr $ cmd "echo" "yay, I got a Foo" v
+	
+	handleBar :: Script ()
+	handleBar = toStderr $ cmd "echo" "yay, I got a Bar"
+	
+	handleBaz :: Var -> Script ()
+	handleBaz num = forCmd (cmd "seq" (Val (1 :: Int)) num) $
+		toStderr . cmd "echo" "yay, I got a Baz"
+
+## serialization
+
+I've left out a few serialization functions. `fromProto` is used in both
+instances of OutputsProto. The haskell program and the script will both use
+this to serialize Proto.
+
+	fromProto :: Proto -> String
+	fromProto (Foo s) = pFOO ++ " " ++ s
+	fromProto Bar = pBAR ++ " "
+	fromProto (Baz i) = pBAZ ++ " " ++ show i
+	
+	pFOO, pBAR, pBAZ :: String
+	(pFOO, pBAR, pBAZ) = ("FOO", "BAR", "BAZ")
+
+And here's the haskell function to convert the other direction, which was also
+used earlier.
+
+	toProto :: String -> Proto
+	toProto s = case break (== ' ') s of
+		(w, ' ':rest)
+			| w == pFOO -> Foo rest
+			| w == pBAR && null rest -> Bar
+			| w == pBAZ -> Baz (read rest)
+			| otherwise -> error $ "unknown protocol command: " ++ w
+		(_, _) -> error "protocol splitting error"
+
+We also need a version of that written in the Script monad. Here it is.
+Compare and contrast the function below with the one above. They're
+really quite similar. (Sadly, not so similar to allow refactoring out a common
+function..)
+
+	handleProto :: Var -> Script ()
+	handleProto v = do
+		w <- getProtoCommand v
+		let rest = getProtoRest v
+		caseOf w
+			[ (quote (T.pack pFOO), handleFoo =<< rest)
+			, (quote (T.pack pBAR), handleBar)
+			, (quote (T.pack pBAZ), handleBaz =<< rest)
+			, (glob "*", do
+				toStderr $ cmd "echo" "unknown protocol command" w
+				cmd "false"
+			  )
+			]
+
+Both `toProto` and  `handleProto` split the incoming line apart into the
+first word, and the rest of the line, then match the first word against the
+commands in the protocol, and dispatches to appropriate actions. So,
+how do we split a variable apart like that in the Shell monad? Like this...
+
+	getProtoCommand :: Var -> Script Var
+	getProtoCommand v = trimVar LongestMatch FromEnd v (glob " *")
+	
+	getProtoRest :: Var -> Script Var
+	getProtoRest v = trimVar ShortestMatch FromBeginning v (glob "[! ]*[ ]")
+
+(This could probably be improved by using a DSL to generate the globs
+too..)
+
+## conclusion
+
+And finally, here's a `main` to generate the shell script!
+
+	main :: IO ()
+	main = T.writeFile "protocol.sh" $ script foo
+
+The pretty-printed shell script that produces is not very interesting,
+but I'll include it at the end for completeness. More interestingly for the
+purposes of sshing to a host and running the command there, we can use
+`linearScript` to generate a version of the script that's all contained on
+a single line. Also included below.
+
+I could easily have written the pretty-printed version of the shell script
+in twice the time that it took to write the haskell program that generates
+it and also speaks the protocol itself.
+
+I would certianly have had to test the hand-written shell script repeatedly.
+Code like `for _x in $(seq 1 "${_v#[!\ ]*[\ ]}")` doesn't just write and
+debug itself. (Until now!)
+
+But, the generated scrpt worked 100% on the first try! Well, it worked as
+soon as I got the Haskell program to compile...
+
+But the best part is that the Haskell program and the shell script
+don't just speak the same protocol. They both rely on the same
+definition of Proto. So this is fairly close to the kind of type-safe
+protocol serialization that Fay provides, when compiling Haskell to
+javascript.
+
+I'm getting the feeling that I won't be writing too many nontrivial shell
+scripts by hand anymore! :)
+
+## the pretty-printed shell program

(Diff truncated)
update
diff --git a/code.mdwn b/code.mdwn
index 68243c1..bd7835a 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -23,6 +23,7 @@ In maintenance mode mostly, but I still have my hands in it somewhat.
 [[pdmenu]]
 [[filters]]
 [electrum-mnemonic](http://hackage.haskell.org/package/electrum-mnemonic)
+[shell-monad](http://hackage.haskell.org/package/shell-monad)
 [brainfuck-monad](http://hackage.haskell.org/package/brainfuck-monad)
 
 ## Past projects

one more
diff --git a/blog/entry/announcing_git-annex.mdwn b/blog/entry/announcing_git-annex.mdwn
index d4e4d0b..8f182d6 100644
--- a/blog/entry/announcing_git-annex.mdwn
+++ b/blog/entry/announcing_git-annex.mdwn
@@ -23,3 +23,4 @@ uploaded to Debian shortly; or
 [git clone it](http://git-annex.branchable.com/download/).
 
 [[!meta title="announcing git-annex"]]
+[[!tag haskell]]

more
diff --git a/blog/entry/Template_Haskell_on_impossible_architectures.mdwn b/blog/entry/Template_Haskell_on_impossible_architectures.mdwn
index 43d5876..846fdb0 100644
--- a/blog/entry/Template_Haskell_on_impossible_architectures.mdwn
+++ b/blog/entry/Template_Haskell_on_impossible_architectures.mdwn
@@ -114,3 +114,4 @@ PPS, Also, if you let this put you off Haskell in any way .. well, don't.
 You just might want to wait a year or so before doing Haskell on Android.
 
 [[!meta title="Template Haskell on impossible architectures"]]
+[[!tag haskell]]
diff --git a/blog/entry/a_year_of_haskell___40__not_really__41__.mdwn b/blog/entry/a_year_of_haskell___40__not_really__41__.mdwn
index bb7a799..7ab47e1 100644
--- a/blog/entry/a_year_of_haskell___40__not_really__41__.mdwn
+++ b/blog/entry/a_year_of_haskell___40__not_really__41__.mdwn
@@ -23,3 +23,5 @@ I won't bore you with why
 [connected](http://changelog.complete.org/archives/779-education)
 [in my](http://www.eyrie.org/~eagle/journal/2008-11/018.html)
 [head](http://www.last.fm/music/Prof.+Malcolm+David+Eckel/+listeners).
+
+[[!tag haskell]]
diff --git a/blog/entry/announcing_github-backup.mdwn b/blog/entry/announcing_github-backup.mdwn
index 6d996bd..a7e96c2 100644
--- a/blog/entry/announcing_github-backup.mdwn
+++ b/blog/entry/announcing_github-backup.mdwn
@@ -16,3 +16,4 @@ Available in Cabal now, in Debian maybe if someone packages
 [[!tag code/github-backup]]
 
 [[!meta title="announcing github-backup"]]
+[[!tag haskell]]
diff --git a/blog/entry/build_a_dynamic_webapp_in_Yesod_in_just_three_days.mdwn b/blog/entry/build_a_dynamic_webapp_in_Yesod_in_just_three_days.mdwn
index 9b2839e..b481a39 100644
--- a/blog/entry/build_a_dynamic_webapp_in_Yesod_in_just_three_days.mdwn
+++ b/blog/entry/build_a_dynamic_webapp_in_Yesod_in_just_three_days.mdwn
@@ -98,3 +98,4 @@ I also see a lot of potential in Yesod to improve from where it is.
 [[!tag haskell yesod webapp braindump]]
 
 [[!meta title="build a dynamic webapp in Yesod in just three days"]]
+[[!tag haskell]]
diff --git a/blog/entry/case_study:_adding_box.com_support_to_git-annex.mdwn b/blog/entry/case_study:_adding_box.com_support_to_git-annex.mdwn
index eb97801..a89febd 100644
--- a/blog/entry/case_study:_adding_box.com_support_to_git-annex.mdwn
+++ b/blog/entry/case_study:_adding_box.com_support_to_git-annex.mdwn
@@ -82,3 +82,4 @@ Would someone like to fund my time to add a dropbox special remote to git-annex?
 [[!tag haskell code/git-annex consulting cloud]]
 
 [[!meta title="case study: adding box.com support to git-annex"]]
+[[!tag haskell]]
diff --git a/blog/entry/debugging_musings.mdwn b/blog/entry/debugging_musings.mdwn
index 9d354ef..df200b2 100644
--- a/blog/entry/debugging_musings.mdwn
+++ b/blog/entry/debugging_musings.mdwn
@@ -51,3 +51,4 @@ assumption is that the runtime environment just works, but I by now may
 have over-challenged that assumption. :)
 
 [[!meta title="debugging musings"]]
+[[!tag haskell]]
diff --git a/blog/entry/first_real_Haskell_program.mdwn b/blog/entry/first_real_Haskell_program.mdwn
index 3d3a6dd..df7ae6a 100644
--- a/blog/entry/first_real_Haskell_program.mdwn
+++ b/blog/entry/first_real_Haskell_program.mdwn
@@ -71,3 +71,4 @@ the input handle! Which is problimatic if you were going to use the file
 after you locked it..
 
 [[!meta title="first real Haskell program"]]
+[[!tag haskell]]
diff --git a/blog/entry/ghc_threaded_runtime_gotchas.mdwn b/blog/entry/ghc_threaded_runtime_gotchas.mdwn
index 21cb8d3..ad3963c 100644
--- a/blog/entry/ghc_threaded_runtime_gotchas.mdwn
+++ b/blog/entry/ghc_threaded_runtime_gotchas.mdwn
@@ -100,3 +100,4 @@ wide-ranging suite of thread communications data types (`MVar`, `Chan`,
 `QSemN`, `TMVar`, `SampleVar`, etc) have made developing a complex threaded
 program something I feel comfortable doing for the first time, in any
 language.
+[[!tag haskell]]
diff --git a/blog/entry/happy_haskell_hacker.mdwn b/blog/entry/happy_haskell_hacker.mdwn
index e9bbbb4..a66bc33 100644
--- a/blog/entry/happy_haskell_hacker.mdwn
+++ b/blog/entry/happy_haskell_hacker.mdwn
@@ -116,3 +116,4 @@ before, but it's very nice that it makes it so clean, easy, and generic.
 [[!tag code haskell]]
 
 [[!meta title="happy haskell hacker"]]
+[[!tag haskell]]
diff --git a/blog/entry/haskell_and_xmonad.mdwn b/blog/entry/haskell_and_xmonad.mdwn
index 5a26f81..43617f4 100644
--- a/blog/entry/haskell_and_xmonad.mdwn
+++ b/blog/entry/haskell_and_xmonad.mdwn
@@ -81,3 +81,5 @@ My current set of gripes with xmonad is small:
   type Haskell into the window manager. I rarely speak `lua` to `awesome` or
   `ion3`, but it's nice to have the ability there. I *think* it should be
   possible to do this with Haskell (see `ghci`).
+
+[[!tag haskell]]
diff --git a/blog/entry/haskell_baby_steps.mdwn b/blog/entry/haskell_baby_steps.mdwn
index 31d7d32..de18eac 100644
--- a/blog/entry/haskell_baby_steps.mdwn
+++ b/blog/entry/haskell_baby_steps.mdwn
@@ -32,3 +32,4 @@ error message and google took longer than figuring out how to write the rest
 of the program. :-(
 
 [[discussion]]
+[[!tag haskell]]
diff --git a/blog/entry/how_I_wrote_init_by_accident.mdwn b/blog/entry/how_I_wrote_init_by_accident.mdwn
index 1edb65e..946a2ac 100644
--- a/blog/entry/how_I_wrote_init_by_accident.mdwn
+++ b/blog/entry/how_I_wrote_init_by_accident.mdwn
@@ -54,3 +54,4 @@ from propellor.
 
 [[!meta title="how I wrote init by accident"]]
 [[!tag propellor]]
+[[!tag haskell]]
diff --git a/blog/entry/more_on_ghc_filename_encodings.mdwn b/blog/entry/more_on_ghc_filename_encodings.mdwn
index 44f9842..4fadd24 100644
--- a/blog/entry/more_on_ghc_filename_encodings.mdwn
+++ b/blog/entry/more_on_ghc_filename_encodings.mdwn
@@ -50,3 +50,4 @@ Language bugs.. gotta love em.
 [[!tag haskell]]
 
 [[!meta title="more on ghc filename encodings"]]
+[[!tag haskell]]
diff --git a/blog/entry/no_longer_a_perl_programmer.mdwn b/blog/entry/no_longer_a_perl_programmer.mdwn
index c5113e5..006efd7 100644
--- a/blog/entry/no_longer_a_perl_programmer.mdwn
+++ b/blog/entry/no_longer_a_perl_programmer.mdwn
@@ -49,3 +49,4 @@ Of course if Bradley Kuhn is right and
 I know what I'll be doing come the unix rollover in 2038. ;)
 
 [[!meta title="no longer a perl programmer"]]
+[[!tag haskell]]
diff --git a/blog/entry/on_haskell.mdwn b/blog/entry/on_haskell.mdwn
index 7737788..7e7971f 100644
--- a/blog/entry/on_haskell.mdwn
+++ b/blog/entry/on_haskell.mdwn
@@ -18,3 +18,4 @@ style=bar barwidth=2 barspacing=1 height=13]]
 
 [[discussion]]
 [[!meta date="Sun Aug 12 05:50:05 2007 +0000"]]
+[[!tag haskell]]
diff --git a/blog/entry/please_build_a_haskell_to_perl_compiler.mdwn b/blog/entry/please_build_a_haskell_to_perl_compiler.mdwn
index 600bae0..7cab4ea 100644
--- a/blog/entry/please_build_a_haskell_to_perl_compiler.mdwn
+++ b/blog/entry/please_build_a_haskell_to_perl_compiler.mdwn
@@ -27,3 +27,4 @@ be forwarded to the EFF instead.
 [[!tag haskell]]
 
 [[!meta title="please build a haskell to perl compiler"]]
+[[!tag haskell]]
diff --git a/blog/entry/three_thousand_lines_of_Haskell.mdwn b/blog/entry/three_thousand_lines_of_Haskell.mdwn
index 9d8b19c..323c8c0 100644
--- a/blog/entry/three_thousand_lines_of_Haskell.mdwn
+++ b/blog/entry/three_thousand_lines_of_Haskell.mdwn
@@ -90,3 +90,4 @@ other random impressions..
 [[!tag braindump haskell]]
 
 [[!meta title="three thousand lines of Haskell"]]
+[[!tag haskell]]
diff --git a/blog/entry/unicode_ate_my_homework.mdwn b/blog/entry/unicode_ate_my_homework.mdwn
index 9868f05..3e8cf0e 100644
--- a/blog/entry/unicode_ate_my_homework.mdwn
+++ b/blog/entry/unicode_ate_my_homework.mdwn
@@ -108,3 +108,4 @@ Previously: [[unicode_eye_chart]] [[wanted_on_a_bumper_sticker]] [[abc]]
 [[!tag unicode haskell]]
 
 [[!meta title="unicode ate my homework"]]
+[[!tag haskell]]
diff --git a/blog/entry/watch_me_code_for_half_an_hour.mdwn b/blog/entry/watch_me_code_for_half_an_hour.mdwn
index 67ae924..5496c2a 100644
--- a/blog/entry/watch_me_code_for_half_an_hour.mdwn
+++ b/blog/entry/watch_me_code_for_half_an_hour.mdwn
@@ -17,3 +17,4 @@ subcommand implemented here into "--auto" options that can be passed to
 [[!poll 40 "watched it all, liked it" 8 "watched some, boring" 3 "too long for me" 14 "too haskell for me" 12 "not interested"]]
 
 [[!meta title="watch me program for half an hour"]]
+[[!tag haskell]]
diff --git a/blog/entry/xmonad.hs_for_the_Palm_Pre.mdwn b/blog/entry/xmonad.hs_for_the_Palm_Pre.mdwn
index 421cf5c..9528d2a 100644
--- a/blog/entry/xmonad.hs_for_the_Palm_Pre.mdwn
+++ b/blog/entry/xmonad.hs_for_the_Palm_Pre.mdwn
@@ -26,3 +26,4 @@ Thus, to start a new terminal, use "Menu a x"; to close a window use
 "Menu a c"; to change layout, use "Menu a space"; and you can read the
 rest of the bindings in
 [the config file](http://git.kitenet.net/?p=joey/home-plus.git;a=blob_plain;f=.xmonad/xmonad.hs.limpet;hb=HEAD).
+[[!tag haskell]]
diff --git a/blog/entry/xmonad_layouts_for_netbooks.mdwn b/blog/entry/xmonad_layouts_for_netbooks.mdwn
index 55b0ae5..20b5bde 100644
--- a/blog/entry/xmonad_layouts_for_netbooks.mdwn
+++ b/blog/entry/xmonad_layouts_for_netbooks.mdwn
@@ -277,3 +277,4 @@ Xmonad layout optimised for the small screen of a netbook:
 
 PS: Thanks to `#xmonad` regulars for always having the answer
 I need up their sleeves.
+[[!tag haskell]]
diff --git a/blog/haskell.mdwn b/blog/haskell.mdwn
index 3b4bb0b..393865d 100644
--- a/blog/haskell.mdwn
+++ b/blog/haskell.mdwn
@@ -1,3 +1,3 @@
 The most recent haskell-related posts to [[Joey]]'s [[blog]].
 
-[[!inline pages="blog/entry/* and link(haskell) and !*/Discussion" show=10]]
+[[!inline pages="blog/entry/* and link(haskell) and !*/Discussion" show=40]]

rename to expose tag
diff --git a/blog/entry/haskell.mdwn b/blog/entry/haskell.mdwn
deleted file mode 100644
index 0fed040..0000000
--- a/blog/entry/haskell.mdwn
+++ /dev/null
@@ -1,19 +0,0 @@
-I saw the old house on Haskell St. last week for the first time in forver.
-I could barely pick it out of the other houses on the block, it's so
-different now.
-
-I also, conincidentially, studied the haskell programming language a bit. I
-watched some talks that John Goerzen linked to, and I read several chapters of
-the "Gentle Introduction to Haskell". Which wasn't all that gentle. :-) I
-should have put off reading it until after the talks and after reading some
-other introductory examples.
-
-At least I have some motivation to learn haskell. I could never see any reason
-to bother learning python or ruby, since there's so few compelling differences
-between them and perl. I had begun to worry that I wasn't learning any new
-programming languages, so it's nice to have a new dot on this graph of
-[[languages]] I've learned since 1990:
-[[!sparkline 2 0 4 0 6 1 1 1 1 0 1 0 0 0 0 0 0 1(red)
-style=bar barwidth=2 barspacing=1 height=13]]
-
-[[discussion]]
diff --git a/blog/entry/haskell/discussion.mdwn b/blog/entry/haskell/discussion.mdwn
deleted file mode 100644
index ba318f9..0000000
--- a/blog/entry/haskell/discussion.mdwn
+++ /dev/null
@@ -1,50 +0,0 @@
-I also used to think there were no compelling differences between Perl and
-Python.  Then I was forced to learn Python for my new job.  Two weeks later
-I started a new hobby project for myself.  I started it in Perl, because
-I've been programming Perl for a couple of years, and Python for a couple
-of weeks (and I still didn't believe there were any compelling differences
-at that time).  After a couple of hours I gave up, rewrote it in Python,
-and never looked back.
-
-It's not just the language, but the traditions, ways of writing code, ways
-of designing APIs.  I'm significantly more productive in Python than in any
-other language I know (Pascal, C, C++, Java, Perl).
-
-I'm not trying to convince you to start learning Python right now; just to
-point out that it's hard to see what's compelling about Python without
-actually using it for a while.
-
-  -- Marius Gedminas (marius@gedmin.as)
-
-> While I haven't bothered to learn it, I'm familiar enough with python
-> that I can extrapolate how productive I would be in it if I invested, say
-> 5 years into learning and becoming extremely familiar with the language
-> (as I did with perl).
-> 
-> One benefit of python is that it might only take 3 years to get to that
-> point, thanks to things like the more regular calling conventions for
-> fuctions, less quirky syntax, better introspection, more standardised OO.
-> I might find learning all the libraries that much eaiser. Once I know
-> them in detail though, those things rarely matter, they might add a few
-> minutes to learning a new library, that's about all. And I suspect I'd
-> spend marginally more time typing than I do when writing perl, which
-> probably would balance that out pretty well.
-> 
-> Omitting the learning curve issues though, I see no significant
-> difference in productivity between python masters and perl masters. I
-> don't see anything in python or perl that would provide one, and I don't
-> see any evidence of one in the real world.
->
-> (The analysis is a bit different for ruby, and made with rather less
-> information, but my gut feeling is the same.)
-> 
-> One of the interesting things about haskell, to me, is that I can't even
-> make this kind of analysis about it yet. It's too different. Maybe
-> haskell masters crank out beautiful functional code with no bugs all day.
-> Maybe they spend all day tracking down hard to debug cases of infinite
-> recusion. The only statement I can make about them based on real world
-> observation is that they seem to think more formally than either perl or
-> python programmers, and that many of them seem to be focused more on
-> theory than on implementing real-world stuff.
-> 
-> --[[Joey]]
diff --git a/blog/entry/on_haskell.mdwn b/blog/entry/on_haskell.mdwn
new file mode 100644
index 0000000..7737788
--- /dev/null
+++ b/blog/entry/on_haskell.mdwn
@@ -0,0 +1,20 @@
+I saw the old house on Haskell St. last week for the first time in forver.
+I could barely pick it out of the other houses on the block, it's so
+different now.
+
+I also, conincidentially, studied the haskell programming language a bit. I
+watched some talks that John Goerzen linked to, and I read several chapters of
+the "Gentle Introduction to Haskell". Which wasn't all that gentle. :-) I
+should have put off reading it until after the talks and after reading some
+other introductory examples.
+
+At least I have some motivation to learn haskell. I could never see any reason
+to bother learning python or ruby, since there's so few compelling differences
+between them and perl. I had begun to worry that I wasn't learning any new
+programming languages, so it's nice to have a new dot on this graph of
+[[languages]] I've learned since 1990:
+[[!sparkline 2 0 4 0 6 1 1 1 1 0 1 0 0 0 0 0 0 1(red)
+style=bar barwidth=2 barspacing=1 height=13]]
+
+[[discussion]]
+[[!meta date="Sun Aug 12 05:50:05 2007 +0000"]]
diff --git a/blog/entry/on_haskell/discussion.mdwn b/blog/entry/on_haskell/discussion.mdwn
new file mode 100644
index 0000000..ba318f9
--- /dev/null
+++ b/blog/entry/on_haskell/discussion.mdwn
@@ -0,0 +1,50 @@
+I also used to think there were no compelling differences between Perl and
+Python.  Then I was forced to learn Python for my new job.  Two weeks later
+I started a new hobby project for myself.  I started it in Perl, because
+I've been programming Perl for a couple of years, and Python for a couple
+of weeks (and I still didn't believe there were any compelling differences
+at that time).  After a couple of hours I gave up, rewrote it in Python,
+and never looked back.
+
+It's not just the language, but the traditions, ways of writing code, ways
+of designing APIs.  I'm significantly more productive in Python than in any
+other language I know (Pascal, C, C++, Java, Perl).
+
+I'm not trying to convince you to start learning Python right now; just to
+point out that it's hard to see what's compelling about Python without
+actually using it for a while.
+
+  -- Marius Gedminas (marius@gedmin.as)
+
+> While I haven't bothered to learn it, I'm familiar enough with python
+> that I can extrapolate how productive I would be in it if I invested, say
+> 5 years into learning and becoming extremely familiar with the language
+> (as I did with perl).
+> 
+> One benefit of python is that it might only take 3 years to get to that
+> point, thanks to things like the more regular calling conventions for
+> fuctions, less quirky syntax, better introspection, more standardised OO.
+> I might find learning all the libraries that much eaiser. Once I know
+> them in detail though, those things rarely matter, they might add a few
+> minutes to learning a new library, that's about all. And I suspect I'd
+> spend marginally more time typing than I do when writing perl, which
+> probably would balance that out pretty well.
+> 
+> Omitting the learning curve issues though, I see no significant
+> difference in productivity between python masters and perl masters. I
+> don't see anything in python or perl that would provide one, and I don't
+> see any evidence of one in the real world.
+>
+> (The analysis is a bit different for ruby, and made with rather less
+> information, but my gut feeling is the same.)
+> 
+> One of the interesting things about haskell, to me, is that I can't even
+> make this kind of analysis about it yet. It's too different. Maybe
+> haskell masters crank out beautiful functional code with no bugs all day.
+> Maybe they spend all day tracking down hard to debug cases of infinite
+> recusion. The only statement I can make about them based on real world
+> observation is that they seem to think more formally than either perl or
+> python programmers, and that many of them seem to be focused more on
+> theory than on implementing real-world stuff.
+> 
+> --[[Joey]]

haskell feed
diff --git a/blog/entry/a_brainfuck_monad.mdwn b/blog/entry/a_brainfuck_monad.mdwn
index 941aa84..68dbba8 100644
--- a/blog/entry/a_brainfuck_monad.mdwn
+++ b/blog/entry/a_brainfuck_monad.mdwn
@@ -117,3 +117,4 @@ Of course, the real point is that "monad" and "brainfuck" so obviously
 belonged together that it would have been a crime not to write this.
 
 [[!meta title="a brainfuck monad"]]
+[[!tag haskell]]
diff --git a/blog/entry/adding_docker_support_to_propellor.mdwn b/blog/entry/adding_docker_support_to_propellor.mdwn
index 492bf30..0762f2b 100644
--- a/blog/entry/adding_docker_support_to_propellor.mdwn
+++ b/blog/entry/adding_docker_support_to_propellor.mdwn
@@ -70,3 +70,4 @@ not expressive enough.
 
 [[!meta title="adding docker support to propellor"]]
 [[!tag propellor]]
+[[!tag haskell]]
diff --git a/blog/entry/clean_OS_reinstalls_with_propellor.mdwn b/blog/entry/clean_OS_reinstalls_with_propellor.mdwn
index 9fcd34e..0671fd2 100644
--- a/blog/entry/clean_OS_reinstalls_with_propellor.mdwn
+++ b/blog/entry/clean_OS_reinstalls_with_propellor.mdwn
@@ -106,3 +106,4 @@ distribution you want. Patches welcomed...
 [[!tag propellor]]
 
 [[!meta title="clean OS reinstalls with propellor"]]
+[[!tag haskell]]
diff --git a/blog/entry/introducing_propellor.mdwn b/blog/entry/introducing_propellor.mdwn
index c05b524..97f6f28 100644
--- a/blog/entry/introducing_propellor.mdwn
+++ b/blog/entry/introducing_propellor.mdwn
@@ -96,3 +96,4 @@ to build just the thing you want without the cruft. Nice to have the latter!
 
 [[!tag propellor]]
 [[!meta date="Sun Mar 30 03:50:42 2014 -0400"]]
+[[!tag haskell]]
diff --git a/blog/entry/propelling_containers.mdwn b/blog/entry/propelling_containers.mdwn
index 0399a90..71b4a8e 100644
--- a/blog/entry/propelling_containers.mdwn
+++ b/blog/entry/propelling_containers.mdwn
@@ -1,4 +1,5 @@
 [[!tag propellor]]
+[[!tag haskell]]
 
 Propellor has supported docker containers for a "long" time, and it works
 great. This week I've worked on adding more container support.
diff --git a/blog/entry/propellor-driven_DNS_and_backups.mdwn b/blog/entry/propellor-driven_DNS_and_backups.mdwn
index 954539a..2ad9610 100644
--- a/blog/entry/propellor-driven_DNS_and_backups.mdwn
+++ b/blog/entry/propellor-driven_DNS_and_backups.mdwn
@@ -103,3 +103,4 @@ By the way, Propellor is now up to 3 thousand lines of code
 
 [[!meta title="propellor-driven DNS and backups"]]
 [[!tag propellor]]
+[[!tag haskell]]
diff --git a/blog/entry/propellor_introspection_for_DNS.mdwn b/blog/entry/propellor_introspection_for_DNS.mdwn
index 9bc8d0b..432ad05 100644
--- a/blog/entry/propellor_introspection_for_DNS.mdwn
+++ b/blog/entry/propellor_introspection_for_DNS.mdwn
@@ -94,3 +94,4 @@ main = do
 
 [[!meta title="propellor introspection for DNS"]]
 [[!tag propellor]]
+[[!tag haskell]]
diff --git a/blog/entry/propellor_is_d-i_2.0.mdwn b/blog/entry/propellor_is_d-i_2.0.mdwn
index 90b51e9..21d116c 100644
--- a/blog/entry/propellor_is_d-i_2.0.mdwn
+++ b/blog/entry/propellor_is_d-i_2.0.mdwn
@@ -66,3 +66,4 @@ and adding a few thousand lines of code to it.
 
 [[!meta title="propellor is d-i 2.0"]]
 [[!tag propellor]]
+[[!tag haskell]]
diff --git a/blog/entry/propellor_type-safe_reversions.mdwn b/blog/entry/propellor_type-safe_reversions.mdwn
index 62993c8..9545318 100644
--- a/blog/entry/propellor_type-safe_reversions.mdwn
+++ b/blog/entry/propellor_type-safe_reversions.mdwn
@@ -44,3 +44,4 @@ revertable, and others not:
 
 [[!meta title="propellor type-safe reversions"]]
 [[!tag propellor]]
+[[!tag haskell]]
diff --git a/blog/entry/shell_monad.mdwn b/blog/entry/shell_monad.mdwn
index 3069000..1f4d5d7 100644
--- a/blog/entry/shell_monad.mdwn
+++ b/blog/entry/shell_monad.mdwn
@@ -1,4 +1,5 @@
 [[!meta title="generating shell scripts from haskell using a shell monad"]]
+[[!tag haskell]]
 
 Shell script is the lingua franca of Unix, it's available everywhere and often
 the only reasonable choice to Get Stuff Done. But it's also clumsy and it's
diff --git a/blog/haskell.mdwn b/blog/haskell.mdwn
new file mode 100644
index 0000000..3b4bb0b
--- /dev/null
+++ b/blog/haskell.mdwn
@@ -0,0 +1,3 @@
+The most recent haskell-related posts to [[Joey]]'s [[blog]].
+
+[[!inline pages="blog/entry/* and link(haskell) and !*/Discussion" show=10]]

format
diff --git a/blog/entry/shell_monad.mdwn b/blog/entry/shell_monad.mdwn
index 995ffe2..3069000 100644
--- a/blog/entry/shell_monad.mdwn
+++ b/blog/entry/shell_monad.mdwn
@@ -45,7 +45,7 @@ When run, that haskell program generates this shell code.
 Which, while machine-generated, has nice indentation, and is generally
 pretty readable.
 
-[[!format shell """
+[[!format sh """
 #!/bin/sh
 f1 () { :
 	echo 'Ho, ho, ho!' 'Merry xmas!'

title
diff --git a/blog/entry/shell_monad.mdwn b/blog/entry/shell_monad.mdwn
index fe8738f..995ffe2 100644
--- a/blog/entry/shell_monad.mdwn
+++ b/blog/entry/shell_monad.mdwn
@@ -74,5 +74,3 @@ There's a lot of things that could be added to this library
 (`if`, `while`, redirection, etc), but I can already see using
 it in various parts of propellor and git-annex that need to generate
 shell code.
-
-[[!meta title="shell monad"]]

blog update
diff --git a/blog/entry/shell_monad.mdwn b/blog/entry/shell_monad.mdwn
new file mode 100644
index 0000000..fe8738f
--- /dev/null
+++ b/blog/entry/shell_monad.mdwn
@@ -0,0 +1,78 @@
+[[!meta title="generating shell scripts from haskell using a shell monad"]]
+
+Shell script is the lingua franca of Unix, it's available everywhere and often
+the only reasonable choice to Get Stuff Done. But it's also clumsy and it's
+easy to write unsafe shell scripts, that forget to quote variables, typo
+names of functions, etc.
+
+Wouldn't it be nice if we could write code in some better language,
+that generated nicely formed shell scripts and avoided such gotchas?
+Today, I've built a Haskell monad that can generate shell code.
+
+Here's a fairly involved example. This demonstrates several features,
+including the variadic `cmd`, the ability to define shell functions,
+to bind and use shell variables, to build pipes (with the `-:-` operator),
+and to factor out generally useful haskell functions like `pipeLess`
+and `promptFor` ...
+
+[[!format haskell """
+santa = script $ do
+	hohoho <- func $
+		cmd "echo" "Ho, ho, ho!" "Merry xmas!"
+	hohoho
+
+	promptFor "What's your name?" $ \name -> pipeLess $ do
+		cmd "echo" "Let's see what's in" (val name <> quote "'s") "stocking!"
+		forCmd (cmd "ls" "-1" (quote "/home/" <> val name)) $ \f -> do
+			cmd "echo" "a shiny new" f
+			hohoho
+
+	cmd "rm" "/table/cookies" "/table/milk"
+	hohoho
+
+pipeLess :: Script () -> Script ()
+pipeLess c = c -|- cmd "less"
+
+promptFor :: T.Text -> (Var -> Script ()) -> Script ()
+promptFor prompt cont = do
+	cmd "printf" (prompt <> " ")
+	var <- newVar "prompt"
+	readVar var
+	cont var
+"""]]
+
+When run, that haskell program generates this shell code.
+Which, while machine-generated, has nice indentation, and is generally
+pretty readable.
+
+[[!format shell """
+#!/bin/sh
+f1 () { :
+	echo 'Ho, ho, ho!' 'Merry xmas!'
+}
+f1
+printf 'What'"'"'s your name?  '
+read '_prompt1'
+(
+	echo 'Let'"'"'s see what'"'"'s in' "$_prompt1"''"'"'s' 'stocking!'
+	for _x1 in $(ls '-1' '/home/'"$_prompt1")
+	do :
+		echo 'a shiny new' "$_x1"
+		f1
+	done
+) | (
+	less
+)
+rm '/table/cookies' '/table/milk'
+f1
+"""]]
+
+Santa has already uploaded [shell-monad](http://hackage.haskell.org/package/shell-monad)
+to hackage and git.
+
+There's a lot of things that could be added to this library
+(`if`, `while`, redirection, etc), but I can already see using
+it in various parts of propellor and git-annex that need to generate
+shell code.
+
+[[!meta title="shell monad"]]

poll vote (watched it all, liked it)
diff --git a/blog/entry/watch_me_code_for_half_an_hour.mdwn b/blog/entry/watch_me_code_for_half_an_hour.mdwn
index 85dda47..67ae924 100644
--- a/blog/entry/watch_me_code_for_half_an_hour.mdwn
+++ b/blog/entry/watch_me_code_for_half_an_hour.mdwn
@@ -14,6 +14,6 @@ Not shown is the hour I spent the next day changing the "optimize"
 subcommand implemented here into "--auto" options that can be passed to
 [[code/git-annex]]'s get and drop commands.
 
-[[!poll 39 "watched it all, liked it" 8 "watched some, boring" 3 "too long for me" 14 "too haskell for me" 12 "not interested"]]
+[[!poll 40 "watched it all, liked it" 8 "watched some, boring" 3 "too long for me" 14 "too haskell for me" 12 "not interested"]]
 
 [[!meta title="watch me program for half an hour"]]

etckeeper website
diff --git a/code/etckeeper.mdwn b/code/etckeeper.mdwn
index 39776cc..f9a7f2b 100644
--- a/code/etckeeper.mdwn
+++ b/code/etckeeper.mdwn
@@ -7,9 +7,7 @@ such as the permissions of `/etc/shadow`. It's quite modular and
 configurable, while also being simple to use if you understand the basics
 of working with revision control.
 
-etckeeper is available in git at `git://git.kitenet.net/etckeeper`, or
-[in gitweb](http://git.kitenet.net/?p=etckeeper.git). It's packaged in
-Debian, Ubuntu, Fedora, etc.
+<http://etckeeper.branchable.com/>
 
 ## News
 
diff --git a/code/etckeeper/news/new_website.mdwn b/code/etckeeper/news/new_website.mdwn
new file mode 100644
index 0000000..4f50548
--- /dev/null
+++ b/code/etckeeper/news/new_website.mdwn
@@ -0,0 +1,3 @@
+etckeeper now has a website at <http://etckeeper.branchable.com/>.
+
+New releases will be posted there; please update your RSS subscriptions.
diff --git a/code/etckeeper/news/version_1.12.mdwn b/code/etckeeper/news/version_1.12.mdwn
deleted file mode 100644
index 54084cf..0000000
--- a/code/etckeeper/news/version_1.12.mdwn
+++ /dev/null
@@ -1,7 +0,0 @@
-etckeeper 1.12 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Portability fixes. Thanks, Harald Dunkel.
-   * Add support for pushing to multiple remote repositories.
-     Thanks, Rouben.
-   * Fix handling of git ignores like dir/*
-     Thanks, Pim van den Berg"""]]
\ No newline at end of file
diff --git a/code/etckeeper/news/version_1.13.mdwn b/code/etckeeper/news/version_1.13.mdwn
deleted file mode 100644
index 1bd3b79..0000000
--- a/code/etckeeper/news/version_1.13.mdwn
+++ /dev/null
@@ -1,7 +0,0 @@
-etckeeper 1.13 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Ignore check-mk-agent-logwatch's FHS violating
-     /etc/check\_mk/logwatch.state. Closes: #[753903](http://bugs.debian.org/753903)
-   * Only allow [-a-z\_] in etckeeper commands to avoid any possible directory
-     traversal etc issues.
-   * update-ignore, uninit: Fix parsing of ignore files containing '\'"""]]
\ No newline at end of file
diff --git a/code/etckeeper/news/version_1.14.mdwn b/code/etckeeper/news/version_1.14.mdwn
deleted file mode 100644
index e5789d3..0000000
--- a/code/etckeeper/news/version_1.14.mdwn
+++ /dev/null
@@ -1,9 +0,0 @@
-etckeeper 1.14 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Handle failure to commit in post-install, pre-install by showing a
-     warning, rather than propigating the error to apt.
-     This avoids breaking the apt run when eg, git is misconfigured and
-     cannot commit.
-     pre-install already did this when it was able to use debconf to display a
-     message, but now debconf is not used, and it always behaves this way.
-     Closes: #[760011](http://bugs.debian.org/760011)"""]]
\ No newline at end of file
diff --git a/code/etckeeper/news/version_1.15.mdwn b/code/etckeeper/news/version_1.15.mdwn
deleted file mode 100644
index 4934b76..0000000
--- a/code/etckeeper/news/version_1.15.mdwn
+++ /dev/null
@@ -1,4 +0,0 @@
-etckeeper 1.15 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Recommend cron-daemon, rather than cron, as etckeeper only needs
-     cron.daily functionality. Closes: #[762721](http://bugs.debian.org/762721)"""]]
\ No newline at end of file
diff --git a/code/etckeeper/news/version_1.16.mdwn b/code/etckeeper/news/version_1.16.mdwn
deleted file mode 100644
index 91d93b4..0000000
--- a/code/etckeeper/news/version_1.16.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-etckeeper 1.16 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Added support for Fedora's DNF highlevel package manager.
-     Thanks, Peter Listiak and Petr Spacek.
-   * Add architecture info to dpkg list-installed. Closes: #[768145](http://bugs.debian.org/768145)
-   * Orphaned the Debian package."""]]
\ No newline at end of file

add news item for github-backup 1.20141222
diff --git a/code/github-backup/news/version_1.20140807.mdwn b/code/github-backup/news/version_1.20140807.mdwn
deleted file mode 100644
index a71336c..0000000
--- a/code/github-backup/news/version_1.20140807.mdwn
+++ /dev/null
@@ -1,4 +0,0 @@
-github-backup 1.20140807 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Fix build with github 0.9.
-   * Fix creation of github branch."""]]
\ No newline at end of file
diff --git a/code/github-backup/news/version_1.20141222.mdwn b/code/github-backup/news/version_1.20141222.mdwn
new file mode 100644
index 0000000..b429657
--- /dev/null
+++ b/code/github-backup/news/version_1.20141222.mdwn
@@ -0,0 +1,8 @@
+github-backup 1.20141222 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Added gitriddance(1), a utility to close all issues and pull requests,
+     for repos that don't want to be bothered with GitHub's proprietary
+     issue tracker.
+   * gitriddance depends on github 0.13.1, which has bug fixes
+     for posting comments.
+   * Various updates to internal git and utility libraries shared with git-annex."""]]
\ No newline at end of file

add
diff --git a/pics/brazil/2014/meeting-in-terravista.jpeg b/pics/brazil/2014/meeting-in-terravista.jpeg
new file mode 100644
index 0000000..deb8130
Binary files /dev/null and b/pics/brazil/2014/meeting-in-terravista.jpeg differ

update
diff --git a/code.mdwn b/code.mdwn
index 93302ef..68243c1 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -6,22 +6,24 @@ occasionally basic websites about my software.
 The stuff that's swapped into my local cache at the moment.
 
 [[git-annex]]
-[[ikiwiki]]
 [[propellor]]
 [[myrepos|mr]]
 [[etckeeper]]
+[[ikiwiki]]
 [[moreutils]]
 [[ikiwiki-hosting]]
 [[github-backup]]
-[[mpdtoys]]
 
 ## Less active projects
 
 In maintenance mode mostly, but I still have my hands in it somewhat.
 
+[[mpdtoys]]
 [[Words2Nums]]
 [[pdmenu]]
 [[filters]]
+[electrum-mnemonic](http://hackage.haskell.org/package/electrum-mnemonic)
+[brainfuck-monad](http://hackage.haskell.org/package/brainfuck-monad)
 
 ## Past projects
 

comment
diff --git a/blog/entry/a_brainfuck_monad/comment_3_156c49d4a06acddecdb4f6a74a643f52._comment b/blog/entry/a_brainfuck_monad/comment_3_156c49d4a06acddecdb4f6a74a643f52._comment
new file mode 100644
index 0000000..6cc4291
--- /dev/null
+++ b/blog/entry/a_brainfuck_monad/comment_3_156c49d4a06acddecdb4f6a74a643f52._comment
@@ -0,0 +1,45 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""alloc"""
+ date="2014-12-12T17:42:51Z"
+ content="""
+Instead of the constants hack, it's useful to define a stack-based
+function call model in brainfuck. This alloc higher-level function
+implements that, and is simplifying the code significantly.
+
+<pre>
+-- | For higher-level programming in brainfuck, it's useful to have a way
+-- to run a function, while allocating a memory cell, which is initialized
+-- to contain 0.
+--
+-- This and many of the functions below assume that
+-- cells to the left are in use, while cells to the right
+-- are unused and may contain any data. Higher-level functions should
+-- generally avoid changing the current cell, and should instead alloc
+-- a new one to use.
+alloc :: BrainFuck a -> BrainFuck a
+alloc a = do
+        next
+        zero
+        cell <- addr
+        r <- a
+        setAddr cell
+        prev
+        return r
+</pre>
+
+Using alloc, we can build a nicer loopFrom, etc.
+
+<pre>
+-- | Allocates a cell and uses it as the loop counter, starting from 
+-- the provided value. The action will continue running in a loop until
+-- it decrements the counter to 0.
+loopFrom :: Int -> (DataPointer -> BrainFuck ()) -> BrainFuck ()
+loopFrom n a = alloc $ do
+        i <- addr
+        add n
+        loopUnless0 $ do
+                a i
+                setAddr i
+</pre>
+"""]]

Added a comment: Ah, so that’s where the “useless” numbers come from…
diff --git a/blog/entry/a_brainfuck_monad/comment_3_435056729f4e0dc16e01a8110e1baa64._comment b/blog/entry/a_brainfuck_monad/comment_3_435056729f4e0dc16e01a8110e1baa64._comment
new file mode 100644
index 0000000..166ec49
--- /dev/null
+++ b/blog/entry/a_brainfuck_monad/comment_3_435056729f4e0dc16e01a8110e1baa64._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="http://mirsolutions.de/"
+ nickname="mirabilos"
+ subject="Ah, so that’s where the “useless” numbers come from…"
+ date="2014-12-12T12:12:22Z"
+ content="""
+… I had wondered.
+
+And it’s probably saying something about me that I find the brainfuck output more legible than the Haskell input ☺
+
+(But only that I speak only one of the two languages.)
+"""]]

innaccurate
diff --git a/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment b/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
deleted file mode 100644
index 06e827f..0000000
--- a/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
+++ /dev/null
@@ -1,13 +0,0 @@
-[[!comment format=mdwn
- username="joey"
- subject="""comment 3"""
- date="2014-12-12T08:10:37Z"
- content="""
-Ah, here's a way to avoid the mess with the constants:
-
-<pre>
--- | Fills the current data cell with zero.
-zero :: BrainFuck ()
-zero = loopUnless0 decr
-</pre>
-"""]]

typo
diff --git a/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment b/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
index ec0eee8..06e827f 100644
--- a/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
+++ b/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
@@ -8,6 +8,6 @@ Ah, here's a way to avoid the mess with the constants:
 <pre>
 -- | Fills the current data cell with zero.
 zero :: BrainFuck ()
-zero n = loopUnless0 decr
+zero = loopUnless0 decr
 </pre>
 """]]

comment
diff --git a/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment b/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
new file mode 100644
index 0000000..ec0eee8
--- /dev/null
+++ b/blog/entry/a_brainfuck_monad/comment_3_01fd8cad33389e3a614d7686f9217888._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2014-12-12T08:10:37Z"
+ content="""
+Ah, here's a way to avoid the mess with the constants:
+
+<pre>
+-- | Fills the current data cell with zero.
+zero :: BrainFuck ()
+zero n = loopUnless0 decr
+</pre>
+"""]]