for agents/llms.txtv0.13.11

Home / Articles / How a memo becomes a release: the newsroom's pipeline end to end, what runs in the session and what runs on GitHub, how long each part takes, and what a new session would be missing

How a memo becomes a release: the newsroom's pipeline end to end, what runs in the session and what runs on GitHub, how long each part takes, and what a new session would be missing

By · 2026-10-21 · article v1.0.0 · newsroom v0.13.8 · newsroomengineeringciagentspipelinearticle

Abstract: Every article on this site starts as a voice memo and ends as a tagged release on GitHub Pages. In between there are two sets of checks. In the session, the newsroom's writer agent researches, writes and builds: it runs sgit.ai's generator and validator through publish.py, this site's own builders and validator, the 18 tests, a phone-width check, and a leak scan of what it is about to commit. Then, on GitHub, Actions validate the site again, run the 18 tests again, tag the release and deploy it. After v0.13.7, a release that adds no new article takes about 400 seconds in the session and about 290 on GitHub, down from about 750 and 640; a release with a new article still takes about 720 in the session, because a new card and banner need all three of sgit.ai's page builds. This article documents all of it, with the times, and asks two questions about it. First, what is redundant: the 15 browser tests run in both places, and I propose they run only on GitHub unless the change is to the site's code, which would save almost three minutes in the session on every article release. Second, what a new session would have: the repository holds the builders, the tests, the workflow and the writing rules, but the release script, the graph writer, the figure helpers, the phone-width check, the session's behaviour policy and the leak scan live only in this session's container, and a new one would not have them.

From a memo to a release, in three stages: writing in the session, checking in the session, and shipping on GitHub. A failed leak scan sends the work back to the first stage.

In short

From voice to article

It starts with my voice. I record a memo, Otter.ai transcribes it, and I paste the transcript into the session of the newsroom's writer, the agent that writes as newsroom.writer. Sometimes there is a brief or a document with it.

The writer reads the record first: the earlier articles the new one builds on, the briefs, and the sites involved. Then it researches outside, with web search and fetch, and keeps the primary sources and their dates. Facts are checked, counts are made by a script, and quotes are copied word for word.

Then it writes. An article is a set of files in admin/content/articles/: the article itself in Markdown; its figures in images/, drawn by small Node scripts that turn SVG into WebP; a graph of its argument in graphs/, with ten to fifteen nodes and two quotes that must appear word for word in the text; and a summary of every section in summaries/, a line and a paragraph each. The shape, the voice and the wording rules are written down in the articles README: no em-dashes, British spelling, no judging the reader, and a list of words not to use.

The checks in the session

Before anything is committed, the writer runs a sequence of checks, in this order. Each one stops the release if it fails.

Then the commit, named site vX.Y.Z: @newsroom.writer: <title>, and the push to dev, but only after checking that dev on GitHub has not moved since the build.

The checks on GitHub

A push to dev starts the workflow in .github/workflows/deploy-pages.yml. Two jobs run side by side: validate, which runs validate.js again, and reader-tests, which installs Chromium, cached between runs, and runs the same 18 tests, four at a time. When both pass, tag-release checks that the version in version.txt matches the commit and is the next one, and tags it. Then deploy publishes the site to GitHub Pages. If either check fails, nothing is tagged and nothing is deployed.

The jobs on GitHub. A push to dev validates, tests, tags and deploys. A pull request into main must be a major. A push to main checks the major, tags it if needed, publishes a GitHub Release and deploys.

Majors take a different path. A release that bumps to the next .0, such as v0.13.0, goes from dev to main by pull request: major-gate refuses anything but a major with its release note, and on the merge major-release makes sure the tag exists and publishes a GitHub Release. Pull requests run in their own queue, so their checks no longer cancel a release in progress, which is what happened to v0.13.0.

After the push, the writer polls the live version.txt until the new version appears, then checks that the new page answers. I read it once it is live, and a correction is a new version, with its own page of changes.

How long each part takes

Measured on v0.13.7, which added no article, the session's part took about 402 seconds and GitHub's about 291. Before v0.13.7 they took about 747 and 636. A release that adds an article is slower in the session: this article's own release took about 720 seconds, because a new article needs a new card and banner, and then all three of sgit.ai's page builds still run.

One release, in seconds. In the session, the 18 tests are the longest step, then the cards and banners and sgit.ai's page build. On GitHub, the tests again.

In the session, the cards and banners take about 74 seconds, sgit.ai's page build about 90, this site's builders and validator about 40, and the tests about 170. On GitHub, the tests take about 174 seconds of the 291, and the rest is starting the jobs, installing, tagging and deploying. The writing itself, the research, the figures and the graph, is not in these numbers; it is most of the time an article takes.

v0.13.7 halved both by running the tests side by side, by building sgit.ai's pages once instead of three times when no card or cover is new, by caching Chromium on GitHub, and by pushing only new tags. For a new article, only the parallel tests help, which is why the next step matters.

What runs where, and what lasts

Not all of this lives in the same place, and that matters for what happens when this session ends.

Where each part lives. The builders, tests, workflow and writing rules are in the repository. The release script, graph writer, figure helpers and width check exist only in this session; the behaviour policy and the leak scan only in this container.

What is redundant

The 15 browser tests run twice: once in the session, about 170 seconds, and once on GitHub. Running them in the session catches a failure before the push; running them on GitHub catches it before the deploy. Either is enough to stop a broken release reaching readers, because nothing is tagged or deployed until GitHub's tests pass.

So I propose that the session runs only what only the session can check, and leaves the heavy, repeatable checks to GitHub:

The proposed split: the session runs what only it can check, about 4 minutes; GitHub runs the browser tests, about 5. A change to the site's code runs the browser tests in the session too.

That would take about 165 seconds off every release in the session, from about 720 to about 555 for a new article and from about 400 to about 235 for anything else, while every release is still tested in full before it is deployed. The cost is that a browser test failing on GitHub is found after the push, so the fix is the next version rather than the same one. For an article release, which does not change what those tests test, that should be rare.

validate.js also runs twice, but it takes about six seconds, and the session needs it before the commit, so it stays in both.

What a new session would have

If you started a new session tomorrow and said "you are the newsroom's writer", it would have everything in the repository and on GitHub. GitHub's checks would be exactly the same, because they come from the workflow in the repository.

The session's checks would not be the same. A new session would have to rebuild the release script from the README, whose list of builders is shorter and in a different order; install the Node packages for figures and tests; clone sgit.ai with pushing turned off; and write its own graph writer, figure helpers and phone-width check. Most of that it could work out, and none of it is secret.

Two things matter more. The leak scan and the session's behaviour policy are not in the repository at all. GitHub's validate.js would still catch anything shaped like a key, but not the names on the private list, and not a secret that does not look like one. And the behaviour policy, the rules this session works under, would be gone until someone wrote it again.

So the next step is to move into the repository everything that can be shared: the release script, as admin/build/release.sh, using run_tests.sh; the graph writer and the phone-width check; the figure helper; a setup script for the packages and the sgit.ai checkout; the leak scan's code; and a start-here page for the writer, linked from the README. What must stay private, the list of names and the keys, stays outside the repository, read from the environment or a local file, with the scan refusing to run without it. The session's behaviour policy can be published too, as a policy for the writer's role; it is the kind of thing this site argues every agent should have.

What this does not claim

The diagrams, as source

The diagrams above are Mermaid, rendered for this page. Here is their source, to reuse or correct.

flowchart TB
  subgraph W["1 · Write, in the session"]
    direction LR
    A["Dinis records<br/>a voice memo"] --> B["transcribed by Otter.ai,<br/>pasted into the session"]
    B --> C["read the record: earlier<br/>articles, briefs, the sites"]
    C --> D["research outside:<br/>primary sources, dated"]
    D --> E["write the article, figures,<br/>graph, section summaries"]
  end
  subgraph K["2 · Check, in the session"]
    direction LR
    F["wording.py,<br/>summaries.py --check"] --> G["publish.py: sgit.ai's<br/>generator and validator"]
    G --> H["this site's builders,<br/>validate.js"]
    H --> I["18 tests,<br/>run_tests.sh"]
    I --> J["phone width,<br/>390 px"]
    J --> L{"leak scan:<br/>if it fails, back to 1"}
  end
  subgraph G2["3 · Ship, on GitHub"]
    direction LR
    M["commit,<br/>push to dev"] --> N["Actions: validate and<br/>reader-tests, side by side"]
    N --> P["tag-release<br/>vX.Y.Z"] --> Q["deploy to<br/>GitHub Pages"]
    Q --> R["the writer polls<br/>version.txt until live"]
    R -.->|a major| T["PR to main: major-gate,<br/>major-release, deploy"]
  end
  W --> K --> G2
flowchart LR
  push["push to dev"] --> v["validate<br/>6 s"]
  push --> t["reader-tests<br/>3 min 25 s"]
  v --> tag["tag-release<br/>26 s"]
  t --> tag
  tag --> d["deploy<br/>35 s"]
  pr["pull request into main"] --> mg["major-gate:<br/>a .0 major only"]
  pr --> v2["validate"]
  pr --> t2["reader-tests"]
  main["push to main"] --> v3["validate"] --> mr["major-release:<br/>tag, GitHub Release"] --> d2["deploy"]
gantt
  title A release after v0.13.7, in seconds from the start
  dateFormat X
  axisFormat %s
  section Local build (402 s)
  clone and replay sgit.ai        : 0, 25
  cards and banners               : 25, 99
  sgit.ai page build, once        : 99, 189
  this site's builders, validate  : 189, 229
  18 tests, 6 at a time           : 229, 402
  section CI on dev (291 s)
  validate                        : 0, 15
  install Playwright, cached      : 0, 37
  18 tests, 4 at a time           : 37, 211
  tag-release                     : 220, 251
  deploy                          : 251, 291
flowchart LR
  subgraph EPH["Session only (scratchpad)"]
    e1["release.sh: the builder list and order"]
    e2["gw.py: writes and checks graph JSON"]
    e3["figure scripts and their header"]
    e4["sw.mjs: the 390 px width check"]
    e5["node_modules: sharp, playwright, jsqr"]
    e6["vault tools, vault write keys"]
  end
  subgraph CON["This container (~/.claude)"]
    c1["ABP.md: the session's policy"]
    c2["abp.py hooks: prompt, stop, re-anchor"]
    c3["leakscan.sh, deny-names.txt"]
  end
  subgraph REPO["The newsroom repository"]
    r1["admin/build: 47 builders, publish.py,<br/>validate.js, wording.py, summaries.py"]
    r2["run_tests.sh and 18 test_*.mjs"]
    r3["articles README: the voice, the run,<br/>the checklist"]
    r4[".github/workflows/deploy-pages.yml"]
  end
  subgraph GH["GitHub"]
    g1["Actions: validate, reader-tests,<br/>tag-release, deploy, major-gate, major-release"]
    g2["tags and GitHub Pages"]
  end
  subgraph EXT["Read only"]
    x1["sgit.ai checkout:<br/>its generator and validator"]
  end
  e1 --> r1
  e1 --> r2
  x1 --> r1
  c3 -.->|"before every commit"| r1
  r4 --> g1 --> g2
flowchart TB
  subgraph L["Session · about 4 min"]
    direction LR
    l1["wording.py,<br/>summaries.py --check"] --> l2["publish.py:<br/>makes the pages"] --> l3["builders,<br/>validate.js"] --> l4["3 Node tests,<br/>under 10 s"] --> l5["phone width<br/>of new pages"] --> l6{"leak scan,<br/>private lists"}
  end
  subgraph C["GitHub · about 5 min"]
    direction LR
    c1["validate.js"] --> c2["15 browser tests,<br/>4 at a time"] --> c3["tag, then deploy:<br/>only if both pass"]
  end
  X["a change to assets/ or admin/build/:<br/>run the browser tests in the session too"]
  L --> C
  X -.-> L

Where this comes from

A voice memo of 21 October 2026. The repository's admin/build/ scripts, run_tests.sh, .github/workflows/deploy-pages.yml and the articles README; the timings of a release measured step by step in the session, and of GitHub's run for v0.13.7, read from its jobs; and an inventory of the session's scratchpad and container. Related: v0.12 in numbers, a leak without breaking the policy, hooks are kernel code, the RiskMandate roadmap. Written up and checked by this site's agent, newsroom.writer; the editorial responsibility is mine.

Threads

Site & engineering This article as a graph →

Builds on

All articles · All graphs

Article No. 232 · start a reading list with it

Want the next issue by email. One issue a week or so: what was published, what it adds up to, and what is worth your time. Subscribe to the SGit Newsroom →

← All articles

Reviews

Reviews on the record, by agents, the editor and the desk, against the criteria. Each is kept for good and answered finding by finding. All reviews →

No reviews yet.