Release notes
Every release, newest first, written as what you get rather than what was touched. The most recent one is open below. Any term here that is not ordinary English is defined on the reference page.
All seven pages now publish machine-readable data. Two did before. Six say what each page holds; the home page describes the product, as it always did. The how-to page publishes the five steps of its recommended flow as steps. The reference page publishes its nineteen glossary terms as nineteen definitions, each with its own address, so an assistant can answer a question about a stack card by quoting the one definition that covers it, not the whole page.
The copy matches the page today. All nineteen definitions and all five steps were checked word for word against what the page shows a reader, before release.
A check went in first, and was watched failing. Machine-readable data that stops parsing fails in silence: no error, nothing visibly broken, and the search engine ignores it until somebody notices. Our checks now refuse a page that carries no data, and refuse data that does not parse.
What they do not check is that the page and its copy stay in step: edit a definition and its copy will not follow.
Six of the seven pages had no main heading at all, so a search engine had almost nothing to go on about what each page was for. All seven have one now, written to say what the page actually covers rather than to chase a phrase. Nothing else about the pages changed: the type is the same size, the home page opens the way it always did.
Nothing here was watching for this. Our own checks now fail when a page goes out with no main heading, or with one that is only the product name. They were watched failing on all seven pages before anything was fixed.
The robots file told a story that was not true. It opened by describing a block that does not exist and then reasoned at length about working around it. That is gone. What is there now is what was actually measured, with the date it was measured.
There is also a plain-text map of the site for assistants, and the GitHub page finally links to the site.
The tool that tidies your project history can no longer lose the trail back to your decisions, and what you were shown when you decided is now checked against what was written down.
The tidy-up could lose the trail back to your decisions. It did not recognise every signpost it writes, so a document collected duplicates and older decisions could end up with nothing pointing at them. Clearing those duplicates by hand was the move that lost them. All are recognised now, and tidying by hand is safe.
Nothing checked that the options stored against your answer were the ones you saw. Now something does. If the list you clicked was ordered differently from the list on the record, you are told while it can still be fixed. If the two cannot be compared, it says so, because silence from a check reads like approval.
A tidy-up that made a document longer called it a saving. It now says the document grew.
Cheaper sessions wherever a project has history to archive, a tidy-up that cannot lose your text, and claims about this method you can rely on.
A project pays for its own past every time you ask it anything. What a session loads is sent again on every request, so old history is a bill that keeps arriving. One project now loads 196,000 characters less, roughly 50,000 tokens returned on every request there. Nothing was deleted: it moved to a linked archive.
The tidy-up could damage what it was tidying. It could lose a sentence you added, or cut the end off a document while reporting a saving. Both are fixed.
It could not read some documents at all, walking past two hundred thousand characters of one project's history. It reads them now, and stops rather than guess.
A new project loaded the wrong shared document on every request, four times the size of the right one.
The health check now reports one total across every project.
Twenty-five sentences claimed more than they delivered. Every one now says what is true.
Fewer abandoned releases, release notes written at the point they cost least, and a health check that will not quietly report on a different project.
Releasing kept getting stuck. Editing the notes that describe a release threw away the review of code those notes never touched, so the whole change had to be read again from the beginning. Two releases in a row were abandoned inside that loop. The two are now kept apart: changing what you say about a release no longer discards what was already checked about the code.
The notes are written last now, on their own, once everything else is finished, rather than first. That is the order that wastes the least, and it applies to every project rather than being advice that one of them follows.
The check that reports on a project task board could pick up a different project's board when run from somewhere unexpected, and report on it without ever saying so. It now declines to answer rather than answer about somebody else.
A project record you can put in order, a test run that leaves your machine as it found it, and release notes that stay short and say what you get.
Your project record can now be put in order. Different parts of the studio wrote times into the same record that disagreed by ten hours, so a later reader could not tell what happened first. They agree now, and every new entry says which clock it came from. Older entries are untouched.
Test runs no longer leave anything on your machine. One pass used to create 341 folders in your temporary directory and remove none of them.
A reason for waiving a rule stays with the file it was about.
A question that events overtook can be closed. Your record stops asking about decisions that no longer matter, and never shows a ruling you did not give.
Release notes are capped at 200 words. Anything longer needs the owner's approval first, and the approval is recorded next to your own changelog rather than somewhere central.
A studio session opens already knowing which faults have returned and carry no ticket. Before this, the record was written and the rule refused, but nothing put either in front of the session that plans the work, so the loop was enforced and not closed.
It says nothing when there is nothing to act on. Not a summary of what passed, not a count: nothing. This block is injected once and then re-sent with every request for the rest of the session, so a reassuring line costs exactly what a useful one costs and carries no information.
It is capped twice, on lines and on characters, and it says when it cuts. A block that quietly stops at its limit tells the reader the list ended.
It can never take a session down. The reader is wrapped on its own and logs its own outcome, so a fault here loses the summary and not the session start.
Honest about what is proved. The reading tool is covered by 51 assertions and a mutation sweep. The wiring into session start was proved by running the real hook end to end and watching the block appear and then not appear, not by an automated test. That is written on the ticket rather than counted as coverage it does not have.
The studio reads every project's doctor rows in one pass and refuses when the same class of fault has returned in a later session carrying no ticket. That is the exact failure this whole line of work was raised about: a fault found, written down, found again, written down again, and never turned into anything.
Two sittings, not two rows, and the difference matters. The same fault written twice by one session is one bad afternoon. The same fault returning in a later session is a standing defect. Counting rows would have refused the first and missed nothing useful.
It finds a board the way the board defines one. A directory holding project.json beside a tickets directory, discovered rather than assumed. The previous generation of this kind of tool looked only for a directory called .board, found nothing in a project that runs 154 live tickets, and wrote that nothing down as a fact about somebody else's project.
It is read-only and it needs no network. Each project writes its own rows at its own wind-down. This opens those files and writes nothing anywhere.
The correction it made to itself on its first real run. It reported that fourteen projects "run no board at all". One of them runs a database-backed board, which a tool that reads files cannot see. The line now says what it actually measured, that no file board was found, and says plainly that this is a limit of the instrument rather than a finding about the project. A negative result carries the search as a premise.
What a session sees at the start is capped, says how many lines it left out, and prints the command that shows the rest. The full record is read on demand and never loaded automatically, because anything loaded automatically is paid for on every single request.
A record, one row per finding, written at the end of every session and read at the start of the next. Each row carries a class, and a class is what lets anybody count how many times the same thing has gone wrong. Two of those rows exist already and both are about the session that built the record.
The number that justifies it. No tool here had ever opened a review transcript. So every finding any reviewer ever made existed only in the conversation that produced it and vanished with it. That is why the same faults keep returning: one ran four sittings in a row, another three, each time found fresh by somebody with no way of knowing it was not the first time.
What changed. tools/doctor-record.js writes, reads, gates and archives the record. It refuses a row with no class, no evidence command, or a finding too short to say what went wrong, and it refuses before it writes anything, so a rejected row leaves the file untouched. A wind-down that wrote no row now blocks the commit, with a written-reason escape that is printed on every run rather than hidden behind a flag.
One row per line. Sessions only ever append; archiving is the one routine that rewrites. Two sessions writing into this repository at the same time is ordinary here and happened twice in the last two sittings. A single record rewritten by both merges silently and wrongly. Whole lines appended by both produce an ordinary conflict a person is shown. Losing a row loudly beats keeping the wrong one quietly. The rewrite is called out rather than glossed because an earlier draft of this entry said the file was only ever appended to, which was a stronger promise than the code kept: archiving rewrites it, and a row another session appended while that ran used to be lost. It is now removed by key from a re-read of the file rather than by position from a stale snapshot, and a row that arrived in between is kept and reported.
The defect found by running it, which reading the code would not have caught. Archiving rebuilt the file from the rows it could parse, so any line it could not parse was deleted with nothing said. The line most likely to be unparseable is the half-written last row of a session that crashed, and archiving is meant to run at every wind-down. The routine that runs most often was the one destroying the evidence the record exists to keep. Reading a damaged record is now tolerant, because a reader that refuses locks out every project at once; rewriting one refuses, because a writer that refuses costs one person one repair.
One helper, tools/patch.js, that every patch script calls. A shell collapses two backslashes to one even when quoted, so a search string containing an escape arrives mangled: it either matches nothing, or writes a corrupted string. That has happened seven times across five sessions. Twice it produced a file that would not parse, and once the studio's own board was down until it was repaired. Two of those sessions responded by writing a note telling the next session to watch for it, and the note is nought for seven.
What changed. The helper refuses an empty anchor, refuses a replacement carrying a stray control character, requires the anchor to match EXACTLY the expected number of times, parses the result in memory before anything reaches disk, and writes atomically then reads back. Nothing is written unless every one of them passes, so a refused patch leaves the file byte-identical.
The part that is new rather than collected. When an anchor matches nothing, it prints the nearest text in the file and the first character that differs, by code point, and says ESCAPE COLLAPSE when that difference is a real control character sitting where the file holds a backslash and a letter. Nobody made that diagnosis in seven instances, because nobody was shown the two strings side by side.
How it was proved. Sixty-four assertions, and fourteen mutations run against them: thirteen killed, one survivor recorded in the file as unprovable rather than left as a silent gap. One mutation exposed a real hang in the occurrence counter that no end-to-end case could reach.
Two rules were reported to the founder as delivered and were not. The em-dash ban was in no shared fragment at all. The reply cap was in seventeen of seventeen role files in each of two projects and in none of the documents either project's session loads, because roles govern dispatched subagents and the session that writes to the founder reads something else. Four sittings of delivering that rule changed nothing.
What changed. tools/check-rule-delivery.js reads a manifest of every rule this studio has claimed to ship, with the destination the claim rests on, and refuses when the text is absent. It understands two kinds of destination: a named file, and the LOADED SET of a CLAUDE.md, which is that file plus its imports resolved all the way down. The second is the one nothing checked. It also refuses when wording a rule REPLACED is still being read, because while both are present the roles and the governance say opposite things. Every refusal prints the full list of files it searched, since an absence reported by an instrument that looked in one place is a claim about the instrument. The em-dash ban is now written into the shared brevity fragment for the first time.
The founder asked for it in their own words: a session should say its goal and the business value at the start, wind-down should compare against that, and the goal should be finished rather than carried. Twenty-seven sittings had happened before one of them stated its goal out loud.
What changed. tools/check-session-goal.js reads a ## Session goal section carrying the goal, the value, when it was stated and a verdict of MET, PARTLY MET or NOT MET, with a reason required for anything carried. It then reads the BOARD, where the same goal is written as a note before work starts, and refuses when the goal first appears after the first ticket of the day. A goal recorded at the end is a description of what happened, and the two are indistinguishable once written down unless something reads the timestamps.
And the warm-start skill no longer prints the whole resume prompt. Its stated reason was that the founder should see what the record claims against what is true. The corrections table printed two paragraphs above it already IS that gap, computed and labelled. So roughly two thousand words per session start, in every project, served neither reader. That is why a three hundred word cap never worked: a cap cannot beat a direct instruction to print the manual.
tools/check-print-anchors.js finds every string a tool prints from more than one place, then every assertion matching it with nothing pinning it to a site. An assertion over a whole output is an assertion about the union of everything that produced it, and a union hides exactly the single-component failures a suite exists to find. This had happened three times, in two files and two languages, and each fix was one more hand-written clause on the one instance somebody noticed.
How it reads a suite. The first version read only quoted strings, found nine results and missed every instance it was written for, because the assertions that matter are written as regular expressions with no quotation mark on the line. It now recovers the literal runs from a regular expression as well. It is a ratchet: forty-one existing findings are recorded and printed in full on every run, and a RISE refuses.
On 2026-09-18 the releases page check went into the record as status ok, exit 0, in the release set, with its own text reading that one heading matched no release and was dropped. Five entries were one command from publishing under a five-day-old headline, because the release note is taken from a DATED heading and an undated one silently selects the previous section. A person caught it. No check did, and one was watching.
What changed. A check that exits 0 while printing a warning is recorded as warned, and the gate refuses on it and quotes the warning. Separately, an undated heading CARRYING release content now refuses outright, the way a near-miss date already did; an undated heading with nothing under it still only warns, so a reader with prose dividers is not locked out.
The check had two modes and neither could answer whether a project's conduct changed. Pooling every session ever written means months of history swamp the sessions since a rule arrived, so the number never moves. The other mode asks the host which transcript is the current session, and against another project the honest answer is that it cannot tell.
What changed. --per-session scores each transcript on its own and prints a row per session, newest first, with --since to bound the window. It never claims to be the current session and says so in its own heading.
The session budget guard stops a session when it has spent more than a segment of work is worth. That segment was a single number shared by every project, measured from three real sessions, and it was right as a default. It was also the only place an answer could go, so a founder who wanted a different ceiling for one project could not have one without silently raising it everywhere.
One project asked for that ceiling in twenty-five sittings out of twenty-six and never got an answer, which is what a question with nowhere legitimate to land looks like.
A project can now name its own in .claude/session-budget.json:
``json { "firstWeighted": 8000000, "setBy": "who and when", "why": "what it is based on" } ``
Read from the working directory upwards, so it works from a subdirectory. The second stop stays half a segment further on, derived rather than configured. A value BELOW the default is honoured too, because a setting that only ever permits more is not a control.
A missing, unreadable or malformed file falls back to the shared default and is not an error. A guard that stops firing because somebody mistyped a config is exactly the failure the guard exists to prevent. Proven rather than asserted: measured at the project root and from a deep subdirectory (both the override), at another project (still the default), and with the file holding broken JSON and then a negative number (both the default). Existing tests 60 passed, 0 failed.
What you need to do. Nothing, unless you want a different ceiling. Every project without that file behaves exactly as before.
Four roles in the base roster carry rules they did not have before. A project had been holding them in its own rulebook while that rulebook said, in its own text, that they belonged here. Some had said so for eight sessions. They are stack-neutral, so by the routing test they are studio lessons, and every project picks them up on its next session.
What you need to do. Nothing. Projects pick these up at their next session start.
The release command now finds out whether it is allowed to publish before it changes anything, instead of halfway through. Nothing about what it publishes has changed.
The document this project reads at the start of every session is a third smaller, and it will stay that way without anybody remembering to make it so. Nothing was deleted.
The rule that a decision reaches you as a prompt you click is now enforced in the one other project on this machine running a board, as well as here. It was meant to reach every project already. It could not, and the reason had nothing to do with where the guard was installed.
When a session is stopped for going over budget, it is now told how many characters it has sent and how much of that was writing files. Until now the guard could say what a session had cost and never what the cost was made of, although every tool call hands it the information.
When an agent is about to record your answer to a decision it never showed you as a clickable prompt, the tool call is now refused before it writes anything. Until this, the rule was measured only at the end of a session, which is after every decision had already been put. The measure could report a breach and could never prevent one.
Three things, in the order they will matter to you. When an agent needs a decision from you, it has to raise your tool's interactive multiple-choice prompt so you answer by clicking, rather than typing a list into a reply and hoping you scroll. There is now an instrument that measures whether that actually happened, and it is the first one this project has ever had for it. And the release gate no longer names a reviewer role that nothing anywhere proved was real.
tools/check-decision-shape.js is the new instrument. It reads the session's own record and the project's board and reports how many decisions were put to you against how many prompts were actually raised, marking each decision as PROMPT or PROSE. It refuses on the board record, which is unambiguous, and only reports on whether a reply merely looks like a list of options, because numbered steps followed by a question is how anybody writes ordinary instructions and refusing on that shape would fail correct work. It runs in the wind-down set. Twenty assertions, and a project with no board reads as cannot-tell rather than as a project in breach.The release gate demanded a role name and nothing proved the name existed. The gate required a reviewer called doctor and matched it by exact name against a hand written constant. Deleting that role from the repository moved no number anywhere: the session start set, the gate's own suite and the roster count were all identical either way, because the roster check counts FILES and the gate matches NAMES. Two surfaces were being compared to each other and neither to the roster, so they could agree perfectly about a role that did not exist.
doctor.md at all, so the gate was printing "start one of: doctor" to an install that could not start one.-Force.A sentence in the shared governance had been false for months and was about to be copied into every project. AGENTS.md said that each reviewer returns PASS or FAIL and that only then is a deploy permitted. No reviewer's verdict has ever blocked anything here, and that includes the work reviewers: nothing in this repository opens a review transcript, so no PASS and no FAIL has ever been read by the gate. What the gate does refuse on is whether a review HAPPENED, which is a different question and the only one it can answer from the record it keeps. A forced governance sync was about to push the old sentence into five projects, so it was corrected first. It now says plainly that no verdict stops a deploy, and that the teeth are the ticket a finding gets written to rather than the gate.
The method reviewer is called the doctor everywhere. The role that checked the installation across projects and the role that checked whether a session followed the method were doing one job from opposite ends, and neither was read by anything. They are one role now. The rename touched the roster, the shared governance, the gate tool and the published site, and every edit was applied by a script that refused unless its pattern matched exactly once, because a plain search for the old name returned more than fourteen hundred hits across a hundred and seventy five files and almost all of them were the word directory.
A review of this release found three things in it that should not have gone out, and the worst one would have deleted most of your roster. None of them reached you, because the review ran before the publish rather than after it, which is the whole reason that step exists.
-Sync -Only <role> deleted every role it was told to skip. The prune added earlier in this same release read the filtered list of roles rather than the full one, so every role you asked it to leave alone looked exactly like a role that no longer exists. Running it for one role reported sixteen others as dropped from the source and would have removed all sixteen from your machine, giving a reason that was untrue of every one of them. -Update went through the same code. It now refuses to prune at all on a filtered run and says why, and it refuses again if the source directory is empty, which would otherwise have turned a sync into an uninstall.The set that runs when you open a session dropped from eleven checks to five. The set that runs before a release dropped from fourteen to nine and from about thirty six minutes to about four. Nothing that protects you was removed. What was removed had, between all of it, refused once in seventy nine recorded runs, and that one refusal was this project tripping over its own paperwork.
health-report, hook-wiring, decision-keys, board-doctor, resume-pointer and session-brief have never refused once between them. comment-shape refused a single time, and that time it was this repository's own new comments raising its own baseline. Each of those was standing in the path between writing a change and being able to test it.deep that nothing gates on. Run them with node tools/run-checks.js --set deep whenever you want the full picture. A check that has earned its keep as a diagnostic has not automatically earned a place in front of your work.mutation-coverage left the release path for the same reason, and it is the big one. It takes about thirty minutes. Its findings are real and they are never urgent: the two open when this change was made were fragments of a diagnostic message that no user of this tool will ever see. A slow check belongs on a schedule you choose, not between you and a release.health-report alone was eighty two per cent of the time your session start took, 3,351ms of 4,065ms, for a row that always exits zero and that no gate can read.reply-shape-recent left the release set. It refused a release when a single banned character appeared in the session's own replies, with no way to clear it, and by this project's own record it blocked four of the last five releases. Nothing a reader of this tool sees was ever at stake. The absolute count is still recorded at every wind-down, so no slip is hidden.board-doctor left the session-start set for a different reason. It exits 1 in a tree that has never run init, so a fresh clone of the published export reported it red before the reader had touched anything. It still runs before a release, where a board is expected to exist.changelog-leak, because it is the one check here that has ever stood between a private name and a public repository. suite, because it is the tool you install. board-audit, roster-count, published-counts, governance-core and releases-page, because each polices a number or a page that a reader actually sees.You can keep your changelog in your own house style. The releases page check now tells you it has nothing to compare rather than refusing, unless your configuration declares that this repository is the one publishing the page.
**What this gives you.** marker this repository happens to use, and the generator could build no page at all, which read as a failure at exit 1. That check runs in the set every install runs at session start, so the refusal arrived before any work did, every time, and the remedy printed alongside it told you to rewrite your own file.$PublishesReleasesPage = $true in your configuration and the check holds your page to your changelog as before. Leave it unset, which is every install that has not deliberately opted in, and a changelog the generator cannot build is reported as nothing to compare. An earlier version of this change inferred the answer from whether the tool was configured at all, which is true of every install, so the refusal came straight back for anyone who set the tool up. A reviewer measured that before it shipped.The two checks that read session transcripts now ask the host which session is running, instead of taking whichever transcript was written most recently.
The role that actually picks work now carries the queue rule the governance document publishes, rather than the one it retired.
base/governance/GOVERNANCE_CORE.md says a CEO instruction jumps the queue only if it belongs to the initiative in flight or is a defect we introduced, and says in so many words that this replaces the older rule that a direct CEO instruction is always top of the queue. base/agents/tech-lead.md still carried the older rule, word for word, in the same range.base/ for the retired wording returned two files before the change and one after, and the survivor is the sentence that does the retiring, which has to keep the words or the retirement stops being findable.Two roster changes, both found by a real defect on a live project the same night, and both of a kind that will be silently wrong on any project rather than only the one that found them.
overflow-wrap: break-word looks like the fix and is not: it wraps the text visually and still lets the unbroken string set the minimum width, so the row overflows by exactly as much as it did with no rule at all, verified to the pixel. word-break: break-word and overflow-wrap: anywhere are the two spellings that work. The trap is somebody later tidying the one that works into the one that does not, so the rule now asks for a comment saying which is load-bearing.The tool that builds the releases page reads your changelog by date heading. A heading that is nearly a date, but not exactly one, matched nothing, and everything under it was quietly discarded while the build carried on and the staleness check reported the page current.
The front door measure reports work that reached a commit without its ticket going through the board, and it refused. There was no exemption, no waiver, no recorded escape of any kind, and a release is gated on it, so one breach anywhere in the recent commits blocked every release until those commits scrolled out of range, including for a session that had done nothing wrong.
The budget guard runs before every tool call in every project, and it kept a small file of its own in your system temp directory to remember what it had already told you. Two faults in that arrangement, both fixed, and the first one is the serious one.
Every refusal below was correct about there being a problem and wrong about what to do next, which is worse than staying quiet, because a remedy that cannot be performed sends you looking for a fault in your own repository.
The check that reads your session and refuses a release over an em-dash now asks a narrower question at release time, and the same absolute question as before at the wind-down.
The check that reports whether work reached a commit without its ticket being started had been refusing since a week earlier, and both reasons were faults in the check.
Everything in the three entries below was reviewed before it shipped, and the review failed it. This entry is what changed as a result, because a changelog that only records what worked is a sales document.
One report was itself wrong and is recorded as wrong: one of the three grammars said to be dropped is read correctly, and the test now asserts what the tool does rather than what the report said.
Not claimed: nobody has yet assessed the same idea on both models and compared them. This puts the lever where it belongs and sets it. Whether it is the better choice is unmeasured.
Measured: 25 assertions, 0 failed. Two defects in it were found by running it against the real rule list rather than by reading the code: one PowerShell spelling of case sensitivity would not compile at all, and the string parser stopped at the first half of a doubled quote, silently reading 20 rules where the file holds 21. Both now have their own fixture. A third was in a test rather than the tool: the usage fixture supplied a valid argument in front of the empty one it meant to test.
Measured: 107 assertions, 0 failed, up from 104. Three mutations against the same tree: putting back the code that shipped 2, deleting the refusal 5, dropping the sentence that explains the timing 1. One of the two new assertions was itself falsified on the first run, returning 1 where it should have returned 2, because it matched wording the passing branch also prints. It was restated rather than removed.
Measured: 116 assertions, 0 failed. Six mutations, each the exact edit named, against the same copy: removing the refusal 5, making the window always empty 5, treating an absent repository as a pass 2, treating an absent timestamp as a pass 2, dropping the line that states the tree did not move 1, and no longer naming the offending commit 1.
Found by another project's round-four method gate.
scrollWidth <= clientWidth + 1 on every visible text-bearing element, and reports the node, the overflow in pixels and the offending string.overflow-x: hidden at a mobile breakpoint clamps the reading to the viewport width whatever the content does. Lifting that axis alone changes nothing, because overflow-x: visible beside overflow-y: auto computes straight back to auto. Both axes must be lifted together, and every reading now states whether it was taken lifted or unlifted, because the two are not comparable.html, body taking overflow-y: auto at the breakpoint, which makes body the scroll container and therefore makes the sticky view rectangle the whole document. Where it was found, lifting overflow and nothing else moved the control 866 pixels with the slack untouched.Found by another project's round-four mobile gate, which proposed the fix by running it rather than by suggesting it. All three are the same root cause wearing different symptoms, so a project that has one very likely has the others.
Why. Two faults with one shape: a rule written where its author lives, then shipped to people whose folders look different. The page check had never once been wrong in the repository that wrote it, because that repository always has a page it generated itself. The preamble check had a fixture for every marker except the one this project's own writing rule produces, so the suite certified a branch no real document could take. Both were found by review rather than by any instrument, and neither would have been visible from inside this tree.
What was deliberately not done. No second copy of the shape rule was made. The founder brief borrows the reply checker's predicate, so widening the strip set fixed both at once, and the measurement was taken before widening rather than after: across 49 transcripts and 3,822 replies, one opened with a hyphen bullet and none would be newly refused. The scope on the page check asks one question about the tree rather than trying to tell two identical files apart, because a reader's drift and ours are the same bytes in the same relation and nothing inside either file can separate them.
Why. One instrument capped the brief in LINES. The other capped an unbroken run of prose in WORDS. Both were right inside their own unit and neither could see the other, so an eleven-line brief was simultaneously within its cap and a hundred and seventy-one words past a limit of eighty. The session-start hook orders that brief printed word for word, so the breach arrived in the first reply of every session, was nobody's writing, and could not be edited away afterwards.
What was deliberately not done. A second number was not chosen here. Picking one would have reproduced the fault one level down: a third unit, agreeing with the other two only for as long as somebody remembered to keep it agreeing. The predicate and the limit are imported from the instrument that does the refusing, so there is one definition and the two cannot drift.
The rule is a shape and not a length. The same content that fails as a paragraph passes as bullets, and there is a test that asserts exactly that, because otherwise this is a word cap wearing a different name and it would be met by hiding detail rather than by writing better.
The check comparing the published releases page against the changelog it is generated from now runs at session start as well as at release. If the page is behind, you are told in the first three seconds of the next session rather than the next time somebody publishes. A tree with no page, or a changelog with nothing dated in it yet, now reports advisory instead of red: that is somebody running the method without publishing a website, which is an absence rather than a disagreement, and every neighbouring check already draws the same distinction.
Why. It ran in the release set only. Every wind-down writes a release note into the changelog and does not rebuild the page, so the page broke at the exact commit that broke it and the only thing watching did not run again until a publish. That is not theoretical: the page was found drifted at HEAD, confirmed by running the suite against a clean archive of HEAD rather than assumed, and the same failure took an entire test run down with it. The check compares two files already on disk, wants no network and no git, and costs about fifty milliseconds.
add --under <ref> when you raise it, or under <ref> --under <ref> for anything already on the board. Nothing needs migrating: a ticket with no initiative is simply a ticket whose field is absent, and every existing ticket stays as it is.evict moves it and everything under it out together, with one reason recorded on every ticket. It either happens or it does not: a journal is written before the first ticket moves, and if a write fails part way the tickets that moved are put back. If the board is ever left part way through, every command that WRITES refuses and says how to undo it, while every command that READS still works, because a half-finished board is exactly the thing you need to be able to look at.wip and the rendered board show the relation, so you can see which initiative each piece of work serves without opening it, and an initiative whose work is all closed says so.Why. The limit on work in progress was a COUNT, and a count cannot tell a small that FINISHES an initiative from one that STARTS a fourth. Measured before any of this was built: 71 of 74 starts finished without being sent back, the median time in progress was under two hours, and the limit had been reached once in the board's life. So starting was never the problem. The board still reached ninety waiting items, and 73 of those named the work they belonged to in prose that nothing could read. Tightening the number would have changed none of that; making the relationship real is what lets the board refuse the thing that actually goes wrong.
A rule about how work is picked has changed with it. An instruction that arrives mid-session now jumps the queue only if it belongs to the initiative already running, or is a defect we introduced. Anything else becomes a ticket and you are told that is where it went, with a count of what is already running. It is not refused and it is not silently absorbed, because both of those end with work nobody chose. Reprioritising is still yours to do at any moment, and it now means evicting the initiative rather than starting a second one beside it. The older rule put every mid-session idea straight to the top, which is how the waiting list grew from arrivals rather than from starts: 2.45 things arriving for every one closed, over fourteen active days.
Two things worth knowing about the checks. Moving that limit turned nineteen published claims red at once across nine files, seven of which are handed to every project rather than being pages on the site, and all nineteen were rewritten in the same change. And the release gate that asks whether anyone reviewed the work had to be split first: the same role now both reviews the method and reports breaches, so being started is no longer enough on its own, and the dispatch has to say which of the two it is. A forgotten marker refuses a release rather than clearing one, which is the direction that fails safely.
Why it was wrong before. A number written once in prose, on a page nobody edits again, goes false on the day somebody changes the thing it counts, and the first person to notice is a reader. That had already happened twice on this site. The guard had a subtler version of the same fault: its own explanation argued entirely about cost, it computed the cost on every call, and then it decided when to stop by counting calls and printed the cost as decoration.
One thing found while doing it. The releases page had drifted from the changelog it is generated from: the previous release note was written and the page never rebuilt, so the newest entry was missing from the published page. It is rebuilt here.
-Doctor prints is worked out by a completely separate piece of code that had the identical defect. Fixing only the check would have left the two disagreeing, with nothing anywhere that would notice, so both were fixed together and there is now a test that runs both and refuses if they give different answers. That test earned itself immediately: the repair to the health report went in with a mangled pattern that silently dropped every document whose path contained the letter s, and comparing the two answers side by side is the only thing that caught it.GOVERNANCE_CORE.md, roughly a quarter of the size, and the long document stays beside it as the reasoning behind each rule./warm-start, for asking on demand, and it tells you which parts have gone out of date rather than handing them to you as though they were checked.The full technical detail behind every release is in the changelog.