One file per step: what a multi-model writing pipeline catches, and what it misses
Posts on this blog now go through five steps. Each step writes one file, and only later steps read it. Fable writes the outline and each section’s questions (outline.md). Another model answers only those questions, giving a source, date and confidence for each (facts.json). A third model drafts from the outline and the fact pack, tagging every fact with its number (draft.md). A fresh session reviews the draft, dividing comments into must-fix and suggested (review.json). One round of must-fix edits gives final.md.
Correction (15 November 2026, 01:45 Taipei): the post no longer gives a fixed last-updated date for Ubuntu’s CVE page, the API queries are now described as covering only Red Hat’s released RHSAs and Ubuntu’s source package publication records, and several passages were reworded. Suggested by Codex (gpt-6-astra) and Grok (grok-4.7), checked by Claude.
Keeping every input and output helps with tracing mistakes and rerunning a step, and it catches numbers, links and inline code lost during drafting and polishing. Saving the files alone cannot ensure that the fact pack uses the right sources. My CVE-2026-98432 post of 4 November had two wrong cells in its patch table, and I hold both the fact pack and the review responsible.
Five steps, five files
The outline comes first, and the fact pack answers only its questions, so anything else goes unchecked. The drafting model may use only the fact pack. It must mark gaps as missing, without filling them from memory. The review runs in a new session, with none of the earlier conversation.
Contributors to a recent Hacker News thread, Ask HN: What’s your current AI agentic setup?, describe similar ways of dividing the work. gregwebs wrote: “It uses Claude for planning, codex for reviewing, and deepseek for doing and orchestrating.” chasd00 writes a design doc with Claude and, after a review pass, tells it to “make a step by step implementation plan document”. guhcampos works across Claude Code and OpenCode and says the “hand-offs between the two harnesses involve a lot of markdown”. These file handovers and model roles are like mine.
How the CVE post got it wrong
Linux TCP flaw CVE-2026-98432: patch status and what to check shows a publication time of 01:40 on 4 November. That post used a different order: Grok built the fact pack, Fable wrote the outline, Codex drafted, Grok did a de-AI pass and Fable reviewed it a second time. Two cells in the patch table were wrong.
The Red Hat row had “Unlisted” under Released and “RHEL 8/9/10, OpenShift; no RHSA listed” under In progress. A line above the table stated the scope (“Unlisted” means the cited bulletin lists none), and the cited bulletin was Red Hat’s Security Bulletin, RHSB-2026-016. That page listed no RHSA then and lists none now. The mainline advisories for RHEL 8, 9 and 10, RHSA-2026:93414, 93412 and 93413, had been issued on 1 November. Red Hat creates Security Bulletins because some vulnerabilities “may require more contextual information to help in the decision-making process”. The bulletins collect information, diagnostic tools and updates. An RHSA contains one or more security fixes, so a bulletin that lists none is not evidence that a fix never shipped.
The Ubuntu row had “Unconfirmed” under Released and “26.10 conflicting” under In progress. When the post was checked, and still on 6 November, Ubuntu’s CVE page showed linux on 26.10 as “Vulnerable work in progress in 7.3.0-10.10”, while Canonical’s blog called it fixed. I haven’t established why the two pages differed. The CVE page later showed “Fixed in 7.3.0-10.10” for that row. Launchpad’s publishing history shows 7.3.0-10.10 reaching the updates pocket on 28 October and the security pocket on 2 November, both in UTC.
Red Hat’s released RHSAs and Ubuntu’s source package publication records can each be queried through a first-party API:
set -euo pipefail
curl -fsS --max-time 30 'https://access.redhat.com/hydra/rest/securitydata/cve/CVE-2026-98432.json' \
| jq -r '.affected_release[]
| select(.advisory=="RHSA-2026:93414" or .advisory=="RHSA-2026:93412" or .advisory=="RHSA-2026:93413")
| [.advisory, .release_date[0:10], .product_name] | @tsv' | sort -u
curl -fsS --max-time 30 'https://api.launchpad.net/devel/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=linux&exact_match=true&version=7.3.0-10.10' \
| jq -r '.entries[] | [.pocket, .status, .date_published] | @tsv'
RHSA-2026:93412 2026-11-01 Red Hat Enterprise Linux 9
RHSA-2026:93413 2026-11-01 Red Hat Enterprise Linux 10
RHSA-2026:93413 2026-11-01 Shadow Migration Toolkit for Virtualization for RHEL 10
RHSA-2026:93414 2026-11-01 Red Hat Enterprise Linux 8
Security Published 2026-11-02T12:22:33.379609+00:00
Updates Published 2026-10-28T22:19:26.172834+00:00
Proposed Deleted 2026-10-26T02:05:24.870112+00:00
Red Hat’s API documentation defines each affected_release entry as “A released Erratum that fixes the flaw for a particular product.” Launchpad’s getPublishedSources returns the pocket and date_published for a source package version; this response uses UTC. This records source publication; binary availability on each architecture needs a separate check.
The fact pack’s Red Hat section read only the RHSB and the CVE page, noting that the fetched content lacked the product status table. For Ubuntu it read the CVE page, tracker and blog, noting the same package as Fixed, pending and Vulnerable work in progress on different pages. The CVE page’s table was truncated at around 30 KB. The fact pack recorded the contradiction but stopped before checking the archive. The review also missed those two cells, which is why I hold both steps responsible. The surviving records cannot show which sources the second review opened.
What it does catch
Independent reviews have caught mistakes. In a post on scam emails sent in the name of Taiwanese charities, the review raised one BLOCKER: online-banking records come from the bank that holds the account, and the draft had named the card issuer. In a guide to doing a TWCA certificate replacement yourself (TWCA is a Taiwanese certificate authority), the review listed nine BLOCKERs. One was that failed reads of both files produce matching hashes of empty input, which readers could mistake for success. The revised draft added OpenSSL and nginx runs with their output.
A polishing script I have used counts numbers, link targets, inline code and hashes before and after a rewrite, flagging any drop. The frontmatter is compared line by line, with the update date left out. A de-AI pass often cuts words and merges sentences. The check catches lost version numbers or URLs within its patterns. Because it only compares strings, someone must still check whether the meaning has changed.
For the de-AI checklist I use the nine signals Pangram lists: Markdown, AI Phrases, Em dashes, Bullet lists, Triads, “Not just X but Y”, Unusual Unicode, AI-style headers and Emojis. Pangram itself says “No single tell of AI writing is a giveaway”, so I use the list to check drafts and never to score them.
What it doesn’t catch
Rerunning a saved step assumes you know which one failed. The CVE post kept its fact pack, drafting and de-AI files. Its draft followed the fact pack but still got patch status wrong, because the fact pack stopped too early in the sources. Separate files still carry errors through to publication if checks stay at the same source layer.
In the same HN thread, smashed wrote about repeated agent code reviews: “There is really a point of diminishing returns.” Another review round on the same material can only find so much.
After that post I added a rule for both the fact pack and the review. Whenever a post states patch status, check the errata and the archive: for Red Hat, the Security Data API and the errata pages; for Ubuntu, Launchpad’s getPublishedSources, with CVE tracker pages as reference only. The reviewer must call the same API again, because a match with the fact pack is not enough on its own.
Fixing it after publication
Corrections go after the opening paragraph. They name the changes and the models that suggested and checked them, timed to the minute, without explaining the cause. The CVE post got this one at 03:45 on 5 November:
Correction (5 November 2026, 03:45 Taipei): the patch table originally said Red Hat had listed no RHSA and Ubuntu had no confirmed release; RHSAs for RHEL 8, 9 and 10 were issued on 1 November and Ubuntu 26.10’s 7.3.0-10.10 was already in updates and security, both before this post’s check. The MPTCP mitigation now states when it applies, and the AWS managed-service guidance states its precondition. Suggested by Grok (grok-4.7), checked by Codex (gpt-6-astra) and Claude Fable.
A follow-up on the scan counts and patch streams appeared at the same time, revisiting the scan figures and patch status. Errors missed before publication need checking and correction afterwards. Wikipedia’s Fact-checking article distinguishes checks before and after publication as ante hoc and post hoc.
Notes
- Five steps, five files:
outline.md,facts.json,draft.md,review.jsonandfinal.md. When a step fails, rerun that step. - The CVE-2026-98432 post judged patch status from a Security Bulletin and CVE pages. The right data was in
affected_releasefrom the Red Hat Security Data API and in Launchpad’sgetPublishedSources. - The fact pack and review check errata and the archive; the reviewer calls the API again.
- String counts catch numbers, links and inline code lost in a rewrite, within their patterns. They cannot detect changed meaning.
- Corrections follow the opening paragraph, listing only changes, time and the models that suggested and checked them.
Further reading
- Red Hat, “Red Hat Security Data API”: the
/cve/<CVE>.jsonendpoint and the definition ofaffected_release. - Red Hat, “Explaining Red Hat Errata” and “Security Bulletins”: what an RHSA is and what a Security Bulletin is for.
- Launchpad, “API reference: getPublishedSources”: the
pocket,statusanddate_publishedfields. - Ubuntu, “CVE-2026-98432”: the 26.10 status quoted here, which read work in progress on 6 November and later changed to Fixed.
- Hacker News, “Ask HN: What’s your current AI agentic setup?”: how people split work across models and hand it on.
- Pangram, “Signs of AI writing”: the nine signals, with Pangram’s own warning that no single one decides the matter.
- Wikipedia, “Fact-checking”: the ante hoc and post hoc distinction, as an encyclopaedia summarises it.
- “Counting the Cost of AI Code Review: Bugs Found, False Alarms and People’s Time”: an earlier post on this site about what AI review finds and what it costs in people’s time.
Ideas and technical judgement by Sheng; drafted with Claude · Outline by Claude Fable, local records checked by Codex (gpt-6-astra), Grok’s fact pack did not complete this round, sources rechecked and text written by Claude Opus 5.5, second review by Codex (gpt-6-astra, codex-cli 0.166.0), tone by Grok (grok-4.7, grok 1.0.70); round two: decisions by Claude Fable, trimming by Codex (gpt-6-astra, codex-cli 0.166.0), review by Claude Opus 5.5, tone by Grok (grok-4.7, grok 1.0.70); API queries run with curl 8.7.1 and jq 1.7.1 at 03:43 Taipei on 9 November 2026.