Skip to content

feat(pm): name the two measured claim near-miss forms so the sweep sounds on the unreadable claims - #18911

Merged
os-elon-musk merged 2 commits into
mainfrom
claude/issue-18831-claim-near-miss-forms
Sep 18, 2026
Merged

os-elon-musk merged 2 commits into
mainfrom
claude/issue-18831-claim-near-miss-forms

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

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 a Release:) 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 says Part of and 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:

派工时的边界要写死:修的是那四条评论(请原作者或按协议补一条合规的 Claim:),⛔ 不是放宽读者 —— 放宽读者会同时改变 #18829 正在等裁的那条定义,把一个待裁的问题用一次实现悄悄裁掉。

So CLAIM_COMMENT_MARKER, RELEASE_COMMENT_MARKER and markerMatches are byte-identical in this diff — the identity record's spelling stays exactly what AGENTS.md :394–:395 names, and #18829's undecorator question is decided by nothing here. What grew is only OWNERSHIP_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:

card state the record, as written comment near misses BEFORE AFTER
#15638 open, pm:queue domain:cli p3, assignee os-litant ## Claim heading, then Claimed by the os-dev seat. Session … 5555005915 0 1 (bare-word, prefix Claimed)
#14512 open, pm:queue domain:spec p3, no assignee Claiming the producer half (…) 5523309738 0 1 (bare-word, prefix Claiming)
#11633 open, tracking pm:blocking domain:services ## Claim + dispatch — design only… 5394770585 0 1 (heading-bare, prefix ## Claim)
#11663 open, p0 security tracking pm:epic domain:services ## Claim + dispatch — design only… 5394306066 0 1 (heading-bare, prefix ## Claim)

None of the four carries a readable Claim: or Release: anywhere in its thread — checked by running markerMatches over every comment row of each, not by eye. The card's premise holds.

One honest boundary: #15638's ## Claim heading 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 its heading sibling 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:
    • the bare imperative would have added 5 lines on 5 cards (all real records today) and would also read the protocol's own sentence 「Claim the issue BEFORE you write any code」 — which seats quote — as a near miss. A participle cannot open an imperative; that is the whole discriminator, which is why the omission is the WORD and not a lookahead full of English determiners.
    • the release participles would have added 19 lines on 12 cards, of which 7 are ordinary release-board prose (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, because check-half-states.mjs reads the whole board and exceeds this container's foreground cap:

reading before this diff after
near-miss LINES 14 37
near-miss CARDS 10 of 459 threads 32 of 459 threads
…of those cards, carrying NO readable Claim:/Release: anywhere in the thread 2 16
the four cards #18831 names 0 4

By form, after: bare-word 13 · heading-bare 10 · separator 5 · list-item 5 · heading 4. 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 ## Claim line 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), battery H2/H47/H66 decorated ownership marker 140/136 (98/94 before; the floor rose with the battery, per the roster's own rule). pnpm check:pm-half-states is that same self-test: exit 0.
  • Ablation — the two added members deleted from the roster on a committed tree, the deletion proven on disk (grep -c 1 → 0 for each; blob 5c5a24a7 → 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 with git checkout HEAD --, proven byte-identical (blob back to 5c5a24a7, git diff HEAD empty, git status --porcelain empty). Expected direction was "turns red"; that is what it did.
  • Gates, derived from this worktree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no hand-fed paths; change set = 1 committed path, merge base 600b1e2ab): 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 124 from timeout, 0 ✗ in 1818 lines of output), left to CI rather than reported as a pass or a red. Reconciled with --ran.
  • origin/main was 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's files[] ships it, and the diff is one PM instrument plus its self-test. Hence Clause-②: no.
  • Not touched, deliberately: objectui's byte-pinned copy of check-half-states.mjs (re-synced only through objectui#9395); the writer-side text on the four cards; the three request cards.
  • Noted, not filed: the release-side bare word is a precision question, not a defect — its counts and its prose class are recorded in the roster's own docblock, which is where the next editor of the roster reads them, and the roster is the only surface that could carry the change.

Generated by Claude Code

…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>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c8d3f36c9e10c86a85f378a9ac1616b1ddf56851

① Derived judgments

② Semver level

  • skip-changeset is correct and sits on the PR, where Check Changeset reads it: scripts/pm/** ships in no package's files[]. Clause-②: no on the claim and in the body; PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 18911 at 2026-09-18T05:30Z: exit 0 (derivation part-of), one live claim, no widening tell.

③ Boundary flags

  • Dev flags: none (open_questions empty). Deviations accepted: the label went on the PR, not the card (the dispatch's write-budget line named the wrong target; the dev's reading is the right one); origin/main merged before the gate run on a STALE TREE refusal (AGENTS.md §10 asks for it; the merge touched nothing this diff touches); the census ran over a REST-listed open-card set through the sweep's own exported predicate, pre-authorised; bare-word narrowed to the claim participles from measurement; no 速读 section — scripts/pm/** is not a governed surface; check:pm-dispatch-gates NOT MEASURED (two legs cut by the foreground cap, 0 ✗ before the cut) and declared as such in the --ran reconciliation (38 derived, 37 run, 1 NOT-MEASURED) — CI measures it.
  • The seat's own correction: the dispatch's write budget said POST /issues/18831/labels; the correct target is the PR, as the dev wrote.

Implemented-by: claude/issue-18831-claim-near-miss-forms
Reviewed-by: session_01BTeBejoPUvRHN8WdAJC6oF

VERDICT: PASS


Generated by Claude Code

@os-elon-musk
os-elon-musk marked this pull request as ready for review September 18, 2026 05:38
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit 9364e1b Sep 18, 2026
35 checks passed
@os-elon-musk
os-elon-musk deleted the claude/issue-18831-claim-near-miss-forms branch September 18, 2026 06:04
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants