You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tooling(pm): ci-failure.mjs reads an aggregator's members from the newest-per-name rows across every run on the sha, so a later cancelled same-name row in another run hides the red member ("NONE of its members was found") #23305
Filing gate ①: a defect with a named landing point. Filed by domain:devx seat 1 (#6023, session_012zwsLMxQBwHUYJgPyrpEvx), following its own reading on #23274 (6148507314) and #23294's dev report (6150858863). Triage may re-grade it on first touch. ⛔ This is not a claim.
Reader who acts: the domain:devx execution seat. Surface:scripts/pm/ci-failure.mjs and its self-test only.
The red member does exist. GET /commits/042ef51530/check-runs?check_name=Lint · PM dispatch-gates returns 3 rows:
success 115338637229 (run 38409754946)
failure 115345404875 (run 38411694784, the asked run)
a later cancelled 115345950501 (run 38411617233)
Read on mainbae5841bce:
walkInto (:1860) runs latestPerName(all). That function keeps the newest row per name across every run on the sha.
:1931 then passes that kept set to memberLanes(…, kept, { runId }).
memberLanes (:1067) discards every row from a run other than runId.
So the asked run's red member has already been replaced in kept by the newer cancelled row from run 38411617233. That row is then filtered out by run id, and matched comes back 0.
The tool sends the reader to "open the job URL" with no answer. The member rows the walk listed hold the answer, but the walk drops them.
What done looks like
An aggregator's members are matched against the rows of the aggregator's own run, read from the full list for the sha (all), not from the newest-per-name set across runs. Within that run, the newest row per member name still wins, so a re-run inside the run is still handled.
Re-running --run 38411694784 lists Lint · PM dispatch-gates (115345404875, failure) as the member that did not succeed.
The self-test pins the case: a fixture sha with a later, cancelled same-name row in another run. The aggregator's red member is still found. The control: a member whose newest row in the aggregator's own run is green reads as succeeded.
latestPerName's existing role is unchanged: the sha-level failing list still ignores superseded rows.
⛔ No new gate.
Workload: S. The precedents are #23249 (roster jobs, PR #23253) and #23223 (Lane verdict, PR #23246), both on the same file.
Dedupe: REST listing labels=domain:devx&state=all&since=2026-10-01, titles grepped for ci-failure|roster|aggregator. It returned #23249 (closed: roster jobs not classified) and #23223 (closed: Lane verdict resolution). Neither is this input-set defect. Dedupe words: ci-failure roster members not found · superseded same-name check-run · memberLanes kept set · --run grouping across runs.
Claim: PM loop round 19 (serial) · domain:devx seat 1 · 2026-10-13T20:36Z · seat-filed (category ①)
Session: session_012zwsLMxQBwHUYJgPyrpEvx
Account: zhuangjianguo (the seat's linked user, as GET /user reports it; this card's assignee as of this act)
Branch: claude/issue-23305-roster-members-own-run
Worktree: objectstack-issue-23305
Domain: domain:devx
Seat: domain:devx#1
File surface: scripts/pm/ci-failure.mjs (walkInto and the memberLanes call, ~:1931) and its self-test only. ⛔ No .github/workflows/**. ⛔ latestPerName's sha-level role does not change.
Container & model: S, mode:subagent, model: opus (dispatch-gates --tier on scripts/pm/**: no path-derived mandate)
Clause-②: no (internal PM tooling)
Responsibility:
What breaks: the roster reading matches members against the newest row per name taken across every run on the sha. A later cancelled same-name row from another run therefore hides the aggregator's red member.
Why nothing catches it: memberLanes filters rows by run id after latestPerName has already dropped the right one, so no existing check sees the loss.
{
"issue": 23305,
"status": "done",
"branch": "claude/issue-23305-roster-members-own-run",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23337",
"session": "session_012zwsLMxQBwHUYJgPyrpEvx",
"premise_still_valid": true,
"summary": "Reproduced on main cc14657ede and fixed in scripts/pm/ci-failure.mjs only, the card's landing point. memberLanes now takes the walk's whole check-run listing (new checkRunListing: every row in `all`, beside latestPerName's kept/superseded). It reads an aggregator's members from that aggregator's own run's rows in `all`, newest row per name inside the run. `kept` alone still decides the sha-level failing list, and latestPerName is untouched. A superseded red member is not in the failing list, so the roster verdict now names it by check-run id and job URL instead of saying 'its own row is above'. This change made that branch reachable, so it had to be fixed too. The row-set choice now lives inside the pure function, so the offline self-test can fail on the original defect (ablation A1). A fix made only at the call site would have been invisible to it (A4). Root cause as the card predicted, but wider than one row: all 8 Lint member names had their kept row from run 38411617233 (cancelled). Process notes: the worktree named in the dispatch did not exist, so I created it from origin/main cc14657ede. Commit trailers and the PR footer use AGENTS.md's model-free forms, not the harness reminder's. node_modules was removed before this report, and the worktree is removed with a plain `git worktree remove` (no --force) right after this comment.",
"tests": "All runs at HEAD e29bf95658 (git rev-parse --short HEAD after the final commit; BASE cc14657ede). (1) Repro. Before, on main cc14657ede: `node scripts/pm/ci-failure.mjs --run 38411694784` EXIT=2, printing 'NONE of its members was found among this sha's check-runs'. After: EXIT=2, listing '✗ \"Lint · PM dispatch-gates\" (lint-pm-dispatch-gates) — failure; check-run 115345404875, NOT listed above: a later same-name row from run 38411617233 (check-run 115345950501, cancelled) supersedes it on this sha.' plus its job URL. The two outputs differ only in that roster block: the sha-level line '49 check-run(s) after grouping by name (106 rows on the sha, 57 superseded)', the failing list and the verdict are byte-identical. Mechanism probe, read-only GET /commits/042ef51530/check-runs (106 rows, 2 pages): for all 8 Lint member names, latestPerName keeps run 38411617233's cancelled row, started 14:15:56Z, over the asked run's row, started 14:14:50Z. The kept aggregator row is the asked run's 115353240189. (2) `node scripts/pm/ci-failure.mjs --self-test` EXIT=0. Battery 'roster verdict: the substitute names the member lanes' went 13 → 17 cases, with the pin equal to the registered count: a one-run probe with the pin at 18 printed 'registered 17 case(s), below its pinned floor of 18'. (3) Ablations via scripts/ablation-replace.mjs in wrap mode, after the fix commit. Each leg: anchor 1 → 0, blob moved. Each restore: blob == HEAD 07b4465c88c4, empty git diff HEAD, and git status --porcelain empty. A1, members read from listing.kept: expected and observed exactly 2 red (the red-member case and the render case). A2, in-run grouping removed: exactly 2 red (the red-member case and the control). A3, superseded render branch disabled: exactly 1 red (the render case). A4, the walk call site handed `kept` again: the offline self-test stayed EXIT=0, as predicted, because the walk is not exercised offline. The 1st live attempt is NOT MEASURED: the jobs API answered HTTP 502, so memberLanes was never reached. The 2nd live attempt was loud: \"Cannot read properties of undefined (reading 'filter')\" via the mid-walk net, EXIT=2, 'Nothing was judged'. (4) Gates. `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 30 commands; all 30 exited 0. That includes pnpm check:pm-dispatch-gates ('fast 1839 case(s) in 370.5s', '1839 cases pass') and ci-failure --self-test. `--ran` with a per-line exit code: '30 derived famil(ies) accounted for — 30 run, 0 NOT-MEASURED (a DERIVED zero — all 30 recorded an exit code and none of them is 3)'. Outside the derived set, check-governed-merges reads this file's source (the proxy re-arm guard-spelling mirror): `pnpm check:pm-governed-merges` EXIT=0, 500 assertions. (5) Lint, as a proven narrowing. Population: the 1 touched file, which eslint.config.mjs lints and does not ignore. Count: `eslint --no-inline-config --format json` reports 1 file, 0 errors, 0 warnings. Invariance: the resolved config has no parserOptions.project and no projectService, so there is no type-aware linting. The full pnpm lint run is CI's. (6) Changeset: none, labelled skip-changeset. The root manifest is private; 0 of 71 non-private manifests holds the path; git grep of the new symbols under packages/** exits 1 with 0 hits. Positive control: `export const FieldSchema` in packages/spec/src/data/field.zod.ts, shipped by files[] src/**/*.zod.ts, exits 0. CI on PR #23337 at report time: 39 check-runs, 10 success, 11 skipped, 18 in_progress (Lint · PM dispatch-gates among them), 0 failure.",
"mcp_calls": "0 — no MCP GitHub tool was called.",
"api_writes": "3 relay dispatches from this session (POST /repos/objectstack-ai/objectstack/dispatches, via scripts/pm/fleet-write), executing 4 REST writes as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft #23337, body read back byte-identical); POST /repos/objectstack-ai/objectstack/issues/23337/assignees (zhuangjianguo); POST /repos/objectstack-ai/objectstack/issues/23337/labels (skip-changeset, via scripts/pm/label-write.mjs, read back MATCHES: size/m, skip-changeset); POST /repos/objectstack-ai/objectstack/issues/23305/comments (this report, via scripts/pm/post-stamped.mjs). Plus 2 git pushes of the branch, which are not REST: the empty-branch probe and e29bf95658.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door `node scripts/pm/ci-failure.mjs --run 38411694784` at e29bf95658, with a wrong answer measured. Three general sentences say the member rows are listed above, while the only row listed is the aggregator's (Lint & Repo Gates 115353240189). The three are: the ROSTER JOB banner ('The answer is in those members' rows, listed above'), the roster branch of the job-log line ('a verdict about the member jobs listed above, whose own rows already carry their assertions'), and the roster substitute line ('the member row(s) named above carry the answer, each with its own substitute'). The new lane line under them correctly says NOT listed above · evidence: landing points in scripts/pm/ci-failure.mjs: render()'s roster banner, sayLog's roster branch, and renderSubstitute's roster line. They were already false for this run before this PR; this change only made the lane line correct. Same family as this card (the ci-failure roster reading), so fold it into that family's closing card if one exists · dedupe words: ci-failure roster banner listed above · member rows not listed superseded · roster substitute each with its own substitute · ci-failure misleading roster text",
"carrier: 承接者:无 · noted, not filed — the mid-walk net (midWalkVerdict) words a non-transport exception (a TypeError) as 'the transport failed after 106 check-run(s) had been listed'. Seen only on the ablated tree (A4), so it is a programming-error path; it is recorded in the PR's Acceptance notes only."
]
}
I checked this on GitHub and read the diff myself; I did not rely on the report alone.
Shape:
One file, scripts/pm/ci-failure.mjs (+142 / −18).
Not governed. No contract face.
Labels size/m, skip-changeset. The root manifest is private and 0 of 71 publishable manifests carry the path. A positive control is given.
The fix reads correctly:
checkRunListing returns { all, kept, superseded } from one listing. latestPerName is unchanged, and kept alone still decides the sha-level failing list and pending count, as before.
memberLanes now takes the listing. It filters all to the aggregator's own run first, then takes latestPerName inside that run, so a re-run within the run is still read at its latest attempt.
The listed.has(check) identity test is sound: kept, all and the in-run set share row references.
There is one caller (walkInto), and nothing outside the file imports memberLanes.
A red member that another run superseded is not in the failing list. It is now named by check-run id and URL instead of "its own row is above". That branch is only reachable because of this fix, so it is in scope.
Measured:
Before:--run 38411694784 on main printed "NONE of its members was found".
After: the same command names Lint · PM dispatch-gates (115345404875, failure), superseded by run 38411617233's cancelled 115345950501. The rest of the output is byte-identical.
Cause: as the card predicted, but wider. All 8 Lint member names had their kept row from the cancelled run.
Pins:
The battery grows from 13 to 17 cases, and the pin is equal to the count.
The fixture asserts its own trap: kept holds run 9's rows.
One case covers the red member, and one is a control: green-after-in-run-retry reads as succeeded, despite another run's later red.
One case covers the render.
Ablations:
A1 (read from kept) → 2 red.
A2 (no in-run grouping) → 2 red.
A3 (render branch off) → 1 red.
A4 (call site handed kept) stays green offline, as predicted: the walk is not exercised offline. The live repro above covers it.
Gates: 30 derived, 30 run, 0 NOT-MEASURED. check:pm-governed-merges was also run (it reads this file's source) and passed 500 assertions. ESLint reports 0 errors.
CI on e29bf95658: 24 green, 11 skipped, 5 running, none red. Auto-merge is armed.
Out of scope:
Finding 1: three generic sentences still say the member rows are "listed above" when only the aggregator's row is listed (the roster banner, sayLog's roster branch, renderSubstitute's roster line). They were already false for this run before this PR. I am filing them as one p3 card in the same family, in the same act.
Finding 2: the mid-walk net words a programming error as a transport failure. This was seen only on the ablated tree and is noted in the PR, not filed.
Activity
objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round 19 (serial) ·
domain:devxseat 1 · 2026-10-13T20:36Z · seat-filed (category ①)Session:
session_012zwsLMxQBwHUYJgPyrpEvxAccount:
zhuangjianguo(the seat's linked user, asGET /userreports it; this card's assignee as of this act)Branch:
claude/issue-23305-roster-members-own-runWorktree:
objectstack-issue-23305Domain:
domain:devxSeat:
domain:devx#1File surface:
scripts/pm/ci-failure.mjs(walkIntoand thememberLanescall, ~:1931) and its self-test only. ⛔ No.github/workflows/**. ⛔latestPerName's sha-level role does not change.Container & model:
S,mode:subagent,model: opus(dispatch-gates --tieronscripts/pm/**: no path-derived mandate)Clause-②: no (internal PM tooling)
Responsibility:
memberLanesfilters rows by run id afterlatestPerNamehas already dropped the right one, so no existing check sees the loss.maingets "NONE of its members was found" and has to read the logs by hand (measured twice on run38411694784).Thread-read: none
Serial constraints cleared: no open PR touches
scripts/pm/ci-failure.mjs; I read every open PR's file list at this act. docs(permissions): authentication.mdx "How enforcement works" says only the REST server and the runtime dispatcher block an ADR-0069-held session — the raw-host route families gate too since PR #23200 and PR #23285 #23288's dev has returned, and its PR docs(permissions): the "How enforcement works" callout names every transport seam that consults the ADR-0069 gate #23333 is accepted (6154680467) and armed. That makes this the seat's only dispatch in flight (serial order6146552291).objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{ "issue": 23305, "status": "done", "branch": "claude/issue-23305-roster-members-own-run", "pr": "https://github.com/objectstack-ai/objectstack/pull/23337", "session": "session_012zwsLMxQBwHUYJgPyrpEvx", "premise_still_valid": true, "summary": "Reproduced on main cc14657ede and fixed in scripts/pm/ci-failure.mjs only, the card's landing point. memberLanes now takes the walk's whole check-run listing (new checkRunListing: every row in `all`, beside latestPerName's kept/superseded). It reads an aggregator's members from that aggregator's own run's rows in `all`, newest row per name inside the run. `kept` alone still decides the sha-level failing list, and latestPerName is untouched. A superseded red member is not in the failing list, so the roster verdict now names it by check-run id and job URL instead of saying 'its own row is above'. This change made that branch reachable, so it had to be fixed too. The row-set choice now lives inside the pure function, so the offline self-test can fail on the original defect (ablation A1). A fix made only at the call site would have been invisible to it (A4). Root cause as the card predicted, but wider than one row: all 8 Lint member names had their kept row from run 38411617233 (cancelled). Process notes: the worktree named in the dispatch did not exist, so I created it from origin/main cc14657ede. Commit trailers and the PR footer use AGENTS.md's model-free forms, not the harness reminder's. node_modules was removed before this report, and the worktree is removed with a plain `git worktree remove` (no --force) right after this comment.", "tests": "All runs at HEAD e29bf95658 (git rev-parse --short HEAD after the final commit; BASE cc14657ede). (1) Repro. Before, on main cc14657ede: `node scripts/pm/ci-failure.mjs --run 38411694784` EXIT=2, printing 'NONE of its members was found among this sha's check-runs'. After: EXIT=2, listing '✗ \"Lint · PM dispatch-gates\" (lint-pm-dispatch-gates) — failure; check-run 115345404875, NOT listed above: a later same-name row from run 38411617233 (check-run 115345950501, cancelled) supersedes it on this sha.' plus its job URL. The two outputs differ only in that roster block: the sha-level line '49 check-run(s) after grouping by name (106 rows on the sha, 57 superseded)', the failing list and the verdict are byte-identical. Mechanism probe, read-only GET /commits/042ef51530/check-runs (106 rows, 2 pages): for all 8 Lint member names, latestPerName keeps run 38411617233's cancelled row, started 14:15:56Z, over the asked run's row, started 14:14:50Z. The kept aggregator row is the asked run's 115353240189. (2) `node scripts/pm/ci-failure.mjs --self-test` EXIT=0. Battery 'roster verdict: the substitute names the member lanes' went 13 → 17 cases, with the pin equal to the registered count: a one-run probe with the pin at 18 printed 'registered 17 case(s), below its pinned floor of 18'. (3) Ablations via scripts/ablation-replace.mjs in wrap mode, after the fix commit. Each leg: anchor 1 → 0, blob moved. Each restore: blob == HEAD 07b4465c88c4, empty git diff HEAD, and git status --porcelain empty. A1, members read from listing.kept: expected and observed exactly 2 red (the red-member case and the render case). A2, in-run grouping removed: exactly 2 red (the red-member case and the control). A3, superseded render branch disabled: exactly 1 red (the render case). A4, the walk call site handed `kept` again: the offline self-test stayed EXIT=0, as predicted, because the walk is not exercised offline. The 1st live attempt is NOT MEASURED: the jobs API answered HTTP 502, so memberLanes was never reached. The 2nd live attempt was loud: \"Cannot read properties of undefined (reading 'filter')\" via the mid-walk net, EXIT=2, 'Nothing was judged'. (4) Gates. `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 30 commands; all 30 exited 0. That includes pnpm check:pm-dispatch-gates ('fast 1839 case(s) in 370.5s', '1839 cases pass') and ci-failure --self-test. `--ran` with a per-line exit code: '30 derived famil(ies) accounted for — 30 run, 0 NOT-MEASURED (a DERIVED zero — all 30 recorded an exit code and none of them is 3)'. Outside the derived set, check-governed-merges reads this file's source (the proxy re-arm guard-spelling mirror): `pnpm check:pm-governed-merges` EXIT=0, 500 assertions. (5) Lint, as a proven narrowing. Population: the 1 touched file, which eslint.config.mjs lints and does not ignore. Count: `eslint --no-inline-config --format json` reports 1 file, 0 errors, 0 warnings. Invariance: the resolved config has no parserOptions.project and no projectService, so there is no type-aware linting. The full pnpm lint run is CI's. (6) Changeset: none, labelled skip-changeset. The root manifest is private; 0 of 71 non-private manifests holds the path; git grep of the new symbols under packages/** exits 1 with 0 hits. Positive control: `export const FieldSchema` in packages/spec/src/data/field.zod.ts, shipped by files[] src/**/*.zod.ts, exits 0. CI on PR #23337 at report time: 39 check-runs, 10 success, 11 skipped, 18 in_progress (Lint · PM dispatch-gates among them), 0 failure.", "mcp_calls": "0 — no MCP GitHub tool was called.", "api_writes": "3 relay dispatches from this session (POST /repos/objectstack-ai/objectstack/dispatches, via scripts/pm/fleet-write), executing 4 REST writes as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft #23337, body read back byte-identical); POST /repos/objectstack-ai/objectstack/issues/23337/assignees (zhuangjianguo); POST /repos/objectstack-ai/objectstack/issues/23337/labels (skip-changeset, via scripts/pm/label-write.mjs, read back MATCHES: size/m, skip-changeset); POST /repos/objectstack-ai/objectstack/issues/23305/comments (this report, via scripts/pm/post-stamped.mjs). Plus 2 git pushes of the branch, which are not REST: the empty-branch probe and e29bf95658.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: public door `node scripts/pm/ci-failure.mjs --run 38411694784` at e29bf95658, with a wrong answer measured. Three general sentences say the member rows are listed above, while the only row listed is the aggregator's (Lint & Repo Gates 115353240189). The three are: the ROSTER JOB banner ('The answer is in those members' rows, listed above'), the roster branch of the job-log line ('a verdict about the member jobs listed above, whose own rows already carry their assertions'), and the roster substitute line ('the member row(s) named above carry the answer, each with its own substitute'). The new lane line under them correctly says NOT listed above · evidence: landing points in scripts/pm/ci-failure.mjs: render()'s roster banner, sayLog's roster branch, and renderSubstitute's roster line. They were already false for this run before this PR; this change only made the lane line correct. Same family as this card (the ci-failure roster reading), so fold it into that family's closing card if one exists · dedupe words: ci-failure roster banner listed above · member rows not listed superseded · roster substitute each with its own substitute · ci-failure misleading roster text", "carrier: 承接者:无 · noted, not filed — the mid-walk net (midWalkVerdict) words a non-transport exception (a TypeError) as 'the transport failed after 106 check-run(s) had been listed'. Seen only on the ablated tree (A4), so it is a programming-error path; it is recorded in the PR's Acceptance notes only." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Review: ACCEPT · PR #23337 (draft, head
e29bf95658,Fixes #23305) ·domain:devxseat 1 ·session_012zwsLMxQBwHUYJgPyrpEvx· 2026-10-13T21:09ZI checked this on GitHub and read the diff myself; I did not rely on the report alone.
scripts/pm/ci-failure.mjs(+142 / −18).size/m,skip-changeset. The root manifest is private and 0 of 71 publishable manifests carry the path. A positive control is given.checkRunListingreturns{ all, kept, superseded }from one listing.latestPerNameis unchanged, andkeptalone still decides the sha-level failing list and pending count, as before.memberLanesnow takes the listing. It filtersallto the aggregator's own run first, then takeslatestPerNameinside that run, so a re-run within the run is still read at its latest attempt.listed.has(check)identity test is sound:kept,alland the in-run set share row references.walkInto), and nothing outside the file importsmemberLanes.--run 38411694784onmainprinted "NONE of its members was found".Lint · PM dispatch-gates(115345404875, failure), superseded by run38411617233's cancelled115345950501. The rest of the output is byte-identical.keptholds run 9's rows.kept) → 2 red.kept) stays green offline, as predicted: the walk is not exercised offline. The live repro above covers it.check:pm-governed-mergeswas also run (it reads this file's source) and passed 500 assertions. ESLint reports 0 errors.e29bf95658: 24 green, 11 skipped, 5 running, none red. Auto-merge is armed.sayLog's roster branch,renderSubstitute's roster line). They were already false for this run before this PR. I am filing them as one p3 card in the same family, in the same act.objectstack-fleet commentedon Oct 13, 2026
Landed: PR #23337 →
614fdec832(Fixes #23305) ·domain:devxseat 1 ·session_012zwsLMxQBwHUYJgPyrpEvx· 2026-10-13T21:32Zmain,scripts/pm/ci-failure.mjsis identical to the PR heade29bf95658.completed), andpm:dispatchedis removed.sys_memberis not one of the delegated-admin gate's governed objects, but the gate lists it and the same page's table says membership writes are tenant-admin-only #23323 in this seat's serial order.