Repository navigation
feat(pm): name the two measured claim near-miss forms so the sweep sounds on the unreadable claims - #18911
Merged
Conversation
…unds on them `OWNERSHIP_MARKER_NEAR_MISS_FORMS` carried five forms, every one of which requires a separator after the marker word. Four OPEN cards carry a real ownership record in a spelling with no separator at all — a `## Claim + dispatch` heading, a bare `## Claim`, and prose opening `Claiming …` / `Claimed by …` — so the census read them as absence. The reader is unchanged: `CLAIM_COMMENT_MARKER`, `RELEASE_COMMENT_MARKER` and `markerMatches` are byte-identical, because the contract names one spelling and the repair direction is the WRITE side. Two forms added, both measured over the whole open board (514 open cards, the 459 carrying comments, 131,587 comment lines, read through the REST proxy at 0b31d90): - `heading-bare` — a heading whose word carries no canonical colon: 10 lines on 10 cards, all real records; symmetric across both directives. - `bare-word` — the participle at line start with no separator: 13 lines on 13 cards, all claim-shaped; claim side only, and not the bare imperative, both omissions carrying their measured counterfactual in the docblock. BATTERY68 drives every new member against its own measured `example`, pins the four rows by card and comment id, pins the ordering against the older forms, and carries the silence controls the measurement turned up (a heading naming a file, the protocol's own imperative sentence, release-board prose). The roster id pin and the battery floor rise with it (98/94 -> 140/136). Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF Co-authored-by: Claude <noreply@anthropic.com>
Collaborator
Author
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
This was referenced Sep 18, 2026
os-elon-musk
marked this pull request as ready for review
September 18, 2026 05:38
This was referenced Sep 18, 2026
This was referenced Sep 18, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 28, 2026
… real 422 as a missing route (objectstack-ai#18934) Fixes objectstack-ai#18843 Clause-②: no `post-stamped.mjs` reported a real HTTP 422 from the platform as exit 3 `EXIT_PREREQUISITE_NOT_MET` — 「no route, no act at all」 — and prescribed a transport remedy. This gives the size refusal an exit class of its own, whose text carries the bytes SENT, the measured cap for THAT surface, and the remedy that works; and it homes the comment cap beside `ISSUE_BODY_LIMIT`, which the card says is the same card. ## The fixture, verbatim, and where it came from Built from the record on probe objectstack#18826 — comment id **5722505167**, written 2026-09-17T23:19:18Z. ⛔ No new oversized write was made anywhere to reproduce this. Captured there from `POST /issues/{n}/comments` at 262,145 bytes: ```json {"resource":"IssueComment","code":"unprocessable","field":"data", "message":"Body is too long (maximum is 65536 characters)"} ``` ## The two readings the dispatch carried as unmeasured, now measured 1. **The 422 message is NOT the same string on the two endpoints.** The record is explicit: the create side answers `Body is too long (maximum is 65536 characters)`, and `PATCH /issues/comments/{id}` answers the same sentence with a lower-case `body`; `POST /issues` says it twice. So the trigger is case-insensitive rather than an equality — an equality pinned to the create-side capital would have left the update side at exit 3. Pinned in both spellings, and the ablation below proves the flag is load-bearing.⚠️ One correction to the dispatch's framing: `PATCH /issues/comments/{id}` is not a surface this tool ever writes. Its two write surfaces are `POST /issues/{n}/comments` (`--comment`) and `PATCH /issues/{n}` (`--body`). Both caps are declared; the update-side spelling still matters, because it is the second measured spelling of the sentence the trigger keys on. 2. **The two caps live as two constants, beside each other in `check-half-states.mjs`.** `COMMENT_BODY_LIMIT` is declared after `MARKDOWN_BODY_BUDGET`, with its own bisection provenance. It is homed there rather than in `post-stamped.mjs` on a measured ground: five workflow comments (`cross-repo-issue-closer.yml`, `merge-queue-triage.yml`, `scheduled-full-run-card.yml`, `test-nightly-tiers.yml`) already send a reader to `ISSUE_BODY_LIMIT` in that file for the number and warn against re-deriving 65,536 — the folklore grows back wherever one of two caps is kept somewhere else. ⛔ Two constants and not one shared number: two surfaces measured independently that agree is not one measurement, and the day the platform moves one a shared number would lie about the other. ## The exit register, before and after | code | before | after | |---:|---|---| | 0 | written, and everything sent is on the platform | unchanged | | 1 | usage | unchanged | | 2 | the body broke the stamp contract | unchanged | | 3 | PREREQUISITE NOT MET — no route, no token, no act at all | unchanged, and it now prints what the platform said | | 4 | written, and the platform did NOT store it | unchanged | | 5 | — | **TOO LARGE — the platform ANSWERED and refused the body for its size. Nothing written; shorten or split it. ⛔ Not a route problem.** | `EXIT_TOO_LARGE = 5` is declared in `post-stamped.mjs` beside `EXIT_NOT_STORED`. The usage text, the not-stored text and the prerequisite text each name it, so a reader of a 3 or a 4 learns the fifth code exists. ## The reader test A seat that sends a 300 KB comment body now reads: **exit 5, 307,200 byte(s) sent, cap 262,144 bytes for a comment, 45,056 byte(s) over it, shorten or split the body** — and ⛔ not a word about routes or tokens. Verbatim from the `--self-test` fixture: ```text post-stamped: TOO LARGE — the platform ANSWERED this write and REFUSED it: the body is over the cap for this surface. NOTHING WAS WRITTEN. Sent to objectstack-ai#18843: 307,200 byte(s) as a comment (`POST /issues/{n}/comments`). The measured cap for a comment is 262,144 bytes — UTF-8 BYTES, bisected on objectstack#18826, one byte either side. This body is 45,056 byte(s) over it. The platform said: Validation Failed · Body is too long (maximum is 65536 characters) ⛔ Its number is FALSE in unit and value — the same fleet measured a 262,144-BYTE comment stored, four times what that sentence claims as a maximum. ``` ## What the class deliberately does NOT take - **Not every 422.** The issues listing refuses deep pagination — page 99 stores, page 100 answers 422 (measured 2026-08-31, recorded in `check-half-states.mjs`) — and that refusal is about a cursor. It keeps its 3, pinned as a control, as do the same sentence on a 403, a 500 and a 200. - **Not the platform's number.** The trigger stops before `maximum is`, so keying on a false clause cannot un-classify the refusal the day GitHub corrects its own text; a text carrying only that clause is pinned NOT to classify. - **Not the read path.** A read sends no body, so the `--body` pre-read keeps its PREREQUISITE reading unchanged. - **Not a widened `--body` story.** Over the cap, `PATCH /issues/{n}` refuses SILENTLY (200, old body kept, objectstack-ai#18793), which is exit 4's population; only a 422 the platform actually answers reaches 5. ## A refusal the cap does not explain If the platform ever refuses a body at or under the measured cap, the text says the refusal CONTRADICTS the reading and asks for a re-measurement rather than printing an overage of zero — `over` is `null`, never 0. The ordinary over-cap text is pinned NOT to carry that sentence. ## Self-test counts | battery ledger | before | after | |---|---:|---:| | `post-stamped --self-test` cases | 349 | **402** | | `post-stamped` batteries (floor) | 13 | **14** | | new battery `the size refusal: …` (registered/floor) | — | **53 / 53** | | `check-half-states --self-test` cases | 4,963 | **4,978** | | battery `ISSUE_BODY_LIMIT measured cap` (registered/floor) | 40 / 37 | **55 / 52** | The check-half-states "before" is the merge base `d5ccfb4e3`: 4,978 minus the 15 cases this PR registers. Two independent readings agree on it — the dispatch recorded 4,963 at PR objectstack-ai#18911's head, and this branch measures 4,921 at its own branch point `625db0e85`, which is before objectstack-ai#18911 landed. Both floors RISE by exactly the number of cases added; neither is held flat. ## Ablations — each mutation proven on disk, each restore proven against HEAD All four through `scripts/ablation-replace.mjs`, which asserts the anchor count fell, the blob hash changed, and after restore that the blob equals HEAD's and `git diff HEAD` is empty. | mutation | expected | observed | |---|---|---| | drop the `i` flag from `BODY_TOO_LONG_RE` | red | **19 of 402 cases fail by name**; the lower-case pin stays green, so the ablation discriminates | | put `ROUTE_REMEDY` in the size text's `Fix:` line | red | **exactly the 2 held-apart pins fail** | | `COMMENT_BODY_LIMIT` 262144 → 65536 | red | **5 of 4,978 cases fail by name** | | delete `UTF-8 BYTES` from the comment cap's docblock | red | **exactly 1 pin fails** — and the sibling docblock also contains that phrase, so a mis-pointed extractor would have stayed green | ⭐ **The first ablation found a defect in this PR's own first draft.** It came back GREEN: the classifier was rebuilding the regex from `.source` with the flags retyped at the call site, so the exported constant documented a rule it did not carry and every case-insensitivity pin was vacuous. Fixed in `36ba41a64`, after which the same ablation turns 19 cases red. A second pass (`290acfad0`) made those pins fail by NAME rather than crash on a TypeError before the verdict machinery ran. ## Gates Derived in the worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at HEAD `0e5e054d3`: **39 families, all 39 run, all exit 0**, exit codes captured by redirect-then-`$?`. `--ran` reconciliation: 39 derived, 39 run, 0 UNRUN. The first derivation (before the merge, one path) gave 29; adding `check-half-states.mjs` grew it by 10 and those were run too. Each edited script's own `--self-test` is in that set (`check:pm-post-stamped`, `check:pm-half-states`). `git grep` of both filenames under tests finds no separate suite — the `--self-test` IS the suite. **ESLint was not narrowed at all.** The repo-wide run is the one that was taken: `pnpm lint` (= `eslint . --no-inline-config`, this repo's only style authority) **exit 0** at HEAD `0e5e054d3`. The targeted run over the two changed files is there too — 2 files, 0 errors, 0 warnings — but it is not what this PR rests on. ## Acceptance notes - **Exit 3 still hosts other refusals the platform ANSWERED.** The deep pagination 422 is the measured one, and its remedy text is a route remedy just as wrongly. ⛔ Not filed: it is unreachable through this tool (a comment list would need 10,000 comments to reach page 100), so it is an observation, not a reproducible defect. - **`rest()` now surfaces the platform's sentence on every non-2xx**, which is the same defect family (the tool discarding the answer it was given) and is mechanically part of this change; it is not a separate fix. --- _Generated by [Claude Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 28, 2026
…se announcements (objectstack-ai#19041) Fixes objectstack-ai#18926 Clause-②: no The near-miss ownership census (`ownershipMarkerNearMisses` over `OWNERSHIP_MARKER_NEAR_MISS_FORMS`, landed by PR objectstack-ai#18911 / objectstack-ai#18831) lists cards with a near-miss ownership line and no readable `Claim:` / `Release:`. Five of objectstack-ai#18914's sixteen rows have no ownership record to normalise: four `pm:seat` posts (objectstack-ai#6017 · objectstack-ai#6021 · objectstack-ai#6026 · objectstack-ai#6367), whose ownership is their BODY plus their audit comments and whose comments carry shift narration, and objectstack-ai#17536's `## Released — PR objectstack-ai#17517 merged. pm:blocked -> pm:queue`, a blocker release naming a PR on a card that was never claimed. Two exclusions, both COUNTED rather than dropped, and they are deliberately different KINDS: a seat post leaves the POPULATION (no record is owed on the thread), a release announcement leaves the near-miss CHANNEL (the line is not a refused record). - `ownershipCensusSpeaksAbout(issue)` — a `pm:seat` thread is out of the population, read off the label page the sweep already holds, so the census keeps its "buys NOTHING" contract. Gated at the call site like every other population predicate in this file (`h47SpeaksAbout`, `h58SpeaksAbout`, `h67SpeaksAbout`), which is also what makes it usable by the direct full-board caller that produced objectstack-ai#18914's rows. - `releaseAnnouncementHeading(formId, prefix, line)` — a heading whose word is the PARTICIPLE `Released`, whose remainder names the `PR #n` that landed or the `pm:*` -> `pm:*` transition it unblocked, and which names NO session. The line keeps its card, comment id and prefix and moves to a second channel, so the two arrays partition exactly what the single array used to carry. - `ownershipMarkerNearMissCensus` returns `{ misses, announcements }`; `ownershipMarkerNearMisses` stays the near-miss half and the name every caller and every fixture already uses. The vocabulary is untouched (no form added, no form removed) and so is the reader: no change to what a readable `Claim:` / `Release:` is. H8 / H46 territory is not entered. ## The three narrowings, each measured against the corpus line that forced it Every one of these leaves the line IN the census — the safe direction for a filter whose failure mode is silence. | narrowing | the line that forced it | verdict | |---|---|---| | HEADING forms only | objectstack-ai#18740 comment 5723834336 `Release-landed: … (PR objectstack-ai#18852)` | `separator`, not a heading — stays listed | | the PARTICIPLE only | objectstack-ai#16233 comment 5704218834 `### Release: PR objectstack-ai#18523 …` | bare noun opens the directive — stays listed | | NO session token (`session` or the Chinese spelling) | objectstack-ai#6023 comment 5552092492 `## Released tail — in flight under THIS session's process tree` | names a session — stays listed | ## The census over the live board — BEFORE / AFTER Two readings, both read-only through the session proxy, both with the `--use-env-proxy` re-exec the file performs itself. **(1) The instrument's own clause, one full sweep each.** BEFORE at the base (`784366372`), AFTER at this branch's head: ``` BEFORE Ownership-marker near misses: 17 line(s) on 15 of 222 thread(s) … AFTER Ownership-marker near misses: 16 line(s) on 14 of 226 thread(s) … 0 `pm:seat` thread(s) are OUT of that population (0 near-miss line(s) not listed above) … 1 further line(s) are release ANNOUNCEMENTS rather than refused records … — objectstack-ai#17536 comment 5625672905 「## Released」 (names a PR, names a `pm:*` transition) ```⚠️ MEASURED AND REPORTED AS MEASURED: the population clause prints `0` / `0` on the live sweep, and that is not the filter failing. The census reads only threads `commentCache` already holds, and a `pm:seat` post's page enters that cache in the H44 pass BELOW the census loop (`commentCache.set` at the seat-comments leg; `seatPostRowsFor` itself writes only `seatPageCache`). So on the sweep as currently ordered a seat thread is UNJUDGED by the census, never listed — which is why the clause renders both numbers unconditionally instead of staying silent: a run with nothing excluded and a run where the exclusion stopped being applied must not print alike. The ordering itself is left exactly as it is; moving the census would widen what it reads, which this card does not ask for. **(2) The full-board corpus — the population objectstack-ai#18914's sixteen rows were actually measured over**, and the reading where the population filter bites. Read 2026-09-18 13:0xZ, tree `784366372`: 539 open cards, 484 of them carrying a comment, the census run over every thread: ``` BEFORE (vocabulary alone): 36 line(s) on 30 card(s) pm:seat threads OUT of the population: 5 thread(s), 9 line(s) objectstack-ai#6017 · objectstack-ai#6021 · objectstack-ai#6023 (×4) · objectstack-ai#6026 · objectstack-ai#6367 release ANNOUNCEMENTS out of the near-miss channel: 1 line objectstack-ai#17536 comment 5625672905 「## Released」 AFTER (near-miss channel): 26 line(s) on 24 card(s) partition check: 26 + 9 + 1 === 36 -> true ``` The five rows the card names are gone, each with its printed reason. Of objectstack-ai#18914's other eleven, the ten that are still open are listed byte-identically — objectstack-ai#11633 · objectstack-ai#11663 · objectstack-ai#14512 · objectstack-ai#15638 · objectstack-ai#6736 · objectstack-ai#9659 · objectstack-ai#9707 · objectstack-ai#11286 · objectstack-ai#11453 · objectstack-ai#11925. The eleventh, objectstack-ai#18755, is CLOSED (`pm:done`) and so is absent from an open-board read altogether, not dropped by anything here.⚠️ objectstack-ai#6023 is a FIFTH `pm:seat` post the filing card's table does not name, carrying four narration lines. It is out of the population on the same grounds as the four, and the count above says so rather than folding it into the named five. ## Self-test — floors rise, and the controls are inside the floored battery ``` BEFORE 4978 cases pass. … H2/H47/H66 decorated ownership marker 140/136 … AFTER 5031 cases pass. … H2/H47/H66 decorated ownership marker 193/184 … ``` +53 cases, all in the floored battery, pin raised 136 -> 184 (the file's "just under the count" convention, ~95%). What they floor is a NARROWING — the one direction in which a suite can go green by asserting less — so the firing controls live inside the same battery: the five measured rows as fixtures (each line pasted from the REST read of its own comment), the three narrowings above each pinned through its corpus line, the positive control that objectstack-ai#14512's `Claiming` line is still listed on the near-miss channel and its card still in the population, and the ablation pin that the excluded line IS a near miss under the vocabulary alone (so the exclusion pin is capable of failing). One landed case was RE-POINTED, not deleted: `objectstack-ai#18831: the release side of the HEADING form is named too` carried objectstack-ai#17536's line as its fixture, which is the very line this card moves to the announcement channel. Its claim is untouched and still floored; its fixture is now objectstack-ai#16712 comment 5605768467 (`## Release + seat ruling — …`, a live release heading that names no PR and no transition), and a sibling case pins where the old fixture went. ## Verification - `pnpm check:pm-half-states` — exit 0, `✓ check-half-states self-test: 5031 cases pass.` - Gates derived from this worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (1 path vs merge base `784366372`): 39 commands, 38 run in the foreground, all `exit 0`, reconciled with `--ran`. `pnpm check:pm-dispatch-gates` exceeds the foreground cap and was detached. - The derivation reports a STALE TREE against `origin/main` `dbd474431` (`.github/workflows/pr-automation.yml`). Read: that change is prose inside the `Check Changeset` job (objectstack-ai#18375's route-0 discriminator) and declares no family, so no command is missing from the list above. - No changeset: `scripts/pm/**` publishes nothing from any released package. Route 0 of that same discriminator does not apply — this PR edits no existing changeset. - Control-byte self-scan over the touched file: no hits. **Reader test** — one line, from the worktree root, prints `0 1 false`: the excluded line is no longer a near miss, it IS counted on the announcement channel, and a `pm:seat` thread is out of the population. ```bash node -e 'const L="## Released — **PR objectstack-ai#17517 merged.** `pm:blocked` → `pm:queue`";import("./scripts/pm/check-half-states.mjs").then(m=>console.log(m.ownershipMarkerNearMisses([{id:1,body:L}]).length,m.ownershipMarkerNearMissCensus([{id:1,body:L}]).announcements.length,m.ownershipCensusSpeaksAbout({labels:[{name:"pm:seat"}]})))' ``` ## Acceptance notes Noted, not filed — observations, no card: - The `separator` form reads `Release-landed:` (objectstack-ai#18740 ×3, objectstack-ai#16529 comment 5720102045) as a near miss. Those are report-line labels, not ownership records, and they are the largest remaining class of the same kind this card narrows. Deliberately left listed: the ruling scopes this card to the seat population and the release HEADING, and ⛔ no widening or further narrowing of the vocabulary. Whoever next touches `OWNERSHIP_MARKER_NEAR_MISS_FORMS` is the successor; today there is none. - The census loop sits ABOVE the pass that puts a `pm:seat` page into `commentCache`, so the census never judges a seat thread on a live sweep. Stated in the sweep reading above and in the code comment; not filed, because changing the ordering would widen the census's judged population, which is a decision and not a defect. - The dispatch's assumption that the five rows' FIRST lines are the fixtures was falsified by the paste: on three of the five (objectstack-ai#6017 · objectstack-ai#6021 · objectstack-ai#6367) the census names a later narration line and the heading the card's table quotes carries no near miss at all. The fixtures are the lines the census NAMES; the discrepancy is pinned as a case rather than left as a silence. Serial: `scripts/pm/check-half-states.mjs`. PR objectstack-ai#18980 (objectstack-ai#18939) is a parallel draft on this file with old-line hunks :19613–:19659 and :35268 — the proxy-rearm plan and its guard — disjoint from the census hunks here (:1362–:1440 and the BATTERY68 battery). Nothing there is touched. Whichever lands second merges `origin/main` first. Not governed (`scripts/pm/**`): draft, for the `domain:skills` seat's contract-tier review. --- _Generated by [Claude Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_ Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #18831
Clause-②: no
This is the INSTRUMENT half of #18831: the measured spellings are NAMED as near-miss forms, so the census SOUNDS on the four rows instead of reading absence. The WRITER half — a compliant
Claim:(or aRelease:) on each of those four cards — is the holders', and was requested on #18886 (cli, for #15638) · #18887 (spec, for #14512) · #18888 (services, for #11633 and #11663). #18831 closes only when those four rows are discharged, so this PR saysPart ofand carries no closing keyword: #18831 remains open after this lands.The reader is untouched, on purpose
The binding constraint is the triage grading (5722974011), kept in its own words:
So
CLAIM_COMMENT_MARKER,RELEASE_COMMENT_MARKERandmarkerMatchesare byte-identical in this diff — the identity record's spelling stays exactly whatAGENTS.md:394–:395 names, and #18829's undecorator question is decided by nothing here. What grew is onlyOWNERSHIP_MARKER_NEAR_MISS_FORMS, whose whole job is to make a refusal audible, and which is report-only and never a half-state verdict by itself. No ownership row (H2 / H34 / H47 / H66 / H67) changes its verdict on any card.The premise, re-read live rather than taken from the card
All four rows read through the REST proxy at 2026-09-18T04:26Z, each thread in full:
pm:queuedomain:clip3, assigneeos-litant## Claimheading, thenClaimed by the os-dev seat. Session …bare-word, prefixClaimed)pm:queuedomain:specp3, no assigneeClaiming the producer half (…)bare-word, prefixClaiming)trackingpm:blockingdomain:services## Claim + dispatch — design only…heading-bare, prefix## Claim)securitytrackingpm:epicdomain:services## Claim + dispatch — design only…heading-bare, prefix## Claim)None of the four carries a readable
Claim:orRelease:anywhere in its thread — checked by runningmarkerMatchesover every comment row of each, not by eye. The card's premise holds.One honest boundary: #15638's
## Claimheading carries nothing after the word, and the census deliberately skips a form whose remainder is empty (「a directive with NO record after it is silent — a census, not a heading count」). That card is named through its prose line instead, and the two behaviours are pinned together rather than traded against each other.The two forms, chosen from a measurement and not from symmetry
Measured over the whole open board — objectstack's 514 OPEN cards, the 459 of them carrying a comment, 131,587 comment lines, read through the REST proxy 2026-09-18T04:26Z–04:31Z at
0b31d90fb:heading-bare— a markdown heading whose word carries NO canonical colon (a+, a dash, or nothing): 10 lines on 10 cards, every one a real ownership record. Symmetric across both directives and carrying the inflected spellings, because the heading prefix is already the strong signal itsheadingsibling reads. The only prose casualty the measurement turned up was### release-aftercare.md— a FILE NAME — and requiring whitespace-or-end after the word excludes it by construction (pinned as a control).bare-word— the PARTICIPLE at line start with no separator at all: 13 lines on 13 cards, every one claim-shaped. Claim side only, and deliberately not the bare imperative. Both omissions carry their counterfactual in the docblock:Release timing, …,Release checklist, …,Release board: …,Release condition:,Release confirmed …): that noun opens sentences in this repo, the claim participles do not.A heading carrying an inflected word AND the canonical colon (
## Released:) is named by no form: measured ZERO occurrences over the corpus, so it is left unnamed with its reading written down rather than guessed at.Board census — with the unit, and the counterfactual
Same corpus, run through the sweep's own predicate (
ownershipMarkerNearMisses) over a REST-listed open-card set, becausecheck-half-states.mjsreads the whole board and exceeds this container's foreground cap:Claim:/Release:anywhere in the threadBy form, after:
bare-word13 ·heading-bare10 ·separator5 ·list-item5 ·heading4. What would have made the number different: the two omissions above (+5 lines for the bare imperative, +19 for the release participles, 7 of the latter prose); and the empty-remainder rule, which holds back #15638's## Claimline and every bare## Release:heading. The 14 cards that moved from silent to audible with an unreadable ownership record are the census's own answer to 「四张活卡的身份记录此刻不可读」 — there were never four.Verification
node scripts/pm/check-half-states.mjs --self-test— exit 0, 4963 cases pass (4921 before), batteryH2/H47/H66 decorated ownership marker140/136 (98/94 before; the floor rose with the battery, per the roster's own rule).pnpm check:pm-half-statesis that same self-test: exit 0.grep -c1 → 0 for each; blob5c5a24a7→b35ec2ed), then the self-test re-run: exit 1, 18 of 4955 cases failed — the roster id pin plus all four measured rows and the "every one of the four is named" pin. Restored withgit checkout HEAD --, proven byte-identical (blob back to5c5a24a7,git diff HEADempty,git status --porcelainempty). Expected direction was "turns red"; that is what it did.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no hand-fed paths; change set = 1 committed path, merge base600b1e2ab): 38 commands, 37 exit 0. The 38th,pnpm check:pm-dispatch-gates, spawns every gate script's own self-test and exceeds this container's ~10-minute foreground cap in both legs — NOT MEASURED (exit 124fromtimeout, 0✗in 1818 lines of output), left to CI rather than reported as a pass or a red. Reconciled with--ran.origin/mainwas merged in (600b1e2ab) before the gate run; the merge touched no file this diff touches.Acceptance notes
skip-changeset:scripts/pm/**is published by nothing — no package'sfiles[]ships it, and the diff is one PM instrument plus its self-test. HenceClause-②: no.check-half-states.mjs(re-synced only through objectui#9395); the writer-side text on the four cards; the three request cards.Generated by Claude Code