Skip to content

fix(pm): read the ownership markers through the undecorated line, and make a near miss audible - #18756

Merged
os-justin merged 3 commits into
mainfrom
claude/issue-18680-decorated-claim-release-markers
Sep 17, 2026
Merged

os-justin merged 3 commits into
mainfrom
claude/issue-18680-decorated-claim-release-markers

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #18680

Clause-②: no

The defect

CLAIM_COMMENT_MARKER and RELEASE_COMMENT_MARKER anchor the bare word at line start and tolerate only a leading blockquote, so a line written in the decorated spelling a seat uses when it bolds the directive — **Release:** … — begins with an asterisk and the record reads as ABSENT. Nothing goes red; the ownership rows (H2, H47, H66, H67) simply read a different history than the thread carries, which is the silent direction. #10102 made exactly this judgement for the other directive family (Blocked-by: / Restart-when:) and never for these two markers; PR #18678 landed H67 declaring the loss on its own row rather than widening, because widening moves three landed rows' populations and is its own card. This is that card.

Before-readings (reproduced offline, on the fetched specimen)

The live specimen is objectstack#16529 comment 5691473966 (os-try-charles, 2026-09-16T03:11:44Z), fetched through the REST proxy and never retyped. ⚠️ One correction to the card's prose: the **Release:** line is the 45th line of that comment, not its first — both markers are m-flagged and scan every line, so line position was never what hid the record. The asterisks were.

probe RELEASE_COMMENT_MARKER (bare)
the specimen's whole comment body false
the specimen's release line alone false
the SAME line with **Release:** replaced by Release: (control) true
**Claim:** … / `Release:` … / __Release:__ … / ## Release: … / - Release: … all false

undecorateProseLine — the #10102 function — was measured rather than assumed: its body is a single replace over a character class holding exactly a backtick and an asterisk. It removes backticks and asterisks and nothing else — not underscores, not a heading hash. That measurement is what decides which spellings this repair reaches and which become near misses.

Consumers of the two exported constants, all found by grep across scripts/: nine call sites inside check-half-states.mjs itself (H2, governingClaim, latestClaimComment, H34's guard, h44ArtefactShape, h46ClaimNamesBranch, latestMarkedComment — which is H47's, H49's, H50's, H53's and H67's shared resolver — SEAT_SIGNATURE_FORMS, and h66ReleaseVerdict), each fed a raw comment body or one raw line; and one cross-file importer, scripts/pm/check-clause2-carriers.mjs, which feeds CLAIM_COMMENT_MARKER a raw comment body. That importer is outside this card's file surface and is deliberately unaffected: the constants keep their bare semantics.

The ONE place decoration is handled

markerMatches(marker, text), declared beside the two markers. It tries the bare reading FIRST and short-circuits, then re-tests against the same undecorateProseLine the Blocked-by: family uses. All nine in-file call sites now go through it; ⛔ no regex was widened, ⛔ no second stripper exists, and the two constants still describe the bare directive (the cases that pin them still assert on them directly, and they stay green).

Two properties fall out:

  • Strictly additive, by construction rather than by inspection. The bare test short-circuits, so ⛔ no body that read before can stop reading. Measured on 770 live comments below: 0 regressions.
  • It refuses to undecorate through a markdown LIST ITEM. The shared stripper takes every asterisk, so a * Claim: bullet would become a directive while H20's pinned - Claim: stays refused. A list marker is followed by whitespace and a decoration is not; that is the whole discriminator, and it is pinned both ways.

The near-miss vocabulary — a line that looks like a marker makes a sound

Widening alone leaves the same silence one decoration further out, which is the triage's second half (comment 5716952460). OWNERSHIP_MARKER_NEAR_MISS_FORMS is a frozen, named roster in the register of #18560's SCHEMA_PROPERTY_FORMS; each member carries its own example fixture, and the roster is asserted EQUAL to a frozen list of ids, so a form added without a fixture reds and a form silently dropped reds.

id what example
heading the directive written as a markdown heading ## Release: …
list-item the directive written as a markdown list item - Release: …
underscore-emphasis emphasised with underscores, which the shared stripper does not remove __Release:__ …
inflected-word a spelling the marker's vocabulary does not carry Released: …
separator the canonical word with a separator that is not the canonical colon Release — …

ownershipMarkerNearMisses(commentRows) is the reader; it buys nothing (the sweep hands it threads other rows already paid for), files no finding and proposes no state. It reports on an unconditional summary clause (Ownership-marker near misses: …) naming the card, the comment id and the offending prefix, capped at five named entries with the remainder counted. A line the reading DOES read is ⛔ never a near miss — the two are complements by construction, so a future widening shrinks this census automatically.

H67's declared loss is retired in the same edit, on its row and in its summary clause: both said the decorated line was invisible, and that is no longer true.

Pins

New battery H2/H47/H66 decorated ownership marker, 98 cases, pinned at 94. The roster floor rose 4 to 5.

  • the fetched A consuming repo cannot ask "was this dist built from the tree I pin?" — the content stamp already exists, covers only the AMPLIFIERS list, and is not readable across the repo boundary #16529 specimen: refused by the bare marker, READ through markerMatches; the derived bare spelling of the same line matches both ways (control); a release is still not a claim; the record reads from any line of the body
  • the rows: H2 goes clean on a decorated claim (with the no-claim control still firing); latestMarkedComment locates a decorated release; H66 reads it on the canonical leg with destination pm:queue, quoted undecorated
  • what the stripper measures: backticked and bold-italic directives read; __Release:__ does NOT, and is a near miss instead
  • the firing controls of a widening, inside the same battery: seven bare spellings still read, prose containing the word still does not, Released: is still MALFORMED, the fullwidth colon still does not match (the 2026-08-11 ruling is untouched), a dash-written claim is still H34's row
  • the bullet guard: an asterisk bullet, a hyphen bullet, an ordered 1. marker and a blockquoted bullet all refused; the whitespace discriminator pinned as a pair
  • the vocabulary: every member driven against its own fixture — not read by either marker, reports naming its own form, with the comment id and the offending prefix — plus the counterfactual roster-equality pin, frozen-ness, distinct ids, and ⛔ no g and ⛔ no m flag
  • the summary clause: counts, named entries, the cap clause, unconditional rendering, render order, the forwarding contract, and ⛔ no undefined
  • the PR pm(half-states): H67 reads the queue for cards a PR already landed on #18678 pin, EDITED and not deleted: H67 ⚠️ loss: a DECORATED **Release:** line does not stand the row down becomes H67 ⚠️ loss CLOSED: … now STANDS THE ROW DOWN, as the bare one always did, and its companion flips from "the row DECLARES that blind spot" to "the row no longer DECLARES a blind spot it no longer has". Its two control cases are untouched and still green, because they assert on the marker CONSTANT — which this PR does not change.

Self-test: 4782 cases / 4 batteries becomes 4881 cases / 5 batteries (98 battery cases, plus one case the per-anchor summary-clause coverage loop registers for the new clause automatically).

Ablation

Revert the one call that routes the markers through the undecorated line (delete the undecorated leg of markerMatches, leaving the bare test alone), from the committed state, with an EXIT/INT/TERM trap restoring by absolute path.

Live-board delta — report-only, ⛔ no state write of any kind

Two full sweeps, node scripts/pm/check-half-states.mjs against objectstack-ai/objectstack: BEFORE on a detached worktree at 62bce5c29 (17:53Z to 18:01Z), AFTER on this branch (18:01Z to 18:09Z).

H2 / H47 / H66 verdicts: identical. H2 fired on #13597 and #15638 in both; H47 and H66 listed nothing in either. Eight rows differ between the two runs (H14 #18617, H38 #7623, H52 #18617 dropped; H1 #18709, H19 #18734, H36 #18414/#18720/#18741 appeared) and every one is board churn in the eight minutes between them — none reads an ownership marker.

#16529 specifically still lists on H67 in both, and the reason has nothing to do with the marker: its newest merge is now PR #18678 (merged 2026-09-17), which is NEWER than the 2026-09-16 release record, so "nobody has looked since the delivery landed" is a correct reading. Its row text did change — the declared loss is gone.

Because a sweep only judges threads it bought, the zero above understates the reading. So the same question was asked directly, over the 286 open pm:queue / pm:dispatched cards and their 770 comments:

And the counterfactual the rows themselves cannot show, offering the SAME live thread to both readings — 3 of 6 cards change:

card H47 H66
#16529 FIRES becomes clean (the release now answers the claim) none becomes pm:queue
#17852 FIRES becomes clean none becomes the maintainer
#15468 clean becomes FIRES (a bolded claim nobody has answered) unchanged
#14026, #16233, #18143 unchanged unchanged

⛔ Nothing was written to any card, PR or label from either sweep, and ⛔ no verdict here is a proposal about any of those cards.

Gates

Derived from this worktree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (⛔ no hand-fed path list; change set: scripts/pm/check-half-states.mjs, one path). 38 families, every one run, exit code captured by redirect-then-$? before any pipe, reconciled with --ran.

37 of 38 exited 0, with the two qualifications named below. Notably: check:pm-half-states 0 · check:nul-bytes 0 · check:closing-target-claim 0 · check:commit-card-trailers 0 · check:self-test-wired 0 · check:scripts-symbol-anchors 0 · check:declaration-mirrors 0 · check:whole-set-label-write 0 · check:changeset-no-major 0 · check:pm-governed-queue-guard 0 · check:cross-package-test-inputs 0 · check:parse-guard 0.

⚠️ pnpm check:nul-bytes exited 1 on the first pass and was right to: an editing tool had materialised a backslash-u-0001 escape (written out in words here for the same reason) into a real 0x01 byte in the near-miss dedupe key — the exact slip that gate exists for. Fixed by writing the escape text (byte-identical at runtime), re-run green, and the rule's own grep -naP control-byte self-scan over the file returns nothing.

⚠️ pnpm check:pm-dispatch-gates is the one family whose self-test runs longer than this container's foreground ceiling: a first attempt reached 1768 green cases and was killed by the timeout wrapper at 560s (exit 124 = no verdict reached, which is NOT MEASURED and ⛔ not a red). It was re-run detached; its verdict is reported in this card's os-dev-report comment rather than guessed here. ⛔ Its diff-relevant half is unaffected either way — this PR touches neither dispatch-gates.mjs nor its fixtures.

Repo-wide pnpm lint (eslint . --no-inline-config): exit 0, as PR #18654 did.

Not in scope, read and left alone

Acceptance notes

  • H66's summary clause still carries the dated reading 「Measured 2026-09-16 over 29 threads on two boards, the canonical Release: line appeared ZERO times」. It names its date and its boards, so it stays true as written, but it was taken with the bare reader and this PR changes what a re-measure would find. Noted, not filed — the sentence is a dated measurement, not a live claim. Who would meet it: the next author of H66's buy-order or clause.
  • check-clause2-carriers.mjs reads CLAIM_COMMENT_MARKER against a raw comment body and therefore still cannot see a decorated claim. That is correct for this card's file surface (the constant is unchanged) and is a reading about that file, not a defect in this one. Noted, not filed; the seat decides whether that gate wants the same reading. Who would meet it: whoever next touches that gate's claim leg.

Generated by Claude Code

… make a near miss audible

`CLAIM_COMMENT_MARKER` and `RELEASE_COMMENT_MARKER` anchor the bare word at
line start and tolerate only a leading blockquote, so a seat that BOLDS the
directive — `**Release:** …` — writes a record no reader in this file could
see. The failure is the silent one: nothing goes red, the ownership rows just
read a different history than the thread carries.

Every site that asks whether a comment IS a `Claim:` or a `Release:` now asks
`markerMatches`, which tries the bare reading first and then the same
`undecorateProseLine` the `Blocked-by:` / `Restart-when:` family has used since
#10102. The two constants keep their bare semantics — they are the protocol's
spelling, a sibling gate imports one of them, and the cases that pin them still
assert on them directly. Decoration is handled in ONE place, and the reading is
a strict superset by construction: the bare test short-circuits, so no body that
read before can stop reading.

Undecorating is refused through a markdown LIST ITEM: the shared stripper takes
every `*`, so a `* Claim:` bullet would otherwise become a directive while H20's
pinned `- Claim:` stays refused. A list marker is followed by whitespace and a
decoration is not, which is the whole discriminator.

Widening alone would leave the same silence one decoration further out, so
`OWNERSHIP_MARKER_NEAR_MISS_FORMS` names five refused spellings, the sweep
censuses them off the threads other rows already bought, and an unconditional
summary clause names the card, the comment id and the offending prefix. A future
decoration is ADDED to that list rather than rediscovered from a silent row.

H67's declared loss is retired in the same edit — its row and its summary clause
said the decorated line was invisible, and that is no longer true.

Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Co-authored-by: Claude <noreply@anthropic.com>
… the byte

An editing tool materialised the escape into a real 0x01 while the key was
written — the slip `check-nul-bytes` exists for. Byte-identical at runtime.

Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Co-authored-by: Claude <noreply@anthropic.com>
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@os-justin
os-justin marked this pull request as ready for review September 17, 2026 19:22
@os-justin
os-justin added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 95e1745 Sep 17, 2026
39 checks passed
@os-justin
os-justin deleted the claude/issue-18680-decorated-claim-release-markers branch September 17, 2026 19:45
This was referenced Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants