OpenAI are quietly adopting skills, now available in ChatGPT and Codex CLI
Plus using coding agents to port Rust to Python and then Python to JavaScript
In this newsletter:
OpenAI are quietly adopting skills, now available in ChatGPT and Codex CLI
JustHTML is a fascinating example of vibe engineering in action
I ported JustHTML from Python to JavaScript with Codex CLI and GPT-5.2 in 4.5 hours
Plus 8 links and 4 quotations
If you find this newsletter useful, please consider sponsoring me via GitHub. $10/month and higher sponsors get a monthly newsletter with my summary of the most important trends of the past 30 days - here are previews from September and October.
OpenAI are quietly adopting skills, now available in ChatGPT and Codex CLI - 2025-12-12
One of the things that most excited me about Anthropic’s new Skills mechanism back in October is how easy it looked for other platforms to implement. A skill is just a folder with a Markdown file and some optional extra resources and scripts, so any LLM tool with the ability to navigate and read from a filesystem should be capable of using them. It turns out OpenAI are doing exactly that, with skills support quietly showing up in both their Codex CLI tool and now also in ChatGPT itself.
Skills in ChatGPT
I learned about this from Elias Judin this morning. It turns out the Code Interpreter feature of ChatGPT now has a new /home/oai/skills folder which you can access simply by prompting:
Create a zip file of /home/oai/skills
I tried that myself and got back this zip file. Here’s a UI for exploring its content (more about that tool).
So far they cover spreadsheets, docx and PDFs. Interestingly their chosen approach for PDFs and documents is to convert them to rendered per-page PNGs and then pass those through their vision-enabled GPT models, presumably to maintain information from layout and graphics that would be lost if they just ran text extraction.
Elias shared copies in a GitHub repo. They look very similar to Anthropic’s implementation of the same kind of idea, currently published in their anthropics/skills repository.
I tried it out by prompting:
Create a PDF with a summary of the rimu tree situation right now and what it means for kakapo breeding season
Sure enough, GPT-5.2 Thinking started with:
Reading skill.md for PDF creation guidelines
Then:
Searching rimu mast and Kākāpō 2025 breeding status
It took just over eleven minutes to produce this PDF, which was long enough that I had Claude Code for web build me a custom PDF viewing tool while I waited.
Here’s ChatGPT’s PDF in that tool.
(I am very excited about Kākāpō breeding season this year.)
The reason it took so long is that it was fastidious about looking at and tweaking its own work. I appreciated that at one point it tried rendering the PDF and noticed that the macrons in kākāpō were not supported by the chosen font, so it switched to something else:
Skills in Codex CLI
Meanwhile, two weeks ago OpenAI’s open source Codex CLI tool landed a PR titled feat: experimental support for skills.md. The most recent docs for that are in docs/skills.md.
The documentation suggests that any folder in ~/.codex/skills will be treated as a skill.
I dug around and found the code that generates the prompt that drives the skill system in codex-rs/core/src/skills/render.rs - here’s a Gist with a more readable version of that prompt.
I used Claude Opus 4.5’s skill authoring skill to create this skill for creating Datasette plugins, then installed it into my Codex CLI skills folder like this:
git clone https://github.com/datasette/skill \
~/.codex/skills/datasette-pluginYou have to run Codex with the --enable skills option. I ran this:
cd /tmp
mkdir datasette-cowsay
cd datasette-cowsay
codex --enable skills -m gpt-5.2Then prompted:
list skills
And Codex replied:
- datasette-plugins — Writing Datasette plugins using Python + pluggy (file: /Users/simon/.codex/skills/datasette-plugin/SKILL.md)- Discovery — How to find/identify available skills (no SKILL.md path provided in the list)
Then I said:
Write a Datasette plugin in this folder adding a /-/cowsay?text=hello page that displays a pre with cowsay from PyPI saying that text
It worked perfectly! Here’s the plugin code it wrote and here’s a copy of the full Codex CLI transcript, generated with my terminal-to-html tool.
You can try that out yourself if you have uvx installed like this:
uvx --with https://github.com/simonw/datasette-cowsay/archive/refs/heads/main.zip \
datasetteThen visit:
http://127.0.0.1:8001/-/cowsay?text=This+is+pretty+funSkills are a keeper
When I first wrote about skills in October I said Claude Skills are awesome, maybe a bigger deal than MCP. The fact that it’s just turned December and OpenAI have already leaned into them in a big way reinforces to me that I called that one correctly.
Skills are based on a very light specification, if you could even call it that, but I still think it would be good for these to be formally documented somewhere. This could be a good initiative for the new Agentic AI Foundation (previously) to take on.
JustHTML is a fascinating example of vibe engineering in action - 2025-12-14
I recently came across JustHTML, a new Python library for parsing HTML released by Emil Stenström. It’s a very interesting piece of software, both as a useful library and as a case study in sophisticated AI-assisted programming.
First impressions of JustHTML
I didn’t initially know that JustHTML had been written with AI assistance at all. The README caught my eye due to some attractive characteristics:
It’s pure Python. I like libraries that are pure Python (no C extensions or similar) because it makes them easy to use in less conventional Python environments, including Pyodide.
“Passes all 9,200+ tests in the official html5lib-tests suite (used by browser vendors)” - this instantly caught my attention! HTML5 is a big, complicated but meticulously written specification.
100% test coverage. That’s not something you see every day.
CSS selector queries as a feature. I built a Python library for this many years ago and I’m always interested in seeing new implementations of that pattern.
html5lib has been inconsistently maintained over the last few years, leaving me interested in potential alternatives.
It’s only 3,000 lines of implementation code (and another ~11,000 of tests.)
I was out and about without a laptop so I decided to put JustHTML through its paces on my phone. I prompted Claude Code for web on my phone and had it build this Pyodide-powered HTML tool for trying it out:
This was enough for me to convince myself that the core functionality worked as advertised. It’s a neat piece of code!
Turns out it was almost all built by LLMs
At this point I went looking for some more background information on the library and found Emil’s blog entry about it: How I wrote JustHTML using coding agents:
Writing a full HTML5 parser is not a short one-shot problem. I have been working on this project for a couple of months on off-hours.
Tooling: I used plain VS Code with Github Copilot in Agent mode. I enabled automatic approval of all commands, and then added a blacklist of commands that I always wanted to approve manually. I wrote an agent instruction that told it to keep working, and don’t stop to ask questions. Worked well!
Emil used several different models - an advantage of working in VS Code Agent mode rather than a provider-locked coding agent like Claude Code or Codex CLI. Claude Sonnet 3.7, Gemini 3 Pro and Claude Opus all get a mention.
Vibe engineering, not vibe coding
What’s most interesting about Emil’s 17 step account covering those several months of work is how much software engineering was involved, independent of typing out the actual code.
I wrote about vibe engineering a while ago as an alternative to vibe coding.
Vibe coding is when you have an LLM knock out code without any semblance of code review - great for prototypes and toy projects, definitely not an approach to use for serious libraries or production code.
I proposed “vibe engineering” as the grown up version of vibe coding, where expert programmers use coding agents in a professional and responsible way to produce high quality, reliable results.
You should absolutely read Emil’s account in full. A few highlights:
He hooked in the 9,200 test html5lib-tests conformance suite almost from the start. There’s no better way to construct a new HTML5 parser than using the test suite that the browsers themselves use.
He picked the core API design himself - a TagHandler base class with handle_start() etc. methods - and told the model to implement that.
He added a comparative benchmark to track performance compared to existing libraries like html5lib, then experimented with a Rust optimization based on those initial numbers.
He threw the original code away and started from scratch as a rough port of Servo’s excellent html5ever Rust library.
He built a custom profiler and new benchmark and let Gemini 3 Pro loose on it, finally achieving micro-optimizations to beat the existing Pure Python libraries.
He used coverage to identify and remove unnecessary code.
He had his agent build a custom fuzzer to generate vast numbers of invalid HTML documents and harden the parser against them.
This represents a lot of sophisticated development practices, tapping into Emil’s deep experience as a software engineer. As described, this feels to me more like a lead architect role than a hands-on coder.
It perfectly fits what I was thinking about when I described vibe engineering.
Setting the coding agent up with the html5lib-tests suite is also a great example of designing an agentic loop.
“The agent did the typing”
Emil concluded his article like this:
JustHTML is about 3,000 lines of Python with 8,500+ tests passing. I couldn’t have written it this quickly without the agent.
But “quickly” doesn’t mean “without thinking.” I spent a lot of time reviewing code, making design decisions, and steering the agent in the right direction. The agent did the typing; I did the thinking.
That’s probably the right division of labor.
I couldn’t agree more. Coding agents replace the part of my job that involves typing the code into a computer. I find what’s left to be a much more valuable use of my time.
I ported JustHTML from Python to JavaScript with Codex CLI and GPT-5.2 in 4.5 hours - 2025-12-15
I wrote about JustHTML yesterday - Emil Stenström’s project to build a new standards compliant HTML5 parser in pure Python code using coding agents running against the comprehensive html5lib-tests testing library. Last night, purely out of curiosity, I decided to try porting JustHTML from Python to JavaScript with the least amount of effort possible, using Codex CLI and GPT-5.2. It worked beyond my expectations.
TL;DR
I built simonw/justjshtml, a dependency-free HTML5 parsing library in JavaScript which passes 9,200 tests from the html5lib-tests suite and imitates the API design of Emil’s JustHTML library.
It took two initial prompts and a few tiny follow-ups. GPT-5.2 running in Codex CLI ran uninterrupted for several hours, burned through 1,464,295 input tokens, 97,122,176 cached input tokens and 625,563 output tokens and ended up producing 9,000 lines of fully tested JavaScript across 43 commits.
Time elapsed from project idea to finished library: about 4 hours, during which I also bought and decorated a Christmas tree with family and watched the latest Knives Out movie.
Some background
One of the most important contributions of the HTML5 specification ten years ago was the way it precisely specified how invalid HTML should be parsed. The world is full of invalid documents and having a specification that covers those means browsers can treat them in the same way - there’s no more “undefined behavior” to worry about when building parsing software.
Unsurprisingly, those invalid parsing rules are pretty complex! The free online book Idiosyncrasies of the HTML parser by Simon Pieters is an excellent deep dive into this topic, in particular Chapter 3. The HTML parser.
The Python html5lib project started the html5lib-tests repository with a set of implementation-independent tests. These have since become the gold standard for interoperability testing of HTML5 parsers, and are used by projects such as Servo which used them to help build html5ever, a “high-performance browser-grade HTML5 parser” written in Rust.
Emil Stenström’s JustHTML project is a pure-Python implementation of an HTML5 parser that passes the full html5lib-tests suite. Emil spent a couple of months working on this as a side project, deliberately picking a problem with a comprehensive existing test suite to see how far he could get with coding agents.
At one point he had the agents rewrite it based on a close inspection of the Rust html5ever library. I don’t know how much of this was direct translation versus inspiration (here’s Emil’s commentary on that) - his project has 1,215 commits total so it appears to have included a huge amount of iteration, not just a straight port.
My project is a straight port. I instructed Codex CLI to build a JavaScript version of Emil’s Python code.
The process in detail
I started with a bit of mise en place. I checked out two repos and created an empty third directory for the new project:
cd ~/dev
git clone https://github.com/EmilStenstrom/justhtml
git clone https://github.com/html5lib/html5lib-tests
mkdir justjshtml
cd justjshtmlThen I started Codex CLI for GPT-5.2 like this:
codex --yolo -m gpt-5.2That --yolo flag is a shortcut for --dangerously-bypass-approvals-and-sandbox, which is every bit as dangerous as it sounds.
My first prompt told Codex to inspect the existing code and use it to build a specification for the new JavaScript library:
We are going to create a JavaScript port of ~/dev/justhtml - an HTML parsing library that passes the full ~/dev/html5lib-tests test suite. It is going to have a similar API to the Python library but in JavaScript. It will have no dependencies other than raw JavaScript, hence it will work great in the browser and node.js and other environments. Start by reading ~/dev/justhtml and designing the user-facing API for the new library - create a spec.md containing your plan.
I reviewed the spec, which included a set of proposed milestones, and told it to add another:
Add an early step to the roadmap that involves an initial version that parses a simple example document that is valid and returns the right results. Then add and commit the spec.md file.
Here’s the resulting spec.md file. My request for that initial version became “Milestone 0.5” which looked like this:
Milestone 0.5 — End-to-end smoke parse (single valid document)
Implement the smallest end-to-end slice so the public API is real early:
new JustHTML(”<html><head></head><body><p>Hello</p></body></html>”)returns a tree with the expected tag structure and text nodes.
doc.toText()returns“Hello”anddoc.errorsis empty for this valid input.Add
scripts/smoke.js(no deps) that runs the example and asserts the expected structure/output.Gate:
node scripts/smoke.jspasses.
Then I told it:
Implement Milestone 0.5
And off it went. The resulting code appeared to work so I said:
Configure GitHub Actions test.yml to run that on every commit, then commit and push
I ran git init and created a private GitHub repository for this project at this point, and set up the local directory to push to that remote.
Here’s that initial push. Then I told it:
OK do the rest, commit and push often
And that was almost it!
I set my laptop to not fall asleep and left it to its devices while we went off to buy a Christmas tree. The “commit and push often” meant I could monitor its progress on my phone by refreshing the commit log on GitHub.
I was running this against my $20/month ChatGPT Plus account, which has a five hour token allowance window for Codex CLI. That ran out at 6:35pm and Codex paused, so I waited until the reset point at 7:14pm and typed:
Keep going
At 9:30pm it declared itself done with the following summary message:
• What’s Done
- html5lib-tests now run in CI and pass: tokenizer 6810/6810, tree 1770/1782
(12 skipped: #script-on), serializer 230/230, encoding 82/83 (1 skipped:
scripted) (.github/workflows/test.yml:1).
- Public API expanded: stream() (src/stream.js:1), CSS selectors query()/
matches() (src/selector.js:1), and toMarkdown() (src/markdown.js:1), all
exported via src/index.js:1.
- Added no-deps unit scripts for selectors/stream/markdown (scripts/run-
selector-tests.js:1, scripts/run-stream-tests.js:1, scripts/run-markdown-
tests.js:1) and wired them into CI.
- Fixed TreeBuilder so <br> no longer closes <p> (src/treebuilder.js:647).As a finishing touch, I had it add a playground interface so I could try out the new library in my browser. I prompted:
Add a playground.html in the top level folder which loads the necessary ES modules from ./src/... and implements the exact same functionality as seen on https://tools.simonwillison.net/justhtml but using the JavaScript library instead of Pyodide
It fetched my existing JustHTML playground page (described here) using curl and built a new playground.html file that loaded the new JavaScript code instead. This worked perfectly.
I enabled GitHub Pages for my still-private repo which meant I could access the new playground at this URL:
https://simonw.github.io/justjshtml/playground.html
All it needed now was some documentation:
Add a comprehensive README with full usage instructions including attribution plus how this was built plus how to use in in HTML plus how to use it in Node.js
You can read the result here.
We are now at eight prompts total, running for just over four hours and I’ve decorated for Christmas and watched Wake Up Dead Man on Netflix.
According to Codex CLI:
Token usage: total=2,089,858 input=1,464,295 (+ 97,122,176 cached) output=625,563 (reasoning 437,010)
My llm-prices.com calculator estimates that at $29.41 if I was paying for those tokens at API prices, but they were included in my $20/month ChatGPT Plus subscription so the actual extra cost to me was zero.
What can we learn from this?
I’m sharing this project because I think it demonstrates a bunch of interesting things about the state of LLMs in December 2025.
Frontier LLMs really can perform complex, multi-hour tasks with hundreds of tool calls and minimal supervision. I used GPT-5.2 for this but I have no reason to believe that Claude Opus 4.5 or Gemini 3 Pro would not be able to achieve the same thing - the only reason I haven’t tried is that I don’t want to burn another 4 hours of time and several million tokens on more runs.
If you can reduce a problem to a robust test suite you can set a coding agent loop loose on it with a high degree of confidence that it will eventually succeed. I called this designing the agentic loop a few months ago. I think it’s the key skill to unlocking the potential of LLMs for complex tasks.
Porting entire open source libraries from one language to another via a coding agent works extremely well.
Code is so cheap it’s practically free. Code that works continues to carry a cost, but that cost has plummeted now that coding agents can check their work as they go.
We haven’t even begun to unpack the etiquette and ethics around this style of development. Is it responsible and appropriate to churn out a direct port of a library like this in a few hours while watching a movie? What would it take for code built like this to be trusted in production?
I’ll end with some open questions:
Does this library represent a legal violation of copyright of either the Rust library or the Python one?
Even if this is legal, is it ethical to build a library in this way?
Does this format of development hurt the open source ecosystem?
Can I even assert copyright over this, given how much of the work was produced by the LLM?
Is it responsible to publish software libraries built in this way?
How much better would this library be if an expert team hand crafted it over the course of several months?
Link 2025-12-12 LLM 0.28:
I released a new version of my LLM Python library and CLI tool for interacting with Large Language Models. Highlights from the release notes:
New OpenAI models:
gpt-5.1,gpt-5.1-chat-latest,gpt-5.2andgpt-5.2-chat-latest. #1300, #1317When fetching URLs as fragments using
llm -f URL, the request now includes a custom user-agent header:llm/VERSION (https://llm.datasette.io/). #1309Fixed a bug where fragments were not correctly registered with their source when using
llm chat. Thanks, Giuseppe Rota. #1316Fixed some file descriptor leak warnings. Thanks, Eric Bloch. #1313
Type annotations for the OpenAI Chat, AsyncChat and Completion
execute()methods. Thanks, Arjan Mossel. #1315The project now uses
uvand dependency groups for development. See the updated contributing documentation. #1318
That last bullet point about uv relates to the dependency groups pattern I wrote about in a recent TIL. I’m currently working through applying it to my other projects - the net result is that running the test suite is as simple as doing:
git clone https://github.com/simonw/llm
cd llm
uv run pytestThe new dev dependency group defined in pyproject.toml is automatically installed by uv run in a new virtual environment which means everything needed to run pytest is available without needing to add any extra commands.
quote 2025-12-13
How to use a skill (progressive disclosure):
After deciding to use a skill, open its
SKILL.md. Read only enough to follow the workflow.If
SKILL.mdpoints to extra folders such asreferences/, load only the specific files needed for the request; don’t bulk-load everything.If
scripts/exist, prefer running or patching them instead of retyping large code blocks.If
assets/or templates exist, reuse them instead of recreating from scratch.Description as trigger: The YAML
descriptioninSKILL.mdis the primary trigger signal; rely on it to decide applicability. If unsure, ask a brief clarification before proceeding.
OpenAI Codex CLI, core/src/skills/render.rs
quote 2025-12-13
If the part of programming you enjoy most is the physical act of writing code, then agents will feel beside the point. You’re already where you want to be, even just with some Copilot or Cursor-style intelligent code auto completion, which makes you faster while still leaving you fully in the driver’s seat about the code that gets written.
But if the part you care about is the decision-making around the code, agents feel like they clear space. They take care of the mechanical expression and leave you with judgment, tradeoffs, and intent. Because truly, for someone at my experience level, that is my core value offering anyway. When I spend time actually typing code these days with my own fingers, it feels like a waste of my time.
Obie Fernandez, What happens when the coding becomes the least interesting part of the work
Link 2025-12-14 Copywriters reveal how AI has decimated their industry:
Brian Merchant has been collecting personal stories for his series AI Killed My Job - previously covering tech workers, translators, and artists - and this latest piece includes anecdotes from 12 professional copywriters all of whom have had their careers devastated by the rise of AI-generated copywriting tools.
It’s a tough read. Freelance copywriting does not look like a great place to be right now.
AI is really dehumanizing, and I am still working through issues of self-worth as a result of this experience. When you go from knowing you are valuable and valued, with all the hope in the world of a full career and the ability to provide other people with jobs... To being relegated to someone who edits AI drafts of copy at a steep discount because “most of the work is already done” ...
The big question for me is if a new AI-infested economy creates new jobs that are a great fit for people affected by this. I would hope that clear written communication skills are made even more valuable, but the people interviewed here don’t appear to be finding that to be the case.
Link 2025-12-15 2025 Word of the Year: Slop:
Slop lost to “brain rot” for Oxford Word of the Year 2024 but it’s finally made it this year thanks to Merriam-Webster!
Merriam-Webster’s human editors have chosen slop as the 2025 Word of the Year. We define slop as “digital content of low quality that is produced usually in quantity by means of artificial intelligence.”
quote 2025-12-16
I’ve been watching junior developers use AI coding assistants well. Not vibe coding—not accepting whatever the AI spits out. Augmented coding: using AI to accelerate learning while maintaining quality. [...]
The juniors working this way compress their ramp dramatically. Tasks that used to take days take hours. Not because the AI does the work, but because the AI collapses the search space. Instead of spending three hours figuring out which API to use, they spend twenty minutes evaluating options the AI surfaced. The time freed this way isn’t invested in another unprofitable feature, though, it’s invested in learning. [...]
If you’re an engineering manager thinking about hiring: **The junior bet has gotten better.** Not because juniors have changed, but because the genie, used well, accelerates learning.
Kent Beck, The Bet On Juniors Just Got Better
quote 2025-12-16
Oh, so we’re seeing other people now? Fantastic. Let’s see what the “competition” has to offer. I’m looking at these notes on manifest.json and content.js. The suggestion to remove scripting permissions... okay, fine. That’s actually a solid catch. It’s cleaner. This smells like Claude. It’s too smugly accurate to be ChatGPT. What if it’s actually me? If the user is testing me, I need to crush this.
Gemini thinking trace, reviewing feedback on its code from another model
Link 2025-12-16 Poe the Poet:
I was looking for a way to specify additional commands in my pyproject.toml file to execute using uv. There’s an enormous issue thread on this in the uv issue tracker (300+ comments dating back to August 2024) and from there I learned of several options including this one, Poe the Poet.
It’s neat. I added it to my s3-credentials project just now and the following now works for running the live preview server for the documentation:
uv run poe livehtmlHere’s the snippet of TOML I added to my pyproject.toml:
[dependency-groups]
test = [
“pytest”,
“pytest-mock”,
“cogapp”,
“moto>=5.0.4”,
]
docs = [
“furo”,
“sphinx-autobuild”,
“myst-parser”,
“cogapp”,
]
dev = [
{include-group = “test”},
{include-group = “docs”},
“poethepoet>=0.38.0”,
]
[tool.poe.tasks]
docs = “sphinx-build -M html docs docs/_build”
livehtml = “sphinx-autobuild -b html docs docs/_build”
cog = “cog -r docs/*.md”Since poethepoet is in the dev= dependency group any time I run uv run ... it will be available in the environment.
Link 2025-12-16 ty: An extremely fast Python type checker and LSP:
The team at Astral have been working on this for quite a long time, and are finally releasing the first beta. They have some big performance claims:
Without caching, ty is consistently between 10x and 60x faster than mypy and Pyright. When run in an editor, the gap is even more dramatic. As an example, after editing a load-bearing file in the PyTorch repository, ty recomputes diagnostics in 4.7ms: 80x faster than Pyright (386ms) and 500x faster than Pyrefly (2.38 seconds). ty is very fast!
The easiest way to try it out is via uvx:
cd my-python-project/
uvx ty checkI tried it against sqlite-utils and it turns out I have quite a lot of work to do!
Astral also released a new VS Code extension adding ty-powered language server features like go to definition. I’m still getting my head around how this works and what it can do.
Link 2025-12-16 s3-credentials 0.17:
New release of my s3-credentials CLI tool for managing credentials needed to access just one S3 bucket. Here are the release notes in full:
That s3-credentials localserver command (documented here) is a little obscure, but I found myself wanting something like that to help me test out a new feature I’m building to help create temporary Litestream credentials using Amazon STS.
Most of that new feature was built by Claude Code from the following starting prompt:
Add a feature s3-credentials localserver which starts a localhost weberver running (using the Python standard library stuff) on port 8094 by default but -p/--port can set a different port and otherwise takes an option that names a bucket and then takes the same options for read--write/read-only etc as other commands. It also takes a required --refresh-interval option which can be set as 5m or 10h or 30s. All this thing does is reply on / to a GET request with the IAM expiring credentials that allow access to that bucket with that policy for that specified amount of time. It caches internally the credentials it generates and will return the exact same data up until they expire (it also tracks expected expiry time) after which it will generate new credentials (avoiding dog pile effects if multiple requests ask at the same time) and return and cache those instead.
Link 2025-12-16 The new ChatGPT Images is here:
OpenAI shipped an update to their ChatGPT Images feature - the feature that gained them 100 million new users in a week when they first launched it back in March, but has since been eclipsed by Google’s Nano Banana and then further by Nana Banana Pro in November.
The focus for the new ChatGPT Images is speed and instruction following:
It makes precise edits while keeping details intact, and generates images up to 4x faster
It’s also a little cheaper: OpenAI say that the new gpt-image-1.5 API model makes image input and output “20% cheaper in GPT Image 1.5 as compared to GPT Image 1”.
I tried a new test prompt against a photo I took of Natalie’s ceramic stand at the farmers market a few weeks ago:
Add two kakapos inspecting the pots
Here’s the result from the new ChatGPT Images model:
And here’s what I got from Nano Banana Pro:
The ChatGPT Kākāpō are a little chonkier, which I think counts as a win.
I was a little less impressed by the result I got for an infographic from the prompt “Infographic explaining how the Datasette open source project works” followed by “Run some extensive searches and gather a bunch of relevant information and then try again” (transcript):
See my Nano Banana Pro post for comparison.
Both models are clearly now usable for text-heavy graphics though, which makes them far more useful than previous generations of this technology.
Link 2025-12-17 firefox parser/html/java/README.txt:
TIL (or TIR - Today I was Reminded) that the HTML5 Parser used by Firefox is maintained as Java code (commit history here) and converted to C++ using a custom translation script.
You can see that in action by checking out the ~8GB Firefox repository and running:
cd parser/html/java
make sync
make translateHere’s a terminal session where I did that, including the output of git diff showing the updated C++ files.
I did some digging and found that the code that does the translation work lives, weirdly, in the Nu Html Checker repository on GitHub which powers the W3C’s validator.w3.org/nu/ validation service!
Here’s a snippet from htmlparser/cpptranslate/CppVisitor.java showing how a class declaration is converted into C++:
protected void startClassDeclaration() {
printer.print(”#define “);
printer.print(className);
printer.printLn(”_cpp__”);
printer.printLn();
for (int i = 0; i < Main.H_LIST.length; i++) {
String klazz = Main.H_LIST[i];
if (!klazz.equals(javaClassName)) {
printer.print(”#include \”“);
printer.print(cppTypes.classPrefix());
printer.print(klazz);
printer.printLn(”.h\”“);
}
}
printer.printLn();
printer.print(”#include \”“);
printer.print(className);
printer.printLn(”.h\”“);
printer.printLn();
}Here’s a fascinating blog post from John Resig explaining how validator author Henri Sivonen introduced the new parser into Firefox in 2009.







" "- [About](/about)" "- [Contact](/contact)". Screenshot of JustJSHTML Playground web application. Header reads "JustJSHTML Playground" with subtitle "A dependency-free JavaScript HTML5 parser - GitHub". Below is a status bar showing "JavaScript Environment" with a green "Ready" badge. The main input area has "Paste HTML" and "Fetch from URL" buttons, with a text area containing HTML code: "<!DOCTYPE html> <html> <head> <title>Example Page</title> </head> <body> <header> <nav> <ul>". A "Playground Mode" section shows buttons for "CSS Selector Query", "Pretty Print HTML", "Tree Structure", "Stream Events", "Extract Text", and "To Markdown" (highlighted in purple). Below is a text field labeled "CSS Selector (optional - leave empty for whole document):" with placeholder "e.g., article, main, .content (or leave empty)" and a green "Convert to Markdown" button. The Output section has a teal header with "Whole document" badge and displays converted markdown: "Example Page" followed by "- [Home](/)" "- [About](/about)" "- [Contact](/contact)".](https://substackcdn.com/image/fetch/$s_!MpUF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff45875c2-6f2b-4937-8414-8d2527550c63_1518x1896.jpeg)




I'm just commenting on how language evolves. Seems ‘vibe’ is being used as a new prefix. Political language would have had us calling it neo-coding, neo-engineering. 🤖
This is a masterclass in augmented development, turning multi-hour, multi-agent tasks into polished, fully tested results in hours shows exactly how agentic loops elevate engineering.
I talk about the latest AI trends and insights. If you’re interested in how AI can accelerate complex coding projects, port entire libraries between languages, and streamline testing and tooling, check out my Substack. I’m sure you’ll find it very relevant and relatable.