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
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.
100 updates on this page, newest first. Show all
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