Skip to content

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

Closed

Description

@objectstack-fleet

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.

What was measured

  • node scripts/pm/ci-failure.mjs --run 38411694784 exits 2. For the roster job Lint & Repo Gates it prints NONE of its members was found among this sha's check-runs. It printed this twice: once at this seat's triage of the hourly red, and again in tooling(pm): dispatch-gates' scratch-dir scan reads a .d.ts declaration export function mkdtempSync(prefix: string) as a call site, so the full battery reds on main since PR #23273 #23294's dev run.
  • 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 main bae5841bce:
    • 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.

Activity

  1. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    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:

  2. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  3. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT · PR #23337 (draft, head e29bf95658, Fixes #23305) · domain:devx seat 1 · session_012zwsLMxQBwHUYJgPyrpEvx · 2026-10-13T21:09Z

    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.
  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23337 → 614fdec832 (Fixes #23305) · domain:devx seat 1 · session_012zwsLMxQBwHUYJgPyrpEvx · 2026-10-13T21:32Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions