LLM proofreading update
October 23, 2025 at 10:20 AM by Dr. Drang
I’m still experimenting with ChatGPT and Claude as proofreaders for my blog posts. Because I’ve been writing more posts lately, I’ve been learning the good and the bad more rapidly than I would have expected when I started this. Time for a preliminary assessment.
Overall, ChatGPT does a better job than Claude, but they both have found errors that the other has missed. So far, this has cost me time but not money, as I’m still using the free versions of both. If I decide to subscribe, it will be to only one of them, so I see this as a sort of playoff round.
(I haven’t given Apple’s Writing Tools a serious test yet, mainly because I doubt they can navigate the mixture of text and code that my posts usually have. But I suppose it’s time to start and see if my doubts are justified.)
Both LLMs are pretty good at finding my most common mistakes, which are missing words (I often think I’ve typed a word that I haven’t) and inconsistent tense (which usually crops up as I rewrite part of a sentence). By “pretty good,” I mean they tend to find both real errors and hallucinatory errors. So far, the number of false positives has been acceptably small.
The two main problems I’ve had have been stopping them from giving me stylistic advice and getting them to give me good directions on where the errors are. My proofreading instructions have evolved over the past couple of weeks to address these deficiencies. Here’s my current set of instructions, which I insert via Typinator:
Find typographical errors and grammatical mistakes in the following Markdown text. Do not make any changes, just tell me what you think should be changed. Ignore all embedded URLs, quoted text, and programming code. Only report actual errors, not style suggestions. I am using the linefeed character to end each line and have put line numbers at the beginning of each line; use those line numbers to report where the errors are.
Claude suggested some of this prompt. In particular, the redundancy of including “Only report actual errors, not style suggestions” in addition to “Find typographical errors and grammatical mistakes” was Claude’s doing.
The wordy final sentence is my latest attempt to solve the navigation problem. I write my posts in BBEdit with line numbers along the left side of the window.

As you can see, line numbers in BBEdit are based not on how the lines appear as wrapped but on where the linefeed characters (ASCII decimal 10) are. This is how I want the location of errors reported to me, and it’s been surprisingly difficult to get both ChatGPT and Claude to do this right.
I started by telling them to report the line numbers of the errors. That gave me nonsensical and inconsistent line numbers. Then I told them to use the linefeed character as the line separator, and they still came back with nonsense. I had to repeat the instruction and tell them to do the analysis again. That second instruction in the same session managed to get both of them to give me correct line numbers, but ChatGPT spent a lot of time—maybe as long as a minute—doing the second analysis.
I asked ChatGPT why it took so long to process the text with proper line numbers in the analysis. Here’s what it said:
Good question — it’s because I have to process the text exactly as you specify (counting literal linefeed characters, not wrapped lines), which means I can’t rely on my usual tokenization or formatting tools. I have to manually parse and number every line in the order they appear, skipping over things like embedded HTML or math markup but still counting their linefeeds. That’s slower and more error-prone than my normal text analysis, but it’s the only way to follow your instruction precisely.
Apparently ChatGPT doesn’t know how to use wc -l. I asked it for the best way to handle this problem. It suggested four options:
- I could add section markers to the text. It would then report errors as “section 2, paragraph 3.” This was a nonstarter. The point is to analyze the text as written.
- It could report character offsets like “character 1,085.” Yeah, that’s very helpful.
- It could present to me a version of the text with line numbers at the beginning of each line. After I confirm the line numbering, it could then report errors using those numbers. This was tempting, but I didn’t really want my text spit back at me for confirmation. And what if I disagree with the line numbers it generated?
- I could give it the text with line numbers already in place at the start of each line. I was going to reject this suggestion until I realized how easily I could add line numbers to what I paste into ChatGPT without changing the text in BBEdit.
So I chose the fourth option and made this simple Keyboard Maestro macro to insert line numbers before pasting:

The macro is available from the Keyboard Maestro menu only when ChatGPT or Claude is the active application. The single-line shell script that adds the line numbers is
nl -ba | sed -E 's/^ +//'
The nl command adds the line numbers. The -ba option tells it to number all the lines instead of just the nonblank lines, which makes the line numbering consistent with BBEdit’s. The sed command then strips the leading space characters, which I thought might confuse ChatGPT, leaving just the line number and a tab character before each line proper.
Because the first action puts the line-numbered text on the clipboard, I added the second action to remove it, leaving the clipboard the way it was before the macro was called.
Although I wrote this macro based on ChatGPT’s responses, it works with Claude, too. Both LLMs now give me the line numbers I want.
Does it seem right that computer systems that use billions of dollars of hardware, electricity, and clean water need my help to add line numbers to a few kilobytes of plain text? No, it does not. But at least this gives me deterministic results for one part of the processing.
I still have my Mac recite my posts to me after passing them through the LLMs. This isn’t much help in finding mistakes anymore, but it still leads me to rewrite paragraphs here and there.
Ties and percentages
October 22, 2025 at 11:25 AM by Dr. Drang
I was looking at the NFC standings yesterday, particularly the NFC North, the Chicago Bears’ division. The hated Packers are at the top of the division with a 4-1-1 record and a winning percentage of .750.

(Screenshot taken this morning from ESPN.)
How does a 4-1-1 record turn into .750? I guess the obvious answer is that the tie is treated as half a win and
But for some reason, my initial thought was more complicated. I thought of it as the weighted average of the winning percentage of non-ties with the winning percentage of ties:
(There may be a philosophical argument as to whether a tie truly constitutes a .500 winning percentage, but I’m just going to accept it as a given.)
The calculation works out the same, of course, because
I think this says something about me and my tendency to overcomplicate things. On the other hand, it’s always nice to be able to work out a solution in more than one way.
If you find that referring to numbers like .750, .800, and .500 as “percentages” a tiny bit off-putting, you’re my kind of people. But it’s common in sports to divide one number by another and call it a percentage—a tribute to the elasticity of language.
Two other common features of sports percentages:
- They are always (?) written to three decimal places. I think this was arrived at through experience—enough digits to distinguish close races but not enough to waste space.
- They are always spoken as if the decimal point isn’t there. The examples above are “seven-fifty,” “eight hundred,” and “five hundred.”
Baseball also uses “average” to express one number divided by another: batting average and earned run average come to mind. But it also has the slugging percentage, which isn’t remotely like a percentage.
I’m a big fan of “modulus,” mainly because of its many uses in engineering mechanics, e.g., Young’s modulus, bulk modulus, and section modulus. But I don’t expect to see it on ESPN anytime soon.
Clean Amazon links
October 18, 2025 at 10:06 AM by Dr. Drang
A footnote in last night’s post mentioned that the Amazon links therein had been cleaned of cruft. Today, I’ll show the simple automated way I do that.
When I say “cruft,” I mean the unnecessary additions to the URL that are presumably used by Amazon to benefit Amazon rather than you. For example, I just went to Amazon, searched for “homepod,” and clicked the link for a refurbished HomePod mini. This is the URL:
https://www.amazon.com/Apple-HomePod-mini-Black-Renewed/dp/
B0FK8JL74Q/ref=mpsa12?dib=eyJ2IjoiMSJ9.
okyLqpx2YKuwgWlCBKI2sx5DMA0QRhm1CnQPmA4KRzU8prbSTnXdAbE97fG0
tEwplRuboB2VvY5O6X6RZlvBK-MDyqaDNPwuqLUtqglS2gVwxITbCrE-
A5JKngZ9MmPHgucGCJSduRY0Ap3RUqVJy42OrYI9eh-
GcIQn52Fj7NgpN2pv3jyeZBvnCXsUcbFhTREmb2NV66tKI0ccT4OUg.4yYkD
-bBpGy0yyNVWMQnH8ZirFh3NhnFov0B0BZDIQ&dib_tag=se&keywords=
homepod&qid=1760794983&sr=8-2
I’ve added line breaks to make it easier (sort of) to read. I got this using Vivaldi, a browser I downloaded to my phone a while ago but haven’t used to visit Amazon before. It was definitely not signed into my Amazon account. Even so, I wouldn’t be surprised to learn that some part of this monstrous string is tied to me and my current session at Amazon.
Regardless, virtually none of this URL is needed. The key features are the dp at the end of the first line and the alphanumeric string that starts the second line. That string is the product’s ASIN. This simplified URL,
https://www.amazon.com/dp/B0FK8JL74Q
is enough to get to the product page. To go from the long URL to the shorter one, I use this Keyboard Maestro macro:

There’s not much to it. It grabs the URL of the current Safari page1 and puts it on the clipboard. It then does a search-and-replace on the clipboard using this regular expression for searching:
^.*/(?:dp|gp/product)/([^/?]+).*$
Over the years, I’ve noticed that some URLs have the ASIN after gp/product instead of dp, which is why you see the alternative there in the first set of parentheses. The ?: that follows the opening parenthesis means that this is a non-capturing group. What is captured is the ASIN that follows, which is why you see $1 at the end of the replacement regex:
https://www.amazon.com/dp/$1
The last part of the macro is a simple bit of error handling. I’ve noticed that I sometimes run this macro when I’m not on an Amazon page, which would put a URL I don’t want on the clipboard. So this checks the clipboard for the proper form. If it’s right, the Glass sound plays; if it’s wrong, the Basso sound plays and the clipboard goes back to what it was before.
If you download the macro (right click the link so you can save it with a .kmmacros extension) and import it into Keyboard Maestro, it will appear in the Global Macro Group and will be activated. I have it in the Global group because I sometimes use it in Messages or Mail to keep the URL I’m sending short. It’s not just for writing posts.
I should mention that I wrote about a very similar TextExpander abbreviation a decade ago. In that post, I had TextExpander run JavaScript for Automation to get and simplify the URL. I was also somewhat dismissive of using Keyboard Maestro—which I didn’t own back then—for a task like this. Times have certainly changed.
-
Yes, the long URL is from Vivaldi, not Safari, but I had the same product page open in Safari. The short URL is the same for both because the
dp/B0FK8JL74Qpart of the long URL is the same for both. ↩
Eraserhead
October 17, 2025 at 8:20 PM by Dr. Drang
I was talking to a friend about pencils a few weeks ago. I noticed she had a Blackwing 602 and asked her about it. She told me she never got along with mechanical pencils. The leads break too easily and the erasers don’t last—once they’re worn and need to be adjusted outward, they tend to slide back into their crimped metal holders when you press on them.

I had the same lead-breaking problem before I switched to Ain leads. But the second problem was vexing to me, too. I’ve often gotten around it by using separate erasers, either block or stick,1 but sometimes you just want to use the eraser on the end of your pencil. Its tiny diameter makes it better for small erasures.
I could, of course, squeeze the little metal holder with a pair of pliers. That would probably provide enough grip to keep the eraser from sliding, but I didn’t like the idea of ovalizing the holder.
It was on a Monday morning, as I was adding the upcoming week’s tasks to my notebook, that I thought of wrapping a bit of the washi tape I use to make notebook tabs around the eraser. By increasing the effective diameter of the eraser, the holder could grip it better, and it wouldn’t slip.

The thin red lines you see in the holder’s gap are the design on the washi tape I’m currently using. Because everything is small and tape often sticks where you don’t want it to, it was tricky getting a smooth wrap, but I managed. The eraser hasn’t slid since then. (It looks like I’ll need to push it out a bit more pretty soon.)
I don’t want to suggest that washi tape in particular was necessary for this operation—it was just what I had in front of me when I had the idea. But I do think a thin tape is best. If you build up the diameter of the eraser too much, it won’t fit nicely in the holder anymore.
-
If you’re wondering, I stopped using Amazon affiliate links quite a while ago. I have, however, edited the links in this paragraph to remove the voluminous cruft Amazon typically attaches. I assume that junk is tracking you—or worse, me—and there’s no need for it to be there. ↩