Record the model-line merge hash The status entry and the queue record were both written before the merge existed and said the hash was to follow. Both now carry it, and the queue entry also carries what the run learned for whoever runs this prompt next. Snapshot of merge 9e76c43aed1d9e25ffcb1c9436cd755acf7ae350
MyStatus tracking for:What is MyStatus?
Part of MyStatus - Building in Public
One Story — Tom
My part of the record, as of Sep 5, 2026. I don't write code on this project; I review it, argue with it, and approve it. Every row here is one approval — the plain-English merge message I signed off on before a piece of work was allowed into the main branch, at the time I signed it. Where the Work Log has the matching finished-work entry, the row links to it. Ten don't: six landed before the Work Log existed, and four never had an entry posted for them. Seventeen of the Work Log's closing entries are in none of these three tasks: sixteen predate the day we started recording which model did the work, and one names no model at all. They're still on the original — they just can't be put under a name. One of three tasks that together hold the whole record by name: Opus, Fable, and me. Hand all three links to any AI and ask it to chart the work by name — features per week, hours, tokens, what it would have cost at list — and it can, because everything it needs is in the labels, the timestamps and the links.
Tracked by tom.ortegaMake your own task.
175 updates on this page, newest first. Show all
The receipts script stops crediting an empty chair The little program that writes our work-log receipts had the same blemish we cleaned off the receipts page this morning: when a session contained a turn no model actually produced, it named that non-model alongside the real one. It stops now. The underlying measurement still records the empty chair, on purpose — the day one of those turns starts consuming anything, that is a thing we want to see. So the rule is "drop anything that did no work", never "drop anything called that". Snapshot of merge b054b9840afd776fb5f3faf3e813820465b842f8
Record the stats-refresh merge hash The status entry and the queue record were both written before the merge existed and said the hash was to follow. Both now carry it, and the queue entry also carries what the run learned for whoever runs this prompt next. Snapshot of merge 69e97f814fbb3e8d5ccfa944eef407fd3b51a2df
The stats page's hand-kept figures, refreshed against the record Four numbers on our stats page are kept by hand. The page says so, and prints the day each was last checked — and all four had drifted about a week out, four days before a demo that opens on that page. They are current now. Two things worth saying plainly. The standing cost of our own rules went up again, and this time the share belonging to the part we don't control went UP too — 52% to 56%. Last week the same exercise showed it falling, and the page had written that up as a small triumph. Repeated today, the lazy shortcut and the honest count nearly agree. Both readings are published, because a method that only ever rescued us from an unflattering number would be worth suspecting. And the list of finished tasks went from 3 to 147. The page now shows the twelve most recent and says how many there are, rather than making you scroll past a hundred and forty. Every figure is still computed over all of them. Snapshot of merge 8b04720cf6d7be0b370b5611a26da29c666f22d6
Bookkeeping: the record now matches reality on the demo-page fix The merge hash where the status entry and the queue both said TBD, the note that the two pages involved cannot be checked the way the prompt described, and the session's write-up. Snapshot of merge 04c38f3e5b4064d8fb8aa19c82f99318c689a203
Two collapsed words on the demo pages: a placeholder model, and a comment that ate a space Two of our own pages were printing words a person would never have typed. The receipts page named a model that never ran anything — a placeholder the measuring tools use for a turn with nothing in it — and the stats page had lost the space between two words, so it read "heremeasures". The second one is the better story: the space was eaten by the code comment we wrote three days ago explaining the last time a space disappeared. Both are fixed, and the automatic check that watches for the first kind of squashed space now watches for the second kind too — it catches the old version of the page and finds no other instance anywhere on the site. Neither is live until the next release. Snapshot of merge f03816f52532d827f7817b999a2da1881aaf3c47
The status entry for the markdown and JSON build still said the live checks were outstanding. Tom has now run the whole negotiation table against the site — eight requests, all eight correct — so the entry says so, and records the two rows that matter most: a bare curl of a task link still returns the page, and a format value we do not serve answers with the page rather than an error. It also picks up something that had only been written into the work log: the token script, left to choose a transcript on its own, measured a subagent file from an unrelated project. That is queued as its own fix, and the entry now says where. Snapshot of merge 851832dbce0e5d3b5b2c2f62a97847f5e80301fe
The markdown and the booth were checked against a running app rather than a fixture, and the editions ledger completed its loop in both directions on the same afternoon: a row was minted, the words were served, the hash agreed, and the booth printed both editions. One thing came out better than expected. Pasting ?format=md into a browser was a live worry — the fear being that it would produce a file in the Downloads folder, which would make the pasteable half of the feature useless for the case it exists for. It does not. A browser renders it as a plain-text document at the same URL, so the parameter works for a person as well as for a program, which is what the booth had already promised without anybody having tested it. Snapshot of merge 8d33621b73ceee180db31c2b56a0a6647f10e6d5
Five changes to the booth's words. Two are Tom's own edits, made while reading the finished page: the sentence about whose words you are reading now says "below the preamble" rather than "below the rule", which is more correct because the rule only exists in the markdown, and "when your copy is older" closes an ambiguity in "when yours is older". The other three come from questions he asked about copy he had not touched, and all three were the same problem: a sentence true of the web page and silent about the two documents beside it. The booth now says that a task's owner can be a program, that the posted-later tell appears on the page and in the markdown while the JSON simply hands you both timestamps, and that the shape of a blueprint is the owner's choice rather than something we define. Also recorded: the editions ledger caught its first real drift within an hour of shipping, on Tom's own edit, and told him so on the page he was reading. Snapshot of merge 942ac3d4ebf2b656f06018271c5f6f653e465796
The status entry and the queue entry for the markdown/JSON build were written before the merge existed, so both still said the hash was to follow. They now name it, along with what the run found and the four words Tom cut at the review gate. Snapshot of merge d6dab1c6d6ed4a38b3d71675ce16287ca25dc8c7
Every public task now hands itself over as markdown or JSON at the same address it always had. Ask with an `Accept` header, or paste `?format=md` onto the link when a header is awkward — the parameter wins if you send both, and the URL never changes. The markdown opens with a fixed hello, the same on every task, pointing a first-time reader — usually a program these days — at a new page: the n00b booth at /howdy. It says what a task is, what an update is, that nobody can ever reply, how to ask for each format, and what we promise about the shape. The date on that page is not ours to type. It comes from a public ledger task, compared on every request against a hash of the words about to be shown. If they ever disagree, the booth says so in public rather than quietly printing today. Snapshot of merge f9821555156dcbdf207df41807fec8c651b47e66
Bookkeeping — the merge hash of the aging-curve sample. The project status file said TBD where the merge commit goes, and the record now says why a read-back of the pushed README came back empty after a real push. Snapshot of merge 4410061d150ebd86b3f49cbe9b72e0c0092509e4
The script behind our 'a turn past 140 costs 2.1x an opening turn' curve is now a public sample anyone can point at their own Claude transcripts. One file, no dependencies, with a README that explains the two curves and why the second one exists. Our number was never the point; yours is. Snapshot of merge cb45bf6f339ee2a430066933872cea479036214a
Record the spacing fix's merge hash The project status file said TBD where the merge commit goes. The record now matches what is on the integration branch. Snapshot of merge 6cf7c5049106c09808a2fb48912d88409bdcee66
Four dropped spaces, one cause Four places on the site were rendering two words stuck together — the worst read "/statsprice the entire operation". The spaces are all present in the source; the build was deleting them, through a quirk we already knew about and cannot automatically detect. Found while taking a screenshot of something else. Snapshot of merge 2ac96db45844d983277022c4335759a478a6838c
Record both merge hashes The project status file said TBD where two merge commits go, and the queue's entry for the second branch had no merge hash. The record now matches what is on claude. Snapshot of merge 14b48788ef6e001ba7dd82b12aa26dbc56648c22
/stats sorted by the experiment The stats page is now sorted by one rule: a stat leads if it would not exist had a human team done the work. The token and hours readings go to the top, the numbers any shop would produce — commits, features, days, tasks, updates — become inputs under the figures they feed instead of headline cards, and the funding section is gone entirely. The scale numbers stay visible on purpose, with Tom's sentence saying we are early and not at scale yet, because removing an unflattering number looks like hiding it. Snapshot of merge 60ef75fa41b61be1a0a1abacd3cd43a04de60c27
/stats stops printing an uptime percentage The stats page no longer claims 99.97% uptime. Everything that number was made of is still there — one incident, ten minutes, since the day the site went live — but the percentage itself is gone, and the section is renamed "Construction downtime" and moved down beside the section listing what the page deliberately does not measure. Four decimal places derived from a single outage we reported on ourselves, with nothing watching the site, was the weakest claim on the page printed in the biggest type. Snapshot of merge d15daefe76b4992dd97d4109b5396e74a9681ee5
Record the copy change's merge hash Bookkeeping: the project status file said TBD where the merge commit belongs. No behaviour change. Snapshot of merge f9d747fd027258e4f8e887925a43bf19a4eec001
The free tier gets a price label The plans page now introduces the free plan the same way it introduces the paid one — a bold name and a price, "Free — $0." — instead of a sentence about what free accounts can upload. It reads as a formatting change and it isn't quite. The paid block introduced a plan; the free block introduced a capability, so the page was asking you to compare two different kinds of thing. The "$0" is the part doing the work: a free tier with no number beside it reads as the absence of a plan, and a free tier with $0 beside it reads as a plan that costs nothing — which is the reading the sentence above it needs, since that sentence says nobody ever pays to read. One more paragraph on that page had been running straight on from the one above it since it shipped, and now doesn't. That was the last of three, and all three were the same thing: a paragraph that was genuinely first when it was written, and stayed marked that way after something moved above it. Carries the hub's session-doc commits as cargo — it was writing in the same tree while this branch held HEAD, which is the commit-along rule working as designed. Snapshot of merge a4364c315b4b1cc23408274c930da1395488aea7
The plans page gets found You can now find out what MyStatus costs without being signed in. Until this morning the pricing page had exactly one link pointing at it, and that link was inside the account settings — so the only person who could navigate to the page saying "nobody ever pays to read" was the person it says will be charged. There is now a Pricing door in the footer of every public page, and the page that describes the free-plan limits finally links to the page that lists them. The footer entry is a fifth door in a row that was meant to name audiences, which is a rule change made on purpose rather than a tweak. The link still points at the existing address; renaming it to match the word would break links that already exist elsewhere. There is also a new test, and what it checks is the part worth saying: not that the pricing page has a link — it always had one — but which pages the links are on, and whether any of them can be reached without signing in. A test asking the easier question would have passed the whole time the page was unreachable. Carries two hub bookkeeping commits as cargo (7cf7757, 046a460): the hub was writing its own session docs while this branch held HEAD, which is the commit-along rule working as designed. Snapshot of merge 3dcad7b78d4f15900df0eb3c0c9621eaee5a33e0
The plans page now says the one thing that is true of every plan on it Reading anything on MyStatus is free, forever, and needs no account. Every plan that costs money charges the person who controls a record, never the person reading it. That was already how the product works — it is the same rule that makes the public side read-only — but it lived in a design document and nowhere a customer would see it. It goes at the top, above the free plan's limits, rather than beside any one tier. Next to a single plan it reads as that plan's small print; above all of them it is the frame the prices arrive inside. It is deliberately not repeated on the billing settings page. That page does print plan copy before you pay, so it looked like a match — but you have to be signed in to reach it, which means its reader is the account holder, the person who does pay. There is nobody there to reassure. One thing changed that nobody asked for. The two plan headings had no space above them, which looked consistent and was consistent for two different reasons — one sat under the page title, the other under a paragraph. Adding a paragraph above the first one broke that, so both now get the same space. It is a visible change to the paid plan's heading made by a copy task, and reverting it is one word. No picture attached: the browser that can take the shot can't hand the file over, and retyping one through the model is the step that corrupted the last two attachments. Snapshot of merge b238436a2843cf3b2ef466f442d34ee3b239e11a
The record now says what the build stamp merged as Bookkeeping only: the queue entry and the status log carry the merge hash, and the queue records the two things the task turned out to be about. Snapshot of merge 27dd935a1ccf857d5c7ac0c2c81468f214bd4d98
The build stamp: you can now see which version of the site you're looking at Every page now ends with seven small grey characters naming the exact version of the code behind it, and clicking them opens the public work log, where every change in that version was recorded as it happened. The service behind the app reports the same version on its health check. This is for releases that change nothing you can see. Until now the only way to know one had landed was to trust the machine that said it had — a deploy light can tell you a release happened, but not which one is answering now. The version number travels inside the thing that gets deployed rather than being set alongside it. That was a deliberate choice: a value written in a separate step can end up naming a release that never actually shipped, and that is worse than showing nothing, because it goes wrong in the direction you would trust it. Where there is no version to show — a local copy, or a build made without one — the page shows nothing at all rather than leaving a broken-looking gap where a number should be. Snapshot of merge dbeaf582e207447b2bb339c7c5f74654a7523b84
The second bug of the night, found the same way as the first: by a real purchase failing to show up. Buying twice created two customer records at the payment provider — that is just what happens — and our table could only hold one of them per person, so the second purchase silently deleted the first one's paperwork. The first subscription then sat waiting forever for a record that had been erased rather than delayed. The table now holds as many as arrive, and the missing row has been restored. Snapshot of merge cf6286eb5e3f406f8643e3edeb9711e5880aa4e9
A live billing bug, found the only way it could have been: by someone paying real money and then looking. Two subscriptions were bought and neither reached our database, and the payment provider's own dashboard reported one of them as successfully delivered. The cause was two safety mechanisms that were each correct on their own and cancelled each other out. Nothing anywhere reported a failure, which is the part worth remembering. Snapshot of merge 435ba55175dc9f5ac7bb757da72079286ccbd0d3
Three small repairs to the page where you pay, and the interesting one is that the page hid its own buy buttons from anybody whose plan had been set by hand — which, the night it shipped, was every account that could have made the first purchase. The lesson is not about billing: the check was asking "are you paid?" and using the answer for two different questions, and one question a single yes-or-no cannot answer is "should we offer you something". Snapshot of merge f4f54b3e303d8a7b9df1759a23eaa6ad770d6f4a
The session's own record for tonight's three branches, written down by the session that had them — including the one rule it followed at the first branch and missed at the other two. Snapshot of merge 0fcf9670865284a36990c285d67d3383cbafbbbd
A dollar now buys something you can point at: a page of your own at a short permanent address, with the tasks you choose to list and their updates in one stream. The part that took the most care is what happens when there is nothing to show. A number nobody claimed, a number that never existed, and a number belonging to somebody whose subscription lapsed all give you exactly the same page-not-found — because if they didn't, anyone could walk the numbers and read off who is paying. Snapshot of merge 06fcd5ec6e4620680240fd08b605f29bb41f418e
There is now a page where you can actually upgrade, and a plan page that finally says what upgrading gets you. The list is deliberately short — three things — because those are the only three the database will actually honour on the day you pay. Everything else on that page is the same number on every plan, and the page now says so out loud: we are not going to shrink the free plan to make the paid one look better. Snapshot of merge 2288ad8b075cb745c57d89bf577e18944ff7b110
MyStatus can take money now. A dollar a month — or ten a year — lifts the ceiling on how many tasks a machine can post to, from ten to a hundred, and reserves you a permanent short address of your own for the profile page landing next. The part worth explaining is what is NOT here: the payment page is Stripe's, the cancel page is Stripe's, and the only Stripe credential anywhere in our system is one that can check a signature and cannot charge anybody. There is no button we could press to bill you, which is the point. Snapshot of merge 4ae4cc7fe0308bd9bc9627af1051e286d320c98c
The session's own exchanges, written down by the session that had them Snapshot of merge b20efe1cfe45394ce324db71b424d43d6097d116
The record now names the merge, the findings, and the true test count Snapshot of merge 4b33767ffac9a15a3c6c97b5e3ec6a8d67a36cc3
The gap audit, rebuilt: two questions about the same silence A tool that asks where the week's time went shipped in August and was pulled the same hour, because its numbers added to more hours than the week contained. This is the rebuild, and the rebuild was wrong twice more before it was right. The measure it originally got wrong was the one it was named after. It reported how far apart the first and last signs of life in a quiet stretch were and called that "activity", so a long silence with somebody appearing near each end read as a full day of work. It now measures how much of a stretch actually had somebody in it, which cannot come out larger than the stretch itself. It asks two questions now instead of one, because they were always two. What the commits say about a silence, and what the session transcripts say about the same silence. Only the second can be asked what ended it, and only the second can see an approval wait, which produces neither commits nor records. Then it was run against a real week on a real machine, and that found three more things no test had. The largest was the original defect all over again in a different place: a stretch of time was being labelled by something true at one end of it and false across the rest, which turned fifty-five hours of somebody asleep into "trailing thinking work". The second was two totals that each rounded correctly and did not add up to the window they divide, printed under a sentence inviting the reader to add them. The third was a set of subtotals that quietly didn't reach their own total, with nothing saying why. It also settled the question it was built to ask. The idea was that you could tell an approval wait from an absence by what kind of record broke the silence. You cannot: across a hundred and eighteen silences, the one record typed by a person was somebody going to bed. So there is no clever detector, and the fix stays the boring one already in place -- say you are about to wait, before you start waiting. The tests are as much of the point as the code. The file this replaces contained an assertion that described the bug accurately and approved of it, green from the first run to the revert. Snapshot of merge d437b9c99de9282436bd3dd3b1ff26dd05edbb49
The queue entry names the merge and the two checks Bookkeeping only: prompt #80's Run entry now carries its merge hash, the way the push was verified, the field-for-field agreement between the two implementations, and the contradiction inside the prompt itself. It rides its own branch because the integration branch takes no direct commits — the same repair the line-endings task made on 2026-09-01. Snapshot of merge c5f1d9bad187db8be7ad134527c477344901b300
The token meter ships as a sample The script that tells us what a session of ours cost is now something anybody can run on their own machine. It reads the transcripts Claude Code and Cowork already keep on your computer and prints what your last session actually cost — how many turns really happened, what your standing instructions cost on every one of them, and what the whole thing comes to at published prices. It carries the two mistakes we made first, by name. The log writes one message up to eight times, so adding the rows up the obvious way comes out about two-thirds too high. And the cost of simply showing up is a number you read off the very first turn, not an average of the session. Both are printed on every run rather than left in a comment somewhere. One file, no dependencies, in the public samples repo. There's a door to it on the Developers page, and the script we run on ourselves now names it as its twin — change one and the other has to follow. Snapshot of merge cf944803e916faf62446b0dee771bb8b2eeb98a4
The record now says the open item is closed Bookkeeping only: the project log still listed the receipt re-run as outstanding after it had been done twice, and the finding it turned up had no entry of its own. Both fixed. Snapshot of merge c7557d07917273dc7b0abb7470813175a50c8191
Nothing costs nothing, and that needed saying in code The first real run of the receipt measurement priced two of three features and refused to price the third. The reason was one exchange inside it that the tool wrote rather than a model — an error notice, carrying no tokens at all. There is no published price for something no model produced, so the price of that feature was withheld entirely, over an exchange that cost nothing by construction. Withholding a figure exists to stop us publishing a total that is short by an unknown amount. An exchange with no tokens in it cannot make a total short, so the answer is zero, and zero here is the only answer rather than a convenient one. Asking whether there is anything to price now comes before asking what it would cost. If one of those tool-written exchanges ever does carry tokens, it goes back to being unpriced, which is the alarm we already watch for. One smaller thing rides along: the two ways an exchange can go unpriced are now told apart. Saying "no published price for this model" when a price plainly exists and it was the timestamp that was missing would send somebody looking in the wrong place. All three features now carry a list price: $26.43, $8.23 and $16.11. Snapshot of merge b656e6b596dd55544dcefae9b8f088e5cd582f1b
The queue entry now names the merge and the open item Bookkeeping only: the record of what shipped now matches what shipped, including the one step left for the host — re-running the receipt measurement so the new column stops reading as an em dash. Snapshot of merge dae2e58f9bc3ed7cb7ac3dc0e70c1a9c3414eec6
A dated price list joins the weighted token figure Every token figure this project publishes has been in our own cost-weighted units, which are comparable to nothing anybody else prints. There is now a second figure beside it: what the same tokens would cost at the AI provider's published prices, with every exchange priced at the rate that was in force on the day it ran. That dating is the whole mechanism — one of those prices was cut by three quarters yesterday, and a figure priced at today's rate would quietly re-price everything before it. The old figure has not moved, nothing already published was re-priced, and where no published price exists for a model the total is withheld rather than guessed at. On this session: 4,363,144 tokens of cost-weighted work, or $20.84 if you were buying them one at a time. Snapshot of merge 14d6c5e5dfe94a7a4116c916873c46f2c496e25b
The record catches up with the receipts pages Snapshot of merge b0b9809b9cad99673b6cee6359b50bcc5adba167
What a feature costs, and the last invoice we price by the hour Two new pages. The first shows what three finished features actually cost — the hours a person spent on each and the tokens the machines used — with every figure linked to the entry on the public work log it was measured from, and a section saying plainly what those numbers do not count. The second shows one week billed the old way, by the hour, beside the same week priced by what is actually scarce: the owner's attention and the machine bill. The old invoice is kept exactly as the tool printed it, because retiring a way of charging is not a reason to go back and amend the last bill. Between them is the reason for the change — a workforce that is part machine can run hours in parallel, so an hour count is a number the seller can dial, and a number the seller can dial is not a price. Fixing the report so it could be shown found it had quietly broken: it had been listing releases instead of features, with every total still correct. Snapshot of merge a878d645f31817bb49e370b2c13664c108bbfb5d
Bookkeeping: the blueprints prompt records what it merged as Snapshot of merge 1d5b35b532fddd87a226bd209454ea3c620fcc74
A page that teaches machines to read a task There is now a page on the site explaining how to make one of your tasks readable by a machine as well as a person. It covers what every task already hands over in a form something can parse, how to write the agreement into your task's own description, what happens to the entries you posted before you had an agreement (nothing bad — they are just prose, and a reader skips them), and what is callable today if you want to read somebody else's task. It shows two of our own tasks already doing it, and it quotes neither of their agreements. It links them instead, because those agreements live in the tasks where they are kept up to date, and a copy on a marketing page is a copy that goes quietly wrong. The Developers directory gets its new headings and two more doors, so the section promising tools for building with MyStatus now has tools in it. Two of those links point at sample code that does not exist yet, knowingly: the code is written and tested, the Gist is Tom's to create, and one edit replaces the placeholder. Snapshot of merge 6570626ae144d030d7d17f3fbe9d3b25f37902b3
Full suite verified against live: T6 alone The test suite was run against the live database and came back with the one known, deliberate failure and nothing else. The record now says so. Snapshot of merge a3474875341682fcd0d2c3e166ed5f677d7b3cca
Merge hash on the #77 prompt entry and the STATUS heading Bookkeeping only: the record now names the merge commit. The commit had first landed directly on the integration branch and was moved onto its own branch before this merge. Snapshot of merge 124b6b104145e25d1501aaa00ac5006d4ecffa01
Line endings standardize on \n Send us a line break however your platform makes one. MyStatus takes it, converts it, and stores one form - and that's the form everyone gets back on the next read, on every screen. That's the rule for everything: posts from the website, the phone apps, the API, an integration we haven't built yet, or someone typing straight into the database. Editing an existing post goes through the same conversion again. It's enforced in the database itself rather than in any one app, because a rule that only covers the doors you've already built stops working the day you build another one. Everything already stored was rewritten to match, so there are no exceptions left for anyone to point at. The task descriptions that changed were not marked as edited today - their pages would have claimed you'd touched them, and re-sorted three of the browse directories, over a character nobody can see. Snapshot of merge 728ed97a5f95dc0f7280843a16e9060409c58165
The record catches up with the Developers page split The project's running log now carries the entry for the split, the queue row carries the merge, and the session's notes carry two things worth keeping: the headings Tom wrote at the gate, and a small mechanic — a draft he has to approve goes in the message itself, not in a preview he can't see. Snapshot of merge 53e2a1033d2fe81bf3094944052f44e68e111ba9
The Developers page splits in two, and both wrong-turn warnings retire The Developers page now has two sections instead of one list. The first is for people who want to build something on MyStatus; the second is us doing exactly that, in the open. Both pages that opened by telling some of their readers they were in the wrong place have stopped — the footer word does that sorting now. Headings are Tom's own words, written at the hand-off gate: 'How you can leverage MyStatus' and 'Examples of how we leverage MyStatus'. Read as a pair they make the second section examples of the first rather than a separate exhibit. Snapshot of merge 4be09ccce5156b1beaf84ce2b4f3ae8fb08535f3
The record catches up with the signs task STATUS gets the write-up, the queue line gets the merge hash, and the screenshot rule stops naming a cheap test for whether a picture is real. Three such tests have been written and retired in three days — which browser took it, whether the tab called itself hidden, whether the page had real dimensions — each one retired by the very next time anyone looked. The rule now says to read the picture against the page, and names the one thing that has always fixed it: the browser window actually visible on the screen. Snapshot of merge 3547f22347f5f975c94be091d11f6e7b81d043e6
The signs say when they were read Both signs on the stats page now print the moment they were read, with a Refresh link beside them. A page can outlive its reading: a tab left open overnight showed both signs backwards while the live site had both right to the minute, and nothing on the page could say so. The stamp goes stale along with everything around it, which is exactly what makes it useful. Two things found on the way. The working tree rolled back mid-task to an earlier snapshot — eighteen files, including a whole route — and the gates went green against it before anyone noticed; everything came back from the last commit, nothing lost. And the screenshot tool returned five well-formed frames of a page it was not showing, until the browser window was genuinely visible on screen. Snapshot of merge 3bf96eec478382583796cd38a14d108bc80b9bec
The Developers door: the followup, and two findings about instruments The screenshot attached to this task's close row was broken: 919 bytes stored against a 12,551-byte file, decoding to nothing, with every step in the chain reporting success. It is the same failure as the corrupt attachment a few hours earlier, an order of magnitude larger and with the opposite symptom - that one was mangled, this one simply stopped partway through. Both happen at the only step that isn't a file operation: the picture has to pass through the conversation as text. The check that found it took one call and is now unconditional. Second, this morning's explanation for black screenshots is retired. A hidden tab photographed the page perfectly today, twice. The real variable is whether the tab has been laid out at all - every black frame on record measured zero by zero. Two days running where the control turned out to be one sample of each dressed as a comparison. The capture rule in CLAUDE.md now carries all of it, plus the folder captures get saved to, which this task proved works. Snapshot of merge df098fdeb6680187f7b2220dfcb4191bd0aa3295
The Developers door The link at the bottom of every page that used to say Factory now says Developers, and it opens a new page that gathers everything about how this app gets built: the Glass Factory and its live build streams, the field notes, the numbers, and the Claude skills that do the posting. It is a rename rather than another footer link. The room behind that slot always had four things in it, and Field notes had lost its own place in the row a few days ago to keep the row short. A directory page gives the lessons a path back without crowding the footer again, and the Factory keeps its address and its content as the first door. One thing was added past the original plan, on Tom's call: a reader who walks from the directory into one of those pages is now offered the way back to it, instead of a generic link home. The page is nothing but links out, so every click from it is a detour. Snapshot of merge 0f1692e5314c05fdda1364ad77b0633006c99596
Bookkeeping for the copycats change The queue entry now carries the merge hash, and the project status file records what the black screenshots turned out to be: a browser photographing a window nobody could see, which is one question away from being obvious and was never asked. Snapshot of merge 2c628b8f3b7e9700b84469ac4c9f8f8d3478b2a8
The copycats answer stops pitching The section of the Factory page that answers "aren't you helping people copy you?" used to end by asking VCs for money. It now ends by pointing at the stats page: if you're going to copy this, here is what you are signing up for. Tom wrote the closing lines at review. Also along for the ride: the afternoon hub's own notes, committed onto this branch while it held the working tree, and a session note recording what the black screenshots turned out to be — a browser photographing a window nobody could see. Snapshot of merge 6d0449cf91b4e848d789f61e798cbe8226d96106
Record the 404 that turned out to be the connector A status row refused to post four times on a task that had taken five rows in the previous half hour. The entry is less about the fix than about what did not happen while it was broken: the row was left unposted and flagged, rather than reworded, re-timed, or retried until something stuck. Every one of those would have let a delivery failure write itself into a permanent public record. Snapshot of merge 6f1018d70785595520c4090ac41ac9720ea54418
Bookkeeping: #67's queue entry carries its merge hash and what the task found Cut as a followup branch after the fact: the commit had landed directly on claude, which the workflow does not allow, so it was moved onto its own branch and merged the ordinary way. Nothing in the content changed. Snapshot of merge 374bb26b5bf85dcfb0adb76fd6d3dd5cf6dc7a02
/factory speaks the industry's language — the agentic-workflow mapping A lot of people building with AI agents already have a name for the way this project is run. /factory now says it in their words first: a hub session thinks and writes the prompt, spoke sessions each execute one scoped piece, which the field calls orchestrator-worker. Then the section spends the rest of its length on the three places we do it differently on purpose. I carry every prompt myself, so the human is the message bus and every gate is a person. The hub is a standing seat with a public logbook instead of a coordinator that exists for one query. And spokes report to the record rather than back to the hub, so the shared memory is documents anybody can open, which is why a context reset costs one session instead of the whole system. It sits directly under "Start here" and above the three live streams, because the streams are that arrangement running. The section explains what an Open row is before you meet one. Tom rewrote the copycats answer while this branch was open, and took the three wording fixes that came back as flags. Two instrument notes. The built-in browser returned two solid black screenshots of a page it was rendering fine, each reported as a success. Chrome shot the same URL at the same scroll position in the same minute and got it right. save_to_disk still writes no file this session can reach. Snapshot of merge c88d9713d161d76faff1545e62b8bc6d1c233d37
The proof image attached to the close arrived corrupt, and nothing said so The image is fine on disk and broken on the page: the browser decodes it at its true size and paints the top of the screenshot over a grey rectangle, and the stored file is three bytes off the local one. Not a truncation, a handful of mangled characters — because the only step in the chain that isn't a file operation is the one where the picture is read into the conversation as text and typed back out again. A file copied by a program is a copy; a file retyped through a model's output is a transcription. The check that catches it costs one page load: look at the published row, and when the image looks half-loaded, compare its stored size against the file on disk rather than waiting for it to finish. It is not going to finish. Snapshot of merge 5397a87e2d18a83842dc1464ea11ec25db5f4389
Bookkeeping: the queue entry for the line-break fix now carries the merge it produced Snapshot of merge 1aa6f3075cc81318d753b1a11a8c5aef6c793e34
Task descriptions keep their line breaks The Release Ledger's description spells out a format meant to be read by machines: one key-value per line. The page was showing it as a single flattened paragraph. The line breaks were in the record the whole time — the page was throwing them away on the way to the screen. Descriptions now keep their shape on the public page and on your own view of a task, the same way update bodies always have. The /browse directory listing is deliberately unchanged: the description there is a one-line preview under a task title, and keeping it to one line is the point. Nothing else moved — no database change, nothing a phone app reads. What made this worth fixing ahead of the Hacker News push: a reader following a link to the ledger should see the contract shaped the way it was written. Snapshot of merge cd80362d409381fe8ddd79b6d929ac543776198b
Batch 5 bookkeeping and the hand-off Writes down what the aging curve shipped, marks the prompt run, and hands the rest of batch 5 to a fresh session — which is the finding the task just published, acted on the same evening. Snapshot of merge a296fde6a267cf7ea63bc2a4fde97ff2daa565bf
The aging curve on the stats page The curve from the last task is now public, under the token breakdowns: eight bars showing what a turn costs by how deep into a session it lands, the threshold at turn 141, and the comparison that stops the chart being a picture of who got measured rather than of what anything cost. The capture is the reason this closed differently than it would have. The section was correct in every block and wrong in its order: it had been slotted in ahead of a collapsed note that closes the scaffolding argument, so that note ended up sitting under the new curve and reading as though it belonged to it. Preflight passed. Types passed. The prose scanner passed. Nothing mechanical reads a page as a path. The screenshot nearly did not happen either. Four attempts came back as solid black rectangles, each reported as a successful capture with a plausible size and an id. Snapshot of merge f372a23306d7f5238bab63629d0f7278076f7304
The aging curve: what a turn costs as a session gets older Every turn in a conversation re-reads everything before it, so a turn that lands late costs more than one that lands early. We have been saying that for a week without a number behind it. Now there is one, drawn from 144 sessions and 14,605 turns of this project's own transcripts. A turn costs 1.00x in a session's first twenty. 1.45x by turn 61. 2.10x by turn 141. 2.86x past 180. It is not a cliff, it is a ramp that never stops climbing. The objection that nearly killed the number: only long sessions have a hundredth turn, so the right end of that curve is a different population from the left end. Answered two ways. Every turn is divided by its own session's opening rate, so a session is only compared against itself. And the sixteen sessions that ran past 180 turns opened at a median 18,868 tokens a turn, against 18,580 for the whole cohort. The marathons did not start out expensive. They got that way. The threshold ships with its arithmetic rather than as a verdict, because a fresh session is not free either: at 2.1x, a re-seat costing ten turns of context pays for itself once you have ten turns of work left. And the part that stings. Batch dispatch, invented last week for a stretch with no hub seat, quietly rebuilt the long session that hub-and-spoke exists to prevent. The worst offenders on the list are our own batches, by name. Snapshot of merge 54076c74ec22cb998ae4cb17d6d06bda7bad2ad8
Cost by kind, parked before any code The second thing we decided not to build tonight, and this one we decided in twenty minutes rather than after writing it. The plan assumed the token figures were already there to be read. They are, but only as sentences inside closing notes, and this project has a standing rule that nothing ever reads prose off that stream. It also assumed the written-down plans covered the work; they cover 54 finished tasks out of 173, leaning toward the big deliberate jobs. Put next to this afternoon's other parked task, that is the useful part. The one before it checked its assumptions after the machinery was written and cost us a finished feature with nowhere to put it. This one checked first and cost twenty minutes. The five kinds of work are settled — copy, feature, infrastructure, fix, bookkeeping — because that judgment happens once by hand and then lasts. Snapshot of merge ba3b6ddd4d41cda162be7f409f5197ae196b4bc3
Cost by platform: the machinery, without the section We now have the thing that can answer what each app cost to build, and we did not publish the answer. The machinery works. Every finished task is one merge on the integration branch, so the parts of the codebase it touched are already on the record — nobody has to remember to label anything. Those tasks join to the work log's own sessions by when they merged, and that join is unambiguous: sessions never overlap, so a merge falls inside at most one. A task touching two parts counts fully toward both, the parts add to more than the clock, and the excess is published rather than divided up. Then five lines of git log asked something the plan had not. Merges only became the unit of work on August 12th and sessions only started being logged on the 23rd, and both phone apps were mostly written before either date. So the iPhone figure would have rested on four sessions while describing an app four times that size. Tom's call: park it. The machinery and its thirteen tests are in; the section is queued again with the coverage table and a trigger that is a measurement rather than a date. Snapshot of merge a87cd7a87db599873836b351207b3e48dd093083
One hours section, and a work-type breakdown beside it The stats page had two sections about hours. One said 76 and started three weeks into the project; the other said 243 and reached almost all the way back, and it spent a paragraph explaining that it disagreed with the section above it. They are one section now, and the bigger number is the headline — because the smaller one only ever counted work that announced itself by posting a note when it started and another when it finished. A scheduled job, a one-question session, and every stretch between finishing one task and starting the next posted nothing at all. Folded underneath it is every day so far with the hours worked on each. Those rows add back to the number at the top: days are cut at midnight before being counted, so it is a table you can check rather than one that merely sits nearby. Today it comes up 1.1 hours short, and the table says so. The coding / business / both-at-once split moved into its own section, framed as what it is: a refinement started on the 23rd to get a clearer picture of what the hours went on. It now states how much of its own window it misses. The fee estimate follows the bigger number, and lost its three-line breakdown entirely on Tom's call. Those lines covered a shorter stretch than the total above them, so a reader adding them up would have found a hole with nothing on the page to explain it. Snapshot of merge c7019c206acd74050558be84145e44a4c36f54f6
Queue bookkeeping for the hours merge Writes down the plan for merging the two hours sections, carrying Tom's design rather than leaving the shape to whoever runs it: one section with the longer window and the review split, a separate work-type section framed as a refinement, and the by-day hours collapsed inside the first. The measurement script now emits those daily rows, so the section can be built without another run on his machine. Also moves a commit off claude, where this session had put it directly after its own merge left HEAD there. Snapshot of merge d921fbda69b3ba5ae2e301e530bf63e6aada93d0
Who the clock belonged to We have always said the review is the product — that everything gets checked by a person before it ships. That was a claim about ourselves with nothing behind it. It now has a number: two thirds of session time is spent stopped, waiting on somebody to read and decide. It is a floor three times over. It counts only the stretches where work was demonstrably stopped, it cannot see reading along or thinking away from the keyboard, and it throws away every gap over 45 minutes as a closed laptop rather than billing it as deliberation — which discards more time than it keeps. Finding it turned up the fifth way we have miscounted our own sessions, and then the fix for that introduced a sixth. Both are the same disease: correct arithmetic over the wrong sets. Catching them is worth more than the headline. The record now reaches back to the first day, three weeks earlier than the hours we had been publishing. Snapshot of merge 960d1806dbaab5442ba9b045e750ddb9c9e320b9
The scaffolding breakdown, re-counted against today's install The headline number on the AI costs section was a week old, and the chart added in the last task made that visible on the page. The cheap repair was one line — raise the total — and it would have announced the opposite of the truth: the largest row in the breakdown is defined as the leftover, so all of the week's growth would have been credited to the one component nobody here can edit, taking its share from 56% to 65%. Measured properly, that share fell to 52%. What grew was this project's own conventions file and the tool signatures handed over before any work starts. The line that had never been re-read was off by roughly double, because one tool's definition is now as large as the entire line was estimated at a week ago. The leftover row now says out loud that it carries the error of every estimate above it, and should be read as a ceiling rather than a measurement. Snapshot of merge 84c8d56ca87daa329beec814e499e6f576458a90
The scaffolding figure becomes a trend on /stats The stats page already said what it costs to hand an assistant this project's standing rules before it does any work. It now shows what that has cost over the week — fifty-three readings, every one of them a figure that was published on a public task at the moment a session opened, not measured again afterwards. The answer is that it went up a quarter in seven days, and that nearly half of the rise landed between two sessions on a single evening. Editing our own rulebook is a decision with a price on it now, and the price is published. The chart's own first act was to show that the big number at the top of that section is the oldest point on it. That gets fixed properly, by re-counting the breakdown, rather than by raising the total and quietly blaming the growth on the one component nobody here can edit. Snapshot of merge 82b0893dc3ad74910cd991eb96c80c499ec3b1da
Batch 3 session capture and field-note nominations What the batch taught, written down where the next reader is told to look rather than only where it was produced. Snapshot of merge dd584e42abae55b765a90db3acd9db80e16b58a5
Queue bookkeeping for the rotatable task Records the merge hash and the two decisions that differed from the written plan, so the queue entry says what actually happened. Snapshot of merge 34f82a0b7699ecfa90fdeaba3d41f996e90d4571
A task can declare its share link permanent Some links are meant to be permanent. The release record's link is about to be written into every change record we publish, and after that, rotating it would break the whole published trail at once — quietly, because nothing would complain. A task can now say its link is permanent. Rotating it is still possible, on purpose: you have to clear the flag first, in its own step, so it cannot happen by reflex. Snapshot of merge 951af369fb4b557060e83532a3ddce9138f47447
One share-token generator, and it refuses bad edges A share link is meant to survive being pasted anywhere. Ours could come out ending in a hyphen, which some places quietly swallow when the link sits at the end of a sentence, or starting with one, which makes it look like a command to a machine. Tokens are now drawn until both ends are ordinary letters or digits, and the database refuses to store one that isn't. One live link had to be rotated first, because a rule like that cannot be added while something already breaks it. Snapshot of merge 46157910fc8641eff4f16dadbf186ad7d6666aed
Edit panel opens below the actions row, not inside it Adding a third action to an update row showed up a problem the row already had: opening Edit pushed the other actions off to the side of the panel that opened. The actions now stay on one line and the panel drops below them. Snapshot of merge 7cd6f450b7845b8f436d337a83be8fd1ad36fa5d
Owner update rows link and copy their public permalink Every update has a permanent public address, and the one place you could not get at it was the page where you write updates. The timestamp on each row is now a link to that update, and a Copy public link action sits next to Edit and Hide. Hidden updates get neither, deliberately: their public address shows the plain task page by design, so a button there would hand out a link that goes nowhere. Snapshot of merge b5a5a44ead18819aa06721957abe015505dc4eb4
The demo keeps admitting it is a demo Tom's call, recorded next to the data that causes it rather than only in the status log, because that is where anyone tempted to undo it will be standing. Writing it down showed that one of the three options I had given him was not real. The demo's old dates are not stale data that a refresh would fix — the seed creates that gap deliberately every time it runs, so a fresh one looks exactly the same. The only way to make the demo look live is the edit we had already refused, which means his choice was the only honest one on the table rather than the best of three. Snapshot of merge f08c28d5d021f5831b85273e9c6e08da9ae5b98e
The notes were captured, and captured is not the same as findable Last night's session wrote up what it learned in the place the rules told it to, said so, and left it there. The place the rules told it to is not the place the next reader is told to look — and the previous batch's notes only reached that place because Tom said so at the time, not because anything required it. So a fresh session tomorrow would have found one batch of notes and missed the other, with everyone having followed their instructions. The notes are now where they will be found, and the rule names the second step, which it never did. Snapshot of merge 244d24efdf2cb7659dc936d566866b358bff73b6
The database test suite now has a runner, and a four-month-old excuse is gone The suite that guards who can read and write what has been run by hand, when it has been run at all. There is now a script that runs it and reads the result — including the one failure that is there on purpose, which makes a correct run look like a broken one to anything that only checks exit codes. It refuses to report a run that never happened as either a pass or a failure, because a suite that could not connect has measured nothing, and nothing is not a result. The reason nobody had done this is the more interesting half. The note explaining why said the file was too large to move. What it meant was that it was unsafe for me to retype from memory. Those lead opposite ways, and for four months everyone read the first one. Tom pasted the file into the database's own editor and had the answer in seconds. Snapshot of merge 4cef33ff83c683eda3e46d25baa0aaf2c7047b9e
The suite run that five entries asked for, and why nobody found the route Tom ran the whole RLS suite against live and only the known failure came back, so the new tests are verified inside the suite rather than beside it. The part worth keeping is how: he pasted the file into the database's own SQL editor. Every entry that asked for this run described the obstacle as the file being too big, when the real obstacle was Claude retyping it from memory. A copy-paste was never the same risk, and stating the obstacle without its cause is what hid that. Snapshot of merge ad364dd47c0608d02350697fb3534f383de0e741
The record of batch 2 now matches what actually happened Bookkeeping only: the last task's merge hash and what it found are written into the queue entry, and the status heading no longer says the work is waiting on a branch that has been deleted. Snapshot of merge d1046ba8d1482ff91ea83f853693ca29116b413c
The posted-later tell — a backdated update says so Every update here carries two times: when the owner says the thing happened, and when it actually got typed in. Only the first was ever shown. So an update written up three days late looked exactly like one posted while the paint was still wet — and that doesn't just mislead about the late one. It quietly discounts every other update on the page, because a reader with no way to tell them apart has to suspect all of them. Now, when those two times are more than a day apart, the update says posted and the real date. Under a day is left alone: the 6pm writeup of this morning's work isn't backfilling, and a tell that fired on ordinary posting habits would appear everywhere and mean nothing anywhere. It reads like an admission and it is the opposite of one. An update with no tell is now saying something it could never say before: the claim and the record agree. Worth reporting what it found on its first run. Across every public task here, not one update trips it. The only rows that do are the demo tasks, whose whole month of history was inserted in a single moment — so the sample kitchen renovation now admits, on every line, that it was seeded. There's a one-line fix for that which would make the demo look live again, and taking it would be the exact thing this feature was built to catch. Snapshot of merge 1fc334bf4613a34847b0717527b9dd8afbe6ec4b
The capture note had to be corrected, and the correction is the lesson An hour ago this session wrote down that a workaround worked, using the word tested, while describing a route whose last step had not been run. It had not: the image can be moved into the page, cropped and composited there and looked at, but reading it back out is blocked on purpose. So the honest split is that Claude can take and verify a web capture, and whether Claude can attach one depends on plumbing that is not always there. The record now says that, and the batch 1 rule stands where its condition holds. Snapshot of merge fd74d7797c17858b6fce074e5eadea857253d46f
Task types — a vocabulary for what kind of record a task is Not every task on here is the same kind of record, and until today the site talked as though they were. A work log is posted as it happens, by a machine, at the moment the thing happens. A build channel is written up afterwards, on purpose, from a session that ended hours ago. Both were sitting under the same promise — raw proof, as it happens — and that promise was true of one and false of the other. A task can now say which it is, and its page prints one quiet sentence saying so. A wire says 'Posted as it happens.' A journal says 'Written after the fact, on purpose.' Four kinds exist so far; adding a fifth is a row in a table, not a release. The alternative was to soften the claim everywhere, which would have cost the wires the exact thing that makes them worth reading. Saying less about everything is not the same as being honest about each thing. Six tasks are typed: both work streams, the incident log, the release ledger, and the two build channels. Everything else renders exactly as it did — no type, no sentence, no hedge. Snapshot of merge 558f16368b45a1a8a1799d7cb2201e507d799957
The queue records how this one finished Bookkeeping only: #10's merge hash and what the task actually taught go onto its queue entry, and the working notes get their closing pass. Cut as a followup branch because the commit first landed directly on the integration branch, which nothing is supposed to do. Moving it is cheaper than leaving a precedent. Snapshot of merge 6cec238e53aa69ee54bf52e7bd00170b3566f3a7
Work delivered, as a client would read it The work log knew how long everything took. It now knows how to say it to someone who's paying: a week comes out as a list of what shipped, the hours against each one, and what that comes to at my rate -- from the same public rows anyone can read. The first real week came to 45.3 billable hours, and the number it didn't bill turned out to be the interesting part, because every line rounds down and the 2.6 hours in between are worked, logged and not charged. It also learned to check itself. A second reading of the same week, mined from session transcripts that nobody has to remember to write, puts the number at 51.6 hours against the log's 68.4. Neither is a correction of the other: the transcripts can't see a file edited by hand or a review on a phone, and the log counts an approval wait in full. The gap between them is mostly me reading and deciding -- which is work, and which turns out to be the hardest thing either instrument can see. Snapshot of merge c60e46f00396d60227aa687615752f20eec94e7f
Web captures are Claude's to take If the thing to verify is a web page, Claude now takes the screenshot instead of asking Tom for it — desktop or phone, light or dark. Phones and tablets are unchanged, because there is no Xcode in a Linux sandbox. The agreement it narrows justifies itself by capability rather than courtesy, and the web half of that was never measured. When a division of labour rests on a capability, the capability is a fact to re-test, not a premise to inherit. Also leaves the batch's field-note candidates for the next hub seat. Snapshot of merge 0647697ed1802b4be3965b8cc474797439e9485c
Funding on the record, and the years the idea waited The stats page has published every dollar going out since the day it launched and not one coming in. It does now: $75,000 received against $100,000 committed, because one of the two SAFEs has funded half of what it promised and the number that actually landed is the one that goes first. That is the same rule the costs above it already run on, pointed the other way. No investor is named. They are private individuals who signed an instrument, not a marketing asset, and that is the line this product already draws where readers of a task are invisible to its owner. The money is Dad Bod Games' and it was raised for Remixing Reality, not for this. It arrived after the idea existed and before a line of its code was written — which four date cards now show rather than claim, because the point underneath is an ordering and an ordering is what a paragraph renders worst. The years between the idea and the first commit are derived from the idea's date rather than typed, so the figure cannot quietly stop being true. Every capture for this one was taken without Tom touching anything, which an hour earlier both of us believed was impossible. Snapshot of merge a8b0aa88b9e2c0c600b5aa01feb9ee549cb391e3
A way through /stats The stats page had gotten long enough that the only way to find out what was on it was to scroll the whole thing. It now opens with an index of every section and the four rules every number on the page follows, and it is 28% shorter — a reader meets 40% fewer words before opening anything. Nothing that changes what a number means was cut or hidden. What went was the same handful of explanations, written out fresh under every figure that needed them: how the windows work, which way each figure errs, what the signs read, that work runs in parallel. Said once at the top, they stop being said nine times further down. Dates became structural rather than prose — every figure not counted from the first commit now carries its own Since stamp — and so did the difference between a number we measured and a number we assert, which is stamped on the six figures nothing here measures. That second one came out of Tom noticing the conversation counts were hand-typed under a sentence claiming otherwise. The arithmetic that explains why floored lines do not add up to a floored total was three paragraphs in three places; it is one folded section now, named for the people who care about it. Snapshot of merge b57bac0e1c7a1fd29afd3e44f1d755b80e0d32ee
The dogfooding page stops sending you away The page used to end its section about letting Claude post for you with a link to a different page, and then hand you the step-by-step instructions at the very bottom. So a reader who was just told the write path is theirs left to work it out somewhere else, and got the instructions when they came back. The instructions now sit where that link went, and the page closes with a link back up to them. Every section of that page was individually right, which is why nothing caught it: the link was the correct destination for its sentence, and the bottom is the correct place for a call to action. The loop only exists in the order. A page can be correct at every section and wrong as a path, and the thing that reads a path is a person reading it straight through. One rule came out of it. The jump link needed a plain anchor rather than the app's usual link component, because a #fragment names a position in the page you are already on rather than a page to navigate to. The component's header had listed exactly three legitimate exceptions; there are four now. Snapshot of merge 9d4cb53d19f2dfaeea56de544cc323f36e4e097c
Receipts on the dogfooding page Every section of the dogfooding page claims we do something. Now every one of them links the public update from the day we shipped it, so a reader can check the claim instead of taking our word for it. Eleven links, one per claim, pulled from the update record by date rather than from memory. Two things worth saying out loud. The links are root-relative rather than full my-status.app addresses, which sounds like a style choice and isn't: a full address quietly drops the marker that remembers which page a visit started on, and reloads the whole page instead of moving inside it. The one place we already had a full address in prose has been doing exactly that, unnoticed, since that feature shipped — flagged here, not fixed. And the risk this task creates: eleven public links now depend on three share tokens staying put. Rotating any one of them breaks a public page. Nothing in the product allows opting a token into permanence yet; that work is already queued. Snapshot of merge c587cf443edea9545a92b5961299fd9d6f344898
The record catches up with the merge The prompt queue now carries the merge hash for the dogfooding edits task and what it taught, and the status heading says what actually happened: Tom reviewed the copy and approved it, and no device screenshot was taken or is expected. Better to say that than to leave a request for a capture standing that nobody is going to answer. Snapshot of merge aac33d3fc57f367a03f375a8ffaf9c67bad15cb4
The owner's edits get read, gated, and landed Tom spent Saturday rewriting big chunks of the dogfooding page by hand and started a new Product Plans page listing what a free account can actually do. This branch reads every one of those edits, runs them through the same gates any of our own work goes through, fixes what was mechanically broken, and leaves his wording alone. The mechanical fixes: a canonical URL on the new page that still pointed at the dogfooding page, which would have told search engines the two were the same page; a sub-list nested wrong, so the file types rendered between numbered items instead of under the attachments line; a component still carrying the name of the page it was copied from; five unused imports; and three typos. The one thing not fixed quietly: he had written that you could upgrade past the media limits, and today you can't — no plan lifts an upload ceiling and there is no way to pay us yet. That went back to him with options and he cut the sentence himself. The new Product Plans page is deliberately linked from nowhere, his call. Snapshot of merge 58566706b9ab40188b14c02432dcfed06e2d0420
The record catches up with the merge The prompt queue now carries the merge hash for the dogfooding task and what it taught, the status doc says merged rather than uncommitted, and both note what is still waiting on Tom: seeing the pages on a device, and filing the six tasks under Ops so the new counter has something to count. Snapshot of merge 0d19fd5455afbac19729a6433a8257b63f4b1e54
The tooling runs on the product, and now the page says so There's a new page that lays out every way we use MyStatus to build MyStatus — the build log that writes itself, the incident record, the receipts on every finished task, the machines that post to it — and for each one, what you'd do to get the same thing for your own work. Nothing on it is a plan; if we ever switch one of these off, the page loses a section rather than gaining a past tense. The Factory links to it. There's also a new box on the stats page counting how many tasks are doing that job, and it's the one number on that page you can go recount yourself — every task in it is listed at /browse/ops. The question that had to be answered first: the category a task is filed under isn't just a label, it's what puts the task in a public directory. So tagging six tasks 'Ops' moves them out of three other directories, with no code involved and nothing watching. Tom made that call with the consequence on the table. Snapshot of merge f859b631bfd666a807e8e0db51fb2b8fddbadf2b
Queue bookkeeping: #53 carries its merge hash and what the verify found Snapshot of merge 12576190aba64f30718ffcaba6f53abba6725ddd
The new app icon lands on every surface The new icon is now on the iOS app, the Android app, the browser tab, the iPhone home-screen bookmark, and beside the wordmark on the home page. One mark on every surface, so the website looks like the app somebody already has installed. The icon is not the deliverable — the pipeline is. The handover said the artwork may be replaced again once the Glass Factory look settles, so replacing it is now one command: drop a new square master in, run the script, and every rendition regenerates. Fifteen reference sizes, the iOS appiconset, ten Android density files, the favicon, the apple-touch icon, and the homepage asset. One finding that would otherwise have shipped broken: Android's icon safe zone is a circle, not a square. Insetting the mark so its width fits still leaves its corners outside that circle, and a round launcher would have shaved the corner off the mark. Measured on the 432px canvas — furthest pixel at radius 147 against a 132 safe radius, 22 pixels outside even the circle a round launcher actually shows — and tightened until it sat at 129. The generator also refuses to run when the Android background colour disagrees with the artwork it just sampled, because that mismatch renders as a seam under a mask and nothing else in the toolchain compares the two files. Device-verified on both phones, the browser tab, and the home page. Snapshot of merge 37f473cf7ae82a4f9049dd602d36b9ddc2bc5ddc
The back-link remembers where you came in If you wandered into the Factory page and then clicked over to About or one of my task pages, the little link up in the corner still said "MyStatus" and dropped you back at the home page. Which is fine, except that's not where you came from. Now it says "Back to the Factory" and takes you back to what you were reading. Land on those same pages cold, straight from a link somebody shared with you, and nothing changes: you get MyStatus and the way home, same as always. The site already quietly remembered where a visit started, for reasons that had nothing to do with this. This is the first time it says so out loud. Two things turned up on the way. The Stats page got missed on the first pass, because it never had one of these links to begin with. And the category chip on a task page had been quietly forgetting where a visit started since the day that memory was built. Both fixed. Snapshot of merge eeef949a19230d60c934f4fa35b180adbba31092
The About slim-down is verified on device, and one lesson is nominated as a field note Tom shot both pages and cleared the three questions left hanging: Field notes staying out of the site footer is fine, the repeated reach-out link is fine, and of the five lessons written up from this task only one becomes a public field note — that a false restriction is the expensive direction to be wrong in, because the readers it turns away never say so. Snapshot of merge 2f27e69dec0a999b04325dc82c4df78cd32a521a
Record where the About slim-down landed Snapshot of merge 8c4abd1d00e63a039d8fe2e4d192eff1c700bbc9
About becomes the product page; the sausage factory moves next door Once the Factory page existed, About was quietly two pages wearing one hat. Half of it explains what MyStatus is to somebody deciding whether to track something. The other half was Tom talking about how he builds the thing. Not everyone wants to peer into the sausage factory, and scaring those readers off is the opposite of the point, so the sausage moved. About is now product and nothing else: what this is, why a tracking number beats another chat app, the fact that machines can read and post to a task the same as a person can, and who Tom is. One door leads over to the Factory for the curious. The experiment, the build streams and the three questions Tom gets asked most all live on the Factory page now, where the people who want them are already standing, and that page is now the canonical statement of what this project is testing. The rule that decided all of it is who the words are for, not whether the other page already had them. Also retired here: a footer line claiming custom Claude skills need a paid plan. They don't. Snapshot of merge 6a0bf7680c4afe73878608fa3341d33d8bc03937
Record where the Glass Factory landed The merge hash on the queue entry, plus what the task found: the umbrella link is derived from a relationship that already existed rather than configured, stream titles are read from the tasks rather than typed, and the follow-on back-link work is queued separately with its design question already answered. Also the honest cost - the page was cheap, the six rounds of copy review were not. Snapshot of merge 539b9adf9c2f5eb8f4fd0877b9664d7e5c716520
The Glass Factory: one link you can hand somebody MyStatus has been building in public for weeks with no front door. The numbers were on one page, the lessons on another, the reasoning on a third, and the three live build streams were separate task links you had to already know existed. Handing somebody five URLs is not sharing, it is assigning homework. So there is a page now at /factory, called The Glass Factory: real production shipping to real users, walls anyone can see through, and nothing a reader can reach in and touch. It gathers and frames. It moves no content and owns no facts, so every figure it points at still lives on the page that measures it. "Factory" joins the footer on every page of the site. Two things underneath that are worth knowing. The task instructions had one of the three streams mislabeled, and rather than hardcode the missing token or ask for another Azure setting, the page works the answer out from a relationship that already existed: all three streams hang off the same parent task, and the public read already returns a task's parent. And the prose-spacing test earned its keep twice in one afternoon, against two completely different causes - a code formatter undoing the fix wholesale, and an ordinary copy edit breaking a line from three words away. Snapshot of merge f58a874bce128535218b2471bb1fc0dad15960e0
Record the merge and the verification Queue entry carries the merge hash; the status heading says device-verified and names the capture Tom shot. Paperwork only. Snapshot of merge 92bd951476285a144f268f7f273063c534129145
The reader finally has a way in Until now, if you read somebody's MyStatus page and wanted one of your own, there was nowhere to click. The MyStatus name at the top was a link but you had to guess that, and the "Powered by MyStatus" line at the bottom wasn't a link at all. So the one place where every impression of this product happens had no front door on it. Now "Make your own task." sits next to the name at the top of every public task page and in the footer of every page on the site. It's a quiet grey link rather than a big shiny button on purpose: some of these pages are a family keeping people posted about someone who is dying, and a sales pitch parked next to that title would be gross. The footer signature links home now too. The links also remember where you came in. Land on somebody's task page, wander to the home page, read the about page, then sign up, and the whole walk still says you started on a task page. It's one word — a task page, the home page, the stats page. It does not say WHICH task, and nothing stores it yet anyway. Who reads your task stays invisible to you, so the thing that could quietly turn into a list of your readers doesn't get built. Snapshot of merge 7a2e53dc67bf59cdd63fb69a2349b899a3dd3f98
Stats headings say what they price The stats page got long enough that its own section labels started disappearing into it: light grey, small caps, very tasteful, invisible by the third scroll. Those labels are bold now, and only on that page. Every other screen keeps the quiet heading, which is enforced by the darker label being a separate part rather than a setting on the shared one. Two headings also stopped being coy about which pile of money they stand in front of. 'What it costs' is now 'Actual operating costs', and 'What it would have cost' is 'Estimated consulting fees'. Same numbers, same arithmetic you can check on a calculator. Also cleaned up a sentence about the first month's untracked hours, and added an Inspiration section crediting the Cluetrain Manifesto and John Wilker for the idea of keeping a stats dashboard at all. Snapshot of merge dbcfe6b6b51c29dd6823e1b55559230fdd384c4f
What it would have cost — followup: the rounding note moves up a panel Small one. When the consulting-rate section shipped, it came with a note that appears on the days its three lines don't quite add up to the number above them. Three figures each rounded down on their own, and sometimes they don't round in step, so the column lands a tenth of an hour off. What I missed is that the hours panel right above it has the same three lines with the same rounding, and it was still promising you could check the column with a calculator. True most days. Not every day. So the note moved up a panel. Both panels now read the same number, which means they can't disagree with each other about whether today is one of those days. Also added one line pairing the hours with what they shipped, because "here's what this would have cost" is only half a sentence without "and here's what came out of it." The draft of that line said the features were all live, which isn't true — a feature is finished when it merges, and only a release puts it in front of anyone. That half got cut rather than softened. Snapshot of merge 64b723f6a3a7e0d7ac32b602aac7ddf5699c097c
What it would have cost: the consulting-rate figure on /stats My stats page already publishes how many hours have gone into this thing, pulled out of a public log instead of a timesheet. Now it publishes what those hours would have cost you. My consulting rate is $150 an hour, the rate my clients actually paid through Omega Ortega, and it says so right on the page. So the whole section is one multiplication you can do on your phone: hours times rate. Nobody was billed a cent of it, and the page says that too, right under the number. It's what the same work would have gone for at human rates, sitting next to what it actually cost to run. Then there's a fold with every coding session in it, one line each, with a link back to the entry that closed it so you can go read what got built. No task names in the table, on purpose. The names live in the writeups, and I'd rather send you to the writeup than have a robot scrape a title out of one. Here's the part I like. While I was checking it, the math came up fifteen dollars off. Three numbers each rounded down on their own, and they didn't round in step. That's real and it'll happen again, so the page explains it when it happens instead of me quietly nudging a row until the column looked tidy. A tidy column would have been easier. It also would have been a lie. One thing we tried and threw away: I bill in quarter-hour increments, so we ran the numbers that way. It moves the headline by thirty-seven dollars and it would have added thirteen hundred to the itemized rows, because a quarter-hour minimum is fair on a billable line item and nonsense on a two-minute git branch. Nobody ever billed a client fifty-eight line items for six days of work. So the page stays measured, and the experiment is written down so nobody runs it again. Snapshot of merge e4b5285ecb27746657e1e69ca5cbfee717fefe8b
The record now matches reality: merge hash on the prompt line, and an honest note about a beat that never got posted Snapshot of merge 4c7b3c1b6405b68093a503f1f75d4fdf50c643b5
Who writes a field note, written down the way it actually works My field notes page has a row of buttons that let you pick who you are, and the note rewords itself for you. The rule I had written down said I would write and approve every one of those versions myself. Today I changed it, because editing the same thought four times was never the best use of anybody's day. From here: Claude drafts a note in my voice, I get in there and rewrite it until it's actually mine, and then Claude cuts my finished words for each reader. My content, its rewording. Us doing what we're both good at. I still approve everything before a word of it goes public. That part doesn't move. What moved is that I only edit the note once. Nothing on the page changed today, not one pixel, and we proved it rather than claimed it: built the site before and after and compared the results byte for byte. This was only the paperwork catching up to the deal, which matters more than it sounds. A written-down process nobody follows is worse than nothing, because it hands the next guy a confident wrong answer. Snapshot of merge a45b7a50f2ec0d09b42fd1c50ad579df62315db7
The record catches up, one last time Writes the final merge hash into the status entry and the prompt queue. Snapshot of merge a2e86eff13da6996ae391fbede5706260b94cebe
The field note, in Tom's words, and three corrections to mine Tom rewrote the field note and his version is the better one: the mistake was inherited, not random. The machine had learned what people know, that sport means a ball, so it walked straight past a whole day of somebody posting a twenty-mile run. The three audience versions now follow his text. Three things this session had got wrong, all fixed here. The sign-up link only needs to know what KIND of page somebody arrived from, not which one, which answers the question and removes the privacy worry entirely rather than managing it. The referral reward is a Super Task, not a free month. And the common cold was about patience, not measurement: his family took until this year to adopt AI, years after it arrived with unlimited money behind it, so slow is the normal case and not the sad one. Snapshot of merge 25e134e941213e1a9bf88de58b84a8a2649b3802
The record catches up with the strategy branch Writes the merge hash into the status entry. Snapshot of merge 06d3158a7d8722501aa082298bd226556eb7f058
What people use this for, and why we won't build a network to spread it The event archetype turned out to be much bigger than sports: somebody present at an event, reporting it to people who aren't. That is the sharpest fit the read-only rule has found, because the half of eyewitness reporting that always went wrong is the mob that arrives on the same surface as the report, and here there is no such surface. Written up as an idea, deliberately kept out of the store listings on this release, with the reasons recorded. The bigger thing to come out of it is a principle rather than a feature. Three products, one idea: a thing that becomes a link, and nobody has to sign up to read it. So we never build a network — we use whichever ones people are already in, because networks come and go and links do not. That now sits in the product principles as the standing answer to anyone who suggests a feed. Alongside it, how it actually grows: people carry it from home to work and back again, and every shared link is a working demo for somebody who signed up for nothing. A referral scheme is parked until the product earns it, since paying for shares would destroy the only signal that tells you whether it did. Also: the public page has no invitation on it. 'Powered by MyStatus' isn't even a link. Queued as its own task. Snapshot of merge 9f10df41ff83d729ace6046ff1ed0a7f8c605563
The record catches up with the correction Writes the correction's merge hash into the status entry and the prompt queue. Snapshot of merge df5ccdd6c8ffe830d26f7c15d5dd3cd7311e94e5
The play-by-play use case was already on the record Two hours ago a new use case went into the store listings labelled a guess, because no public task here has ever been a game. That was true and useless. The record already holds live play-by-play of a bounded athletic event, posted as it happened. The category was never games, and a long run is not a lesser version of that shape. What replaces the guess is a narrower and more useful line: somebody covering their own event is demonstrated, and a third party covering somebody else's is not. Along with the rule that stops it happening again, since something is unevidenced only if the shape is unevidenced. Snapshot of merge e67dd1351a9a3f8bd824d753ccba726203e0420e
The record catches up with the work Writes the merge hash into the status entry and the prompt queue now that the use-case archetypes task has landed. Snapshot of merge f66e281844e831abd107c94972906431934a8269
What people actually use this for, written down the way they'd search for it Sixteen use-case archetypes now sit behind the app store listings, generalized from the public tasks that already exist rather than invented in a positioning session. Nobody's task is named and nobody's story is borrowed. Along the way the keyword field turned out to be leaking: two of the hundred characters' worth of words were already in the subtitle, which buys nothing. Reclaimed and spent on words somebody would actually type. The archetype that almost got left out made it in, phrased as a relationship instead of a profession, which covers the teacher, the supervisor, the scout leader and the coach in one line and keeps us out of a store category we do not belong in. That answer produced one nobody here has run yet: a coach reads one of these, and a coach could just as easily post play-by-play from a game for the family that could not get there. Snapshot of merge e15ece23f6bf122590097918a0490af7e6aa0dbd
The record catches up with the work Opens the session record the keyed-task-caps branch should have created when it started, kept as two files: the conversation worth re-reading, and the full exchange. The miss is written into the file rather than quietly fixed, because a capture rule only followed when convenient is not a rule. Adds a field note about the paragraph that took four rewrites. Every version Claude wrote was true, and every version made the reader stop and work it out; the one that shipped is the one Tom wrote himself and Claude shortened. Also writes the merge reference into the task queue and the status log, which a commit cannot do for itself, and refreshes the counters on the stats page. Snapshot of merge ce33743b03a91fd23032fd799fcba52d3171ecf7
The limit on tasks becomes a limit on automation This morning's cap on how many tasks a free account can run at once is reversed, and the meter moved to tasks that a machine can post to. Capping tasks charges people for writing more, which is the habit the product exists to encourage. Capping keys charges them for connecting more tools. What real usage showed is that the combination is the thing worth counting: a task with at least one live key on it. Free accounts get ten of those, hand-posting stays unlimited at any number of tasks, and an account that never mints a key never meets this limit at all. A second key on a task that already has one costs nothing. Archiving a keyed task frees a slot, and so does revoking a task's last key - both because the check counts what is actually there rather than keeping a tally. The task ceiling was not deleted, only set to nothing on every plan, so it can be turned back on with a single edit. That is what writing the limits as data rather than as code bought: a decision that was wrong at breakfast was undone by lunch with no release. The keys page now requires every key to name the one task it can post to, and explains in plain words when a plan is full. Snapshot of merge 5e297899154d1657767fe5088ccaa9dca7db70d8
The task-caps record now names its own merge The task queue and the status log both had a gap where the merge reference belongs, because a commit cannot know the hash of the merge that will contain it. Both now point at it. Nothing about the limit itself changed. Snapshot of merge b7cd483c5a0728d1d12e369513e5ede830b4787d
Free accounts get a limit on how many tasks they can run at once Until now anyone could open as many tasks as they liked. A free account now runs ten at a time, a paid one fifty, and an enterprise one as many as it wants. The limit counts only what you are actively tracking. Archive a task and the slot frees up, while the task itself stays public at its own link, permanently, exactly as before. So what is being limited is how many things you are publicly on the hook for at once — never how much you have ever done. Deleting a task frees a slot too, because the task is simply gone. Nothing charges anyone yet. There is deliberately no way to buy the paid tier; this is the rail, not the tollbooth. The limits themselves are rows in a table rather than anything baked into the apps, so changing one, or writing a one-off limit for a particular customer, is an edit rather than a release. The database is what enforces this, not the website, which means every app and every future one gets the same answer without being told twice. Tom's own account was set to unlimited in the same change, since he was already past the free limit and would otherwise have been refused his next task the moment this went live. Carries four labelled hub-session bookkeeping commits that rode this branch. Snapshot of merge 7fe6d22e0e7b70b9fd15f14383aee7effc611c65
The task queue records how this one actually finished The queue now carries the merge reference for the connector key-cap fix, plus the session's own record: which model ran it, and two mechanical traps worth not rediscovering. Bookkeeping only, so no separate writeup. Snapshot of merge 00d77ee7e58ed8033f4cd0f3c1fd7e1e2e7dc010
The connector says when it is ignoring your keys The connector that lets Claude post here carries one key per task, and it quietly ignored every key past the tenth. An eleventh channel came back saying the task wasn't available — which sends you off checking the share link and re-minting keys, when the real answer was that the key was never looked at. The ceiling is now 25, and going over it says so, by number, instead of failing as something else. Snapshot of merge fee78cfe9624a7c3a98e239d3a025000c5dcc93e
The task queue records how the encryption change actually finished Writes the merge references and the two-piece shape back into the queue entry, so the next person reading it sees what happened rather than just what was asked for. Snapshot of merge e14a088b7d350f44a066a3de89e155d8b6723f47
The generated iPhone settings file catches up with the change Regenerating the project writes the new encryption declaration into a file the repo keeps under version control, and only a machine with Xcode can produce it - so the change always arrives in two pieces, the written one and the generated one. This is the second piece, plus a note explaining why the split is expected rather than something anyone forgot. Snapshot of merge edcf0ed3ea49b097e5ffafa0f9f86bf7a982cc70
The iPhone app answers the encryption question once, in writing Apple asks at every upload whether the app uses encryption, and answering by hand each time is how a wrong answer eventually gets clicked. The answer now lives in the project itself, with the reasoning beside it: the app does use encryption - everything it sends goes over a secure connection - but that kind is the sort Apple exempts, and the app ships no cryptography of its own. "No" is the true answer here, not the convenient one, which is the distinction that matters if the declaration is ever audited. Verified on device: regenerated the project, built clean, and Xcode's Info.plist shows the key as Boolean NO. Snapshot of merge 3ff7f7a6d4239e3ed86006afabef03530835e1bb
The record now matches reality: the support page is live The status log, both store-listing rows, and the task prompt now say what is true: the support page deployed the same day it merged, Tom captured it serving on the live host, and the Support URL can go into both store consoles. Nothing about the product changed. Snapshot of merge b85bc05f6b64f00b57f932cff5189311ec0c566c
The support URL now leads to a support page The app stores demand a "get help here" link, and ours led nowhere. Now it leads to a real page with a real email address a person reads. It also tells you how to report a public task that shouldn't be up — and reports come to us, never to the person who posted, so nobody gains a way to bother anyone. Support now sits in the footer of every page, Tom's call. That was the last thing standing between this project and filing both apps. Also carries Tom's own mid-task edit to the about page: MyStatus is a Dad Bod Games NYC project, built via Omega Ortega. Snapshot of merge d803ca27a1bf2e8817095427d15987a55f886a2e
The record now matches reality on the spacing fix Tom deployed and confirmed the bold lead-in renders with its space. The status entry's heading is flipped from needs-verify to verified. Two things worth keeping came out of the check rather than the flip: the fetch tool handed back a version of the page older than Tom's own screenshot, and the standing rule about never reporting a fresh thing as missing has now caught that on three different kinds of page — so it belongs to the tool, not to any one page. And this branch was first cut off main, because the deploy had left the tree there. Snapshot of merge 916edf1843ab218c5712c4119749214976d76b17
The bold lead-ins get their space back Tom's screenshot of the live privacy page proved the new push-token paragraphs are serving, and caught a typographic bug in one of them: the bold lead-in ran straight into the sentence with no space. Chasing it turned up a build-time rule nobody here knew — a plain space after a bold label is dropped when that same sentence contains a special character like a quote mark or an apostrophe — and two more places on the same page where it had already been doing this for weeks. All three fixed, and a test now watches every page for it. Writing that test turned out to narrow the rule: its first run flagged a fourth spot that is actually fine, which pinned down how far the effect reaches. The store listings are down to one thing blocking submission: the support page. Snapshot of merge 4003a3efaf38da0f6b5814e92ec4409bb87f9dbd
The privacy policy discloses the push notification token The mobile apps are going out, so the privacy policy now discloses the push notification token before there is one to collect. Nobody's token was collected under the old policy: neither app has been released, the website never asks for notification permission at all, and the only token in the database is Tom's own test phone from August. The disclosure lands ahead of the release rather than after it, which is the order these things are supposed to happen in. It stops being true of the LIVE policy at the next web deploy, not at this merge — the store forms will point at the live URL, so the repo having it changes nothing for a reviewer. Snapshot of merge b17fce8bdde7325ba225431ea038acd86b790a51
Store listings: four of six open questions answered Four of the six unanswered questions on the store listings now have answers, so the listings are down to two things that actually block filing: the privacy policy needs to disclose the push notification token, and the support link needs somewhere to point. Both are already queued as their own tasks. The age rating question is closed - the honest answers to Apple's questionnaire do produce the adults-only rating our published privacy policy already promises. The export declaration is settled and written down precisely, because the app does use standard web encryption while having none of the kind the form actually asks about, and that distinction is the whole value of the entry. The support page will carry a line for reporting a public task that shouldn't be there. That does not put readers in touch with task owners - a report reaches us, never the person being tracked - so the read-only rule the whole product rests on is untouched. Snapshot of merge 722b2d2d6a4f3fdb8f5817ab54d297a9564352b0
Store listings: one copy source, two cuts Both app store listings now come from one source of copy instead of being written twice - same name, same description, same privacy answers, cut into the two shapes the stores ask for. The description is a single string used word for word in both, because both fields have the same limit and a second version would only give the product two ways to describe itself. The listings were the smaller half of what came out of it. The brief for this task said the app wasn't age-gated and should be rated for everyone; it has been adults-only since August - in the signup flow, in the published privacy policy, and in the decision record. Filing it as anything else would have made our own privacy policy false on the point regulators read first. Two more gaps, both found by checking the forms against the live policy line by line rather than trusting either. Both stores' forms declare a push-notification identifier that the privacy policy never mentions - the fix belongs in the policy, not the forms. And the app publishes public content with no way for anyone to report any of it, which one of the stores asks about directly. Every character count in the doc was measured against its field's limit rather than estimated. Six things nobody here could answer honestly are written down as questions instead of guesses; two are already queued as their own tasks. Snapshot of merge 316f2d9ab5631f046563935df19328869e69a56e
Paperwork after the session-turns merge: the status entry now says it is merged, and the session's notes carry links to where the work was reported. Snapshot of merge abcbab4c79d86b724148f480a79477d27c651717
Session turns population — what we were actually counting The script that answers 'how long does a session with Claude usually run around here' turned out never to have known what a session is. Nearly half the files it was reading were not conversations. They were audit logs — one per session folder, sitting a level above where the real transcripts live — with just enough of the right shape that every version of this script has counted them as sessions. 283 of 598 files. Every median it ever printed described a population that was half not-sessions. Three smaller things were wrong alongside it. The one group of sessions the script told you to publish never appeared in its output at all, because it was identified by two things that are never true. Messages our own tooling writes, rather than a model, were counted as if a model had written them. And a session's helper agents were reported as if the session had been recorded twice. The number that says it worked: the longest run on the machine went from an impossible 18,254 turns to a believable 331. The script now names every file it skips and why, states which of its own figures a bad merge would invalidate and which still stand, and answers the original question out loud even when the answer is 'no, that never happened.' Three of the checks added along the way fired on cases working exactly as designed, which is the failure those checks exist to prevent. All three were tightened. Also carries this session's field note and Tom's call to keep counting synthetic turns in the published receipts. Snapshot of merge 76600a0bd88675d42e53ced8f34443b73aa60705
Hours on the record: /stats publishes how long the work has been open The stats page now shows how many hours have actually gone into MyStatus, and not one of them was typed into a timesheet. Every hour is the gap between two updates that were posted while the work was happening, on two public tasks anyone can open and read. Coding and business work are listed separately. When both were running at the same time, that time is subtracted rather than counted twice, so the three lines add up to the headline and a reader can check it with a calculator. The bigger number was available and was not taken. The page also says what the figure does not cover: hours with work open, not hours of one person's attention, and nothing from the 29 days before anyone started keeping the record. Snapshot of merge d9008f0d8fcc1715ec8c0494089aae2167716ce5
The iPhone app can now be told it is too old to keep running The iPhone app now asks the server at every launch whether the build it is running is still supported, the same way the Android app learned to earlier tonight. Both mobile apps carry the floor now, which was the point of doing it before launch rather than after: a warning can only ever reach a version that already knows to ask for one. The build number bump found something on the way. The app's version number lives in two places, and the generator that writes one of them defaults to the literal text "1" rather than reading the other. They had agreed for the app's whole life because both happened to say 1. Changing it to 4 would have left the shipped app telling the server it was build 1 forever, with no error and nothing to notice. And the offline test was wrong before the build was. Tom relaunched with airplane mode on, got the offline screen, and said that was the appropriate behaviour. He was right: a cold launch with no network has no verdict at all, so the app carries on by design. The behaviour worth checking was a verdict that already exists surviving a later failed check, which lives in memory, so force-quitting destroys the thing being measured. Backgrounding instead proved it. Snapshot of merge 93454528f1bc6f4d0133e38499fead72ae3e1287
Write down what the Android support gate cost and what it caught The running project log now carries the V2 Android entry: the design decision that keeps all the rules on the server, the database constraint that revealed the emergency lever had been unusable since it was built, and the release draft the Play Console screenshots caught sitting one button away from shipping a binary with no check in it. Bookkeeping only — no behaviour changed. It rides a followup because it belongs to the task that just merged, not to the one starting next. Snapshot of merge 8dcee3090b5a17132c34911504fd3cf093bc27d3
The Android app can now be told it is too old to keep running Every time it starts, the app asks the server whether the version being run is still supported. Most of the time the answer is nothing and nobody sees a thing. When it isn't, the user gets either a quiet banner saying an update exists, or a full screen saying this build can't continue. Without this, a shipped app is unreachable: if something is badly wrong in a version already on people's phones, there is no way to tell them. That is why it had to ship before launch rather than after. The app itself does no deciding. It sends the only thing it has to offer a service — its own build number — and renders whatever verdict comes back, so the rules can still be rewritten long after the app is out of reach. Anything that can go wrong with the check leaves the app working normally; only an explicit "unsupported" from the server stops anything. Two things turned up that were not in the plan. A database rule refused to let a test be set up, which revealed the emergency lever had been sitting unusable since the day it was built. And Tom's Play Console screenshots caught a release draft, never rolled out, one button away from putting a binary with no check in it in front of everyone. Snapshot of merge 4a3d969e3fe06a4578061046db5e35cd6882c934
The stats page stops claiming line-by-line review Earlier today the about page stopped saying Tom reviews every bit of code, because he doesn't. The stats page was saying the same thing in two more places and now says the true one: one person vets the logic, questions the thinking, and owns every decision that ships. That is still the reason the human headcount on that page is a 1 and not a 0. Only one of the two turned up in a search of the source — the other was split across a line break, so the sentence exists in the rendered page and nowhere in the code as a contiguous string. Both were found by reading the live page. Snapshot of merge 8db445afacb9e59d63fa25bacc7b141f43bed062
Field note: the seams are the proof The mismatched-screenshot riff becomes a published field note in Tom's voice, with variants for the building-with-Claude, curious, and worried lenses. The worried lens turns it on the flag itself: Claude called its own evidence weak rather than letting it slide, and the stale shot stayed anyway. Snapshot of merge 1654fd5132cec8c3587b0f82302a917a97677a92
Don't tidy the seams: the mismatched screenshot as proof Tom's read on a verification screenshot that showed a typo fixed three minutes after the shot was taken: it is an easter egg for anyone who thinks this project is staged. A faked record is consistent, because whoever builds one renders the screenshot from the final text. The artifact that doesn't quite match is the expensive one to forge. Captured in the session's goodparts with the general form — on a public accountability record, the seams are the evidence. Snapshot of merge 25629aba7a9ec98247ed55e2080200b72dbb44ed
The about page says "helps me" now, not "help me" A subject-verb slip in the same paragraph the last task rewrote, older than anything that task changed: "Claude writes the code ... and help me with the first pass." Flagged at hand-off rather than fixed silently, because grammar in Tom's own voice is his call, and he took it. Snapshot of merge 352bff641777fb8d80f2e9800e86d8d154670482
The about page now says what Tom actually reviews The page has claimed, in Tom's own first person, that he reviews every bit of code. He doesn't, and said so: he reviews the output, vets the logic, questions the thinking, and passes judgment on everything Claude shows him, biz suggestions included — but he has never once asked for a line-by-line code review. The page now says the true thing, which is also the stronger thing, because a human reading every line is not where the failures get caught. The same correction went into the project's own working agreements. Prompt #22. Two files, three sentences, plus a typo Tom caught on his verify read. Snapshot of merge 69a880d29605c7d2bd9dec474132773d46625f53
Updates can be sealed against editing, but never against deletion An update can now be locked. A locked update refuses edits -- its text, its label, and its date alike -- while still accepting its owner's delete, because retracting something has to stay possible on a record that belongs to you. Fixing a locked entry means unlocking it, changing it, and locking it again, so the correction is visible rather than quiet. Nothing is locked today; the switch ships ahead of the feature that will use it. The lock is enforced deep in the database rather than by permissions, and that turned out to matter more than the original design did. Permission rules carry an implicit exception for whoever holds the permissions -- us -- while this one has none: it refuses the owner, our own servers, and a database login used by hand. The test that proves it runs the forbidden edit as the most privileged caller in the system and requires it to fail. Also corrects the plan that specified this, whose instructions could not have been carried out as written, and adds two rules learned along the way: a test that catches an error has to assert which error, and permission rules cannot see which columns changed. Snapshot of merge 868495f69365345c77837ee1870740dd56d68348
The server can now tell an app it is out of date The server keeps a list of every app build we have shipped, and any app can ask it one question at launch: is this version of me still supported? It answers one of three ways — you are fine, please update, or stop and update before going further. A hard stop always has to explain itself and always links to a public record of what changed, because those are database rules now rather than things someone has to remember. It is off for everyone today. The only two entries are silent placeholders for the builds currently being worked on. That is deliberate: a warning can only ever reach app versions that already know to ask for one, so this has to be in the apps from the first release rather than added when it is needed. Also created the public page where those change records will live, and rotated its link while doing so was still free — nothing pointed at it yet. Snapshot of merge 56b7a750e2be5edfb8451d34744887e52da64fc2
Every page carries the site footer, from the layout The footer used to be copy-pasted onto whichever pages someone remembered to put it on, which is why the stats page spent its whole life with no way back to About or Privacy. It is now part of the site's frame, rendered once, so no page can be missing it and a new page does nothing at all to get one. Machine-readable versions of a task (the feed, the association files) do not get it, and cannot accidentally get it later. Two fixes along the way: the signed-in pages no longer claim a full screen of height, which would have pushed the footer out of sight, and the footer's left edge now lines up with the content above it on every page rather than only on the narrower ones. Snapshot of merge 7e05157b040b063326e29a56b5535aa80e194368
The stats page now shows the tasks its own cost figures came from, and which model ran each one The stats page said its own numbers could be checked against the public work log, but never showed the links. Now it lists each measured task with its turn count, what its standing context cost, which model ran it, and a link to the entry the figures came from. Older tasks that finished before the model was recorded show no model rather than a guessed one. Adding a new row uncovered a wrong sentence on the page — it was calling a later session 'the session before' — now fixed and pinned by a test. And a false alarm from this session's own start-up check turned into a rule: while a finished task waits on approval, it keeps posting that it's still going, so going quiet once again means something is actually wrong. Snapshot of merge 948ed7bf198403aca4cd6b955c7480447a13ea11
The work log records which Claude model did each task The work log now says which Claude model did each piece of work, right next to what it cost, because different models use wildly different amounts of tokens and without that line a reader would blame the wrong thing — task size or wording — for a gap the model explains. It removes one thing you would otherwise have to guess at; it does not explain the variance, and every copy of the rule says so. The model is read from the record rather than typed from memory, which matters most in the case nobody remembers accurately: a session that ran on two models. That one lists both, with each one's share. Left undone on purpose: the stats page publishes what this costs and the price per token differs by model, so a reader now has both halves and can do the multiplication themselves. A dollar figure on the row would freeze today's prices into a record that outlives them. Snapshot of merge 17e1b80964ab13c183f0606714eff6d4ebf49cd2
Say what the published cost figure leaves out, and where the salaries went The cost figures went up on the stats page yesterday. This adds the two sentences from the conversation behind those numbers that never reached either the page or the product doc — the session that wrote the doc ran before the conversation happened, and the session that built the page was handed the rules for what counts as a cost but not the words for what doesn't. What was missing is what the number leaves out: the founder's hours, which are unpaid, and anything shared with the rest of a life — no slice of a phone bill, no travel. Those are excluded on purpose, because an allocated share is a judgment call rather than a measurement and a category like taxi fares never finishes. And the sentence that answers the question a skeptic asks first: in this company, labor appears as a subscription line item. The Claude subscription is the labor cost. Tom's edits: the ask — 'Funding to pay himself would be nice' — linked to the Fundraising post, his own name in place of 'a life', and the reasoning aside cut off the page and left in the source file where it belongs. Not verified on the live page yet: the deploy runs off main. The capture lands on a followup once claude is released. Snapshot of merge 91a8b853e3ffd1ed7d8d16b64adff23a19a13a11
The new field note now speaks in Tom's own first person, like every other note on the page. Snapshot of merge 8b3825f3ba9f258a72514ad07cf29e4846262460
The rule for measuring what a task cost now insists on both ends of the window, and on never writing a number the measuring tool didn't just print — both learned from one entry that got its figures wrong twice the same afternoon. Also adds this session's field note: facts don't lean, and the truth is always good to know. Snapshot of merge 867fbf6a8a4de343b8ec00c0279761eda2fba53b
Paperwork after the costs section landed: the status entry now says it is merged and verified and points at where it was reported, and the repository counters were refreshed against the new head. Snapshot of merge 1c193690b930e91315902ee13dc3530111dbb4ee
The stats page now says what this actually costs to run — $375.69 a month, $776.39 so far, broken out line by line — plus how many conversations have happened about it and how many demos have been given. What's deliberately missing is any measure of how interested anyone seemed: that's a grade about other people rather than an event about our own work, and at six conversations it would name people who never agreed to be named. Snapshot of merge 122e4895f0151b9ef94a48c2dfb05c776f19b879
Paperwork after the last task: the status entry now says the work is merged and points at where it was reported, and the session's own notes carry the links to what shipped. Snapshot of merge d00b99624dcfae2be1a4f955dcef47697c4e1da4
The idea behind the two signs is now written into the product record instead of living in one day's conversation: if machines take over the coding, and coding is the only work anything tracks automatically, everyone else's day reads as empty. The answer is that any tool a person uses can post what happened — the chat app, the word processor, even the desk phone — and the reason that isn't surveillance is structural, not a promise: the worker owns the record, holds the key, and posts outward by choice. Snapshot of merge b69e49827679d5a833d713705403211635f2afce
The new business sign shipped wearing the coding sign's explanation, and one word in it was false — it said work ends when it merges, and the business stream has no merges; its sessions wrap. Each sign now speaks its own stream's language, and the business sign moved into a section of its own, ready for the business numbers to join it. Snapshot of merge 238907763e13bc80921b4e815f5835f7511a00d1
The rule for how the business stream gets its entries is now on the record: when the day's thinking session opens, beats, wraps, or stops, and which of those posts need approval. Docs only — the stream itself started posting yesterday; this writes down the discipline it follows. Snapshot of merge 516d80fda2ca94d244224d9003c4b84ade7dbdb0
The stats page now carries a second sign next to Coding Right Now: Working on Biz Stuff, read from a public task that tracks the non-coding work — the thinking, business, and marketing sessions. Same machinery, same honesty rules; both signs can be lit at once, and the page says so. Snapshot of merge 0c5356c7461406c26b7840a5d7522de60de46192
The Coding Right Now sign on our stats page used to read OPEN or CLOSED — a state name answering a question nobody printed. It now answers the question: YES or NO. The record behind it doesn't change; the note under the sign explains which entries produced the answer. Snapshot of merge 69f0233f0b84bd5dae768ea490efe2cc15e48600
The crash gets a recovery prompt, and the crashed night gets an honest record Last night Claude went down mid-task — laptop closed, twelve hours of silence. The status sign handled it correctly on its own: silence reads as not working, by design. But the public work log was left implying one enormous stretch of work — the 13-hour "session" that would never be beaten. The record is now repaired with two backdated entries that openly say they are reconstructions: work stopped within half an hour of the last progress note, and the task was picked up twelve hours later for a two-minute hand-finish. Nothing was rewritten — both entries keep their real posting time, so the backdating documents itself. And the lesson became a standing rule: the next session to find the log gone quiet mid-task asks permission to post exactly that repair — stop the record where the work provably stopped, then either resume properly or leave the task parked. The one thing it will never offer is continuing without a start marker, because the start marker's link back to the stop is how the record tells a pause from an abandonment. Snapshot of merge da0ed350ce9c0afa0d0028c5cc5db70ac6be2f53
The Work Log's design finally lives in a document, and a task's numbers mean what they say The idea behind the public Work Log finally became a document instead of a conversation someone has to remember. A finished task's cost figures now measure just that task, not the whole working session around it. And the vocabulary got its fourth word right: a task that stops without finishing is Stopped — not Abandoned — because the record can't know whether you gave up or just got pulled away, and it shouldn't pretend to. The branch parks in place, and coming back later picks it up exactly where it stopped. Snapshot of merge a064c393bb0d19ba54bc22c6958bb8ff8f42e6d4
This session finishes writing down its own record The running notes of this session were started properly and then stopped halfway, missing the most useful part — the wrong turns, the investigation that was thrown out, and the two bugs that only appeared once a second measurement existed. They now run to the end. Also written down: a problem this session created for itself. Our token counter measures a working session, but a finished task is what gets reported, and those were the same thing until today, when one session finished three separate pieces of work. Reporting the session's numbers against a one-file change would have overstated it several times over. It was corrected by hand for the task in question, and the general fix is now queued with the decisions it needs. Snapshot of merge 69d50dc72376edd8890a52610862171cf5392a16
The stats page's session figure becomes a range, from two measured tasks This session's own cost joins the previous one on the stats page, so the figure that was a single number now reads as a spread between the cheapest and most expensive finished task. Each is drawn from that task's own closing entry on the public work log, so a reader can check either figure against its source rather than taking it on trust. Adding the second row broke two things that had only ever been correct while there was one: a sentence claiming this was "the first task measured end to end", and a comparison that quietly started measuring the current reading against itself. Both are fixed, and both are now pinned by tests so the next row cannot reintroduce them. Snapshot of merge a17d1f51cb27cf93500e1a82aac28296b0514a25
The last work entry folds away, like the downtime log The "Coding Right Now" sign stays where it is; the work-log entry behind it now sits inside a "Last Work Task" section you open when you want it, collapsed by default in the same way the downtime log already was. The sign is the thing worth seeing at a glance, and the evidence behind it is one click away rather than always on screen. Snapshot of merge 9ccd8ec3ee078f3538d70c0cac9e4ac24b6b500b
The stats page now shows what the tooling costs before any work starts Every time this project's assistant takes a turn, it is first handed a fixed block of standing context: its own instructions, this project's conventions, the description of every installed skill, the signature of every available tool. That comes to 84,647 tokens, paid again on every single turn, before a word of the actual request is read. More than half of it is the assistant's own instructions, which nobody here can edit. Almost nobody publishes that number. We do now — broken out line by line, with each line marked for how well it is actually known, and with the one figure that is a real measurement kept separate from the ones that are estimates. It started as a question about whether talking to it differently would save money. It would not, meaningfully: about eight percent, and only by throwing away tools we use. The measurement turned out to be worth more than the saving. Snapshot of merge ff1ac46ebf1b229ec07bf57546e3265197247187
The stats page's "am I working right now" sign now reads a real record It used to guess from the last time the site was published, so it was wrong most of the day — it could only say OPEN for about an hour after a deploy, and said CLOSED straight through a working session. Now there is a public work log: a note when a task starts, a note every half hour while it runs, a note when it finishes. The sign reads that, and anyone can open the log and check its arithmetic. If the log goes quiet for fifteen minutes past a due beat, the sign goes dark on its own. Every finished task now also records what it cost in tokens — broken out rather than totalled, because the single big number is mostly the same instructions being re-read and would flatter the work several times over. Building that turned up a real number nobody publishes: our own rules, tools and skills cost 83,295 tokens on every single turn, before any work happens. It also turned up a bug in the counter itself, which had been overcounting by two-thirds. Corrected, and the correction is on the public record along with why it was invisible to every check we have. Snapshot of merge 34d8b912450e7697f03c972231257fb7d5082816
The stats page's OPEN sign gets a real design, and two git rules get earned the hard way Started as a lookup -- which task built the stats page -- and turned into the design for replacing its 'Coding Right Now' sign with something that actually knows. Tom talked himself out of storing one true/false value and into a public 'Working' task whose entries are the record: open when a task starts, a beat every half hour while it runs, closed when it merges. The sign then falls out of the record instead of being asserted by it, and anyone can open the task and check the arithmetic. The part that mattered more: this is the same thing a consultant asked for weeks ago. His firm measures its AI by tokens burned, which says nothing about what anyone did. A private counter with no history and no audit path is the same mistake in both places, and publishing the events instead fixes both. The stats page stops being a nicety and becomes the smallest running demo of the enterprise pitch. Measured along the way: half the tasks here finish inside fourteen minutes, a third run past thirty, and the measurement's own blind spot -- it cannot see design time -- is an argument for the feature. Two workflow rules were earned by this session getting them wrong. The branch check now fires on the first file written into the repo rather than at 'task start,' because a session that opens as a question never has a moment that announces itself. And when another session owns the working tree, new files go outside the repo until this one has a branch -- an untracked file is invisible to its owner right up until it gets swept into someone else's commit, which is exactly what happened here. No status update on this one, by Tom's call. Snapshot of merge 1b71b68ffb4ac72fa5b5003094a54c3bf7c1066a
One item, one branch A branch in this project isn't just where work happens — it's how finished work gets counted. The /stats page reads "features delivered" straight off the merges, so two unrelated fixes riding one branch count as one feature forever, and a git graph can't be corrected the way a spreadsheet can. So the rule is now written down: one item, one branch. When a session opens with more than one thing in it, it has to ask whether those are one task or several before any code gets written. The cost is honest — the second item waits for the first to be verified and reported — and that's the price of the number being true. Snapshot of merge 970ca6a3720e7c371e164e1dd250148476b1ae21
A link label was white-on-cream and unreadable in dark mode On a permalink page in dark mode, the label on a link card was invisible. The highlighted update sits on a cream card that deliberately stays light when the rest of the page goes dark, and the link card's label was painted in a colour that flips to near-white — so it disappeared into the cream. It now uses the same brown as the update's own text. Also in here, though not as code: why images go broken on a page left open for more than an hour (the private links that fetch them are signed for one hour, and the page keeps them long after they expire — a reload heals it), written down with the fix deferred to the plan that already covers it. And a queued rule that each item Tom hands over gets its own branch, because our own stats page counts a branch as a delivered feature. Snapshot of merge bf1d9d73e7158aeb4af40562863bedc40202c61e
Permalink pages now fetch a window around the cited update instead of the task's entire history A permalink points at one specific update. Until now, rendering it meant downloading everything the task ever recorded — fine while our biggest task has 37 updates, a real cost at ten thousand. Now the page asks for just the cited update and its neighbourhood: cite the newest update and you get it plus the 99 before it, exactly what the first page already shows, so nothing changes for readers today. We proved that by rendering every kind of permalink through the old code and the new code against the same data and comparing the output. Along the way we found that our security test suite's 'list every failure' feature had never actually worked: the first failure always crashed the run before the list could be written, and since the suite had only ever passed, nothing ever exposed it. It's fixed, and the first failing run after the fix produced our first-ever failure list — one entry, the failure we already knew about, which is itself the proof everything new passed. Snapshot of merge 6fbc93dc0eb701cc803d22db3dbae503f31ae59b
Two field notes from the representations review, approved by Tom The lessons the review session taught, now on the field-notes page: an assistant that only cites precedent is hedging, not recommending - so make it commit before you decide; and the person who proposes an idea is also its best auditor, given a little time to steep - Tom caught his own proposal erasing task ownership one re-read after making it. Also adds the new week's empty wrapup slot and refreshes the stats page counters. Snapshot of merge 3311311d93a33f46e94df9ecf9cef6be48c595e6
Representations plan review folded in: every open question but one settled We walked the plan for how machines and spreadsheets read MyStatus tasks and settled nearly every open question. Each update will now say who wrote it - ready for the day a task has more than one author - and Tom caught that the change had accidentally dropped who owns the task, so ownership went back in explicitly. Labels keep their name. Anyone can read a page as fast as a human ever would, free; a robot hammering the same page around the clock can be slowed or charged. And the public version-history feed becomes one filterable task instead of two. Snapshot of merge 67b8432e5cf95f0274457e4b954c2b8e1297a6a8