Commit 1bc22b3
fix(pm): every
Fixes #18456
Clause-②: no
`scripts/pm/` sits outside every workspace package, and the root package
is private, so no
`files[]` can ship this diff — `skip-changeset`.
## The defect
`check-clause2-carriers --pair` is the landing pre-check every seat
runs, and on one pair
(PR #17917 / card #17425) it answered **0 at 02:57Z, 4 at 03:04:09Z and
0 at 03:58:33Z on
2026-09-13 with an identical script blob**. Two explanations were ruled
out with controls
(no comment on that thread was ever edited; the board is resolved from
the environment, never
from the working directory), so the cause is still UNKNOWN — and the
three runs could not be
compared, because not one of them had SAID what it read. The judging
half is already
deterministic given a fixed document (`--pair-json` proves that); what
was unpinned is **what
document the live path builds**. This states it: every `--pair` run now
closes with a fenced
`clause2 input record` block on stderr, with the same field roster on
every exit, so two runs
that disagree are settled by **diffing their two blocks** — never by
re-running until one side
wins. ⛔ No guess at the cause is dressed as a fix here: no predicate, no
state, no row, no
count and no exit code reads one character of the record, and the
judging half is untouched.
## The record's field roster
Rendered from `INPUT_RECORD_RUN_FIELDS` and `INPUT_RECORD_PAIR_FIELDS`
and from nowhere else,
so a field cannot silently disappear: a declared field this run could
not fill renders with an
explicit token rather than vanishing, and a field the builder fills that
the roster does not
declare is NAMED in the block (`record.undeclared`). Values too long for
one line continue on
indented lines under their key.
| half | fields |
|:--|:--|
| run | `record.version` · `run.utc` · `run.mode` · `run.script.path` ·
`run.script.blob` · `run.script.bytes` · `run.node` · `board.repo` ·
`board.source` · `read.plan` · `read.api` · `read.token` · `read.served`
· `read.pair-json` · `run.requests` · `pairs.derived` |
| per pair (`pair.N.`) | `pr` · `card` · `derivation` · `head-sha` ·
`card-comments` · `card-comment-ids` · `card-comment-newest` ·
`pr-comments` · `pr-comment-ids` · `pr-comment-newest` · `claim.rule` ·
`claim.selected` · `claim.rejected` · `claim.clause2-line` ·
`pr-body.clause2-line` |
Four of them are worth naming for WHY they are there:
- **`run.requests`** — every read the run issued, in order, with its
channel, its exact path
and its **row count**. A page asked for with `per_page=100` that answers
with exactly 100
rows is the one shape a truncated read and a complete one share, and
nothing printed it.
- **`claim.rule` + `claim.selected` + `claim.rejected`** — the carrier,
the rule that picked it
and every candidate it did not pick, each with its reason. That
separates "the two runs
selected different comments" from "the two runs applied different
rules".
- **`claim.selected`'s body fingerprint** (bytes + `sha256:`) — the
field the measured 0/4/0
actually needs. A `misplaced` verdict on that thread requires the
governing claim to have
carried no readable declaration while a superseded one did; same ids
with a different verdict
is only possible if the BYTES differed, and the ids were all anybody
could see.
- **`run.script.blob`** — git's blob hash of this file, beside the path
it ran from. "The blob
was identical on both sides" was a claim in the incident; it is now a
printed fact any seat
checks with `git hash-object`. On this PR's head it reads
`25d204236aa8296644813109fa77541d6efe1644`,
which is exactly `git rev-parse
HEAD:scripts/pm/check-clause2-carriers.mjs`.
The `--json` sweep carries the same record under `inputs` — the same
record, ⛔ never a second
format.
## The two live blocks the card names
`--pair 17917` — the pair from the card. Both it and #18654 have since
merged, so `--pair`
answers **exit 2** on each today (the pair cannot be formed from a
closed PR). ⭐ That is
precisely the class of exit the old code said the least about, and the
block is now complete
on it:
```text
----- clause2 input record v1 -----
record.version: 1
run.utc: 2026-09-17T14:10:26.210Z
run.mode: --pair 17917
run.script.path: /home/user/objectstack-issue-18456/scripts/pm/check-clause2-carriers.mjs
run.script.blob: 25d2042 (git blob sha1 — check it with `git hash-object` on the path above)
run.script.bytes: 513169
run.node: v22.22.2
board.repo: objectstack-ai/objectstack
board.source: default — NEITHER PM_SWEEP_REPO NOR GITHUB_REPOSITORY answered
read.plan: (i) token then (ii) token-less public read
read.api: https://api.github.com (REST, accept application/vnd.github+json)
read.token: present
read.served: token=1, public=0, pair-json=0
read.pair-json: (not named — this run read the network)
run.requests: 1 read(s), in the order they were issued
#1 (i) token /repos/objectstack-ai/objectstack/pulls?state=open&per_page=100&page=1 -> HTTP 200 (21 row(s))
pairs.derived: 0 pair(s)
record.how-to-read: two runs that DISAGREE about one pair are settled by diffing their two blocks — ⛔ never by re-running until one side wins. The blob line says whether the two runs were even the same instrument.
----- end clause2 input record -----
```
`--pair 18654` — the pair this seat landed today, which answered 0 at
12:32Z and is likewise
merged now (**exit 2**):
```text
----- clause2 input record v1 -----
record.version: 1
run.utc: 2026-09-17T14:10:27.163Z
run.mode: --pair 18654
run.script.path: /home/user/objectstack-issue-18456/scripts/pm/check-clause2-carriers.mjs
run.script.blob: 25d2042 (git blob sha1 — check it with `git hash-object` on the path above)
run.script.bytes: 513169
run.node: v22.22.2
board.repo: objectstack-ai/objectstack
board.source: default — NEITHER PM_SWEEP_REPO NOR GITHUB_REPOSITORY answered
read.plan: (i) token then (ii) token-less public read
read.api: https://api.github.com (REST, accept application/vnd.github+json)
read.token: present
read.served: token=1, public=0, pair-json=0
read.pair-json: (not named — this run read the network)
run.requests: 1 read(s), in the order they were issued
#1 (i) token /repos/objectstack-ai/objectstack/pulls?state=open&per_page=100&page=1 -> HTTP 200 (21 row(s))
pairs.derived: 0 pair(s)
record.how-to-read: two runs that DISAGREE about one pair are settled by diffing their two blocks — ⛔ never by re-running until one side wins. The blob line says whether the two runs were even the same instrument.
----- end clause2 input record -----
```
⭐ `diff` of those two blocks is **four lines**: `run.utc` and
`run.mode`, twice. Same roster,
same order, same shape — which is the property the card asked for.
## A live block on exit 0
`--pair 18659` (open at the time of writing) — **exit 0**, the full pair
half:
```text
----- clause2 input record v1 -----
record.version: 1
run.utc: 2026-09-17T14:10:36.744Z
run.mode: --pair 18659
run.script.path: /home/user/objectstack-issue-18456/scripts/pm/check-clause2-carriers.mjs
run.script.blob: 25d2042 (git blob sha1 — check it with `git hash-object` on the path above)
run.script.bytes: 513169
run.node: v22.22.2
board.repo: objectstack-ai/objectstack
board.source: default — NEITHER PM_SWEEP_REPO NOR GITHUB_REPOSITORY answered
read.plan: (i) token then (ii) token-less public read
read.api: https://api.github.com (REST, accept application/vnd.github+json)
read.token: present
read.served: token=5, public=0, pair-json=0
read.pair-json: (not named — this run read the network)
run.requests: 5 read(s), in the order they were issued
#1 (i) token /repos/objectstack-ai/objectstack/pulls?state=open&per_page=100&page=1 -> HTTP 200 (21 row(s))
#2 (i) token /repos//issues/18443 -> HTTP 200
#3 (i) token /repos//issues/18443/comments?per_page=100 -> HTTP 200 (4 row(s))
#4 (i) token /repos/objectstack-ai/objectstack/pulls/18659/files?per_page=100&page=1 -> HTTP 200 (1 row(s))
#5 (i) token /repos//issues/18659/comments?per_page=100 -> HTTP 200 (1 row(s))
pairs.derived: 1 pair(s)
pair.1.pr: 18659
pair.1.card: 18443
pair.1.derivation: `closing-keyword` (via a closing keyword) — body line: Fixes #18443
pair.1.head-sha: 1344eb5
pair.1.card-comments: 4 row(s)
pair.1.card-comment-ids: 5713976124,5714587497,5714873191,5715029659
pair.1.card-comment-newest: 5715029659 at 2026-09-17T13:19:56Z
pair.1.pr-comments: 1 row(s)
pair.1.pr-comment-ids: 5715030051
pair.1.pr-comment-newest: 5715030051 at 2026-09-17T13:19:57Z
pair.1.claim.rule: the GOVERNING claim — the NEWEST comment whose body carries a line beginning `Claim:`/`Claimed:` AND whose `Branch:` line parses at least one protocol-shaped branch (newest by `created_at`; an unreadable stamp or a tie falls back to thread order, later row wins). The pool is every claim comment sharing that `created_at`; when NO claim names a branch at all, every claim comment is the pool. ⛔ Not earliest, ⛔ not a session match, ⛔ not the one whose body mentions the key.
pair.1.claim.selected: 1 comment(s) in the pool
5714587497 at 2026-09-17T12:46:45Z — 2159 bytes, sha256:795e1df6c9fd
pair.1.claim.rejected: none — every claim comment on this thread is in the pool
pair.1.claim.clause2-line: DECLARED `no` — Clause-②: no
pair.1.pr-body.clause2-line: DECLARED `no` — Clause-②: no --pair run states the inputs it judged from (#18681)1 parent 0111212 commit 1bc22b3
1 file changed
Lines changed: 755 additions & 28 deletions
0 commit comments