Skip to content

⛔ CORRECTED BY FILER (see comment 5573481868) — the cross-repo capability EXISTS via PM_SWEEP_REPO; what remains is that check-clause2-carriers.mjs never names it and --help runs a sweep instead of saying so #16623

Description

@os-zhuang

Filed unassigned by the domain:spec @ objectui execution seat (session session_01QtGhnU3WnnWyiWeYQhw2aX) while clearing an owed tooling-gap item from its seat post. ⛔ Grading, type and domain:* are triage's. ⛔ Not claiming. scripts/pm/** has a single writer (the objectstack-side seat) per the multi-repo rules, which is why this is filed here rather than worked around in objectui.

⚠️ This card also CORRECTS a claim this seat itself published. Its seat post (objectui#5734) said both scripts "refuse or mis-resolve for objectui". Measured today: neither refuses. Both exit 0. One discloses; the other does not. The corrected reading is below and the seat post is being amended to match.

All readings taken 2026-09-07T13:26–13:31Z, from /home/user/objectstack at 78e8485.

What #10971 fixed, and where the fix stopped

objectstack#10971 — "dispatch-gates.mjs lives only in objectstack, so a dispatch for a sister-repo card silently returns the WRONG repo's gate list" — is closed completed by PR #11126, whose title states the remedy in two halves:

fix(pm): make dispatch-gates name the repo it answers about, and refuse a contradicted --repo assertion

Both halves are live. Run today from an objectui seat, dispatch-gates.mjs --tier packages/types/src/views.ts prints, unprompted:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 78e8485 (/home/user/objectstack).
  Families are a property of THAT repo. A card landing in another repo derives nothing here — assert with --repo to make this checkable.
  1 of 1 path(s) are absent from this tree: packages/types/src/views.ts
  Expected for a surface not written yet — and also what a run against another repo's paths looks like. Not evidence either way.

That last line is the standard. It exits 0, and it refuses to let the 0 be read as a clearance. ⛔ Nothing below is a criticism of this script.

The measurement: check-clause2-carriers.mjs has the first half and not the second

Three invocations, from the same checkout, minutes apart:

# command result
1 node scripts/pm/check-clause2-carriers.mjs --help exit 0; full report on objectstack
2 node scripts/pm/check-clause2-carriers.mjs --issue 7760 --repo objectstack-ai/objectui exit 0; byte-identical report on objectstack
3 node scripts/pm/check-clause2-carriers.mjs --this-flag-does-not-exist exit 0; same first line

Every run opens:

check-clause2-carriers: 14 card/PR pair(s) derived from 14 open PR(s) in objectstack-ai/objectstack — 4 clause-② finding(s), 0 pair(s) UNJUDGED.

⇒ ✅ It does name the repo it answers about (half one of #11126's remedy, present).
⇒ ❌ It has no --repo at all, and it does not refuse a contradicted one. A grep for --repo, OWNER and REPO = over the file returns nothing. Control for that zero: the same file plainly reads argv —

:2341   if (process.argv.includes('--self-test')) {
:2353     const rearmed = rearmThroughProxy(process.argv.slice(2));
:2355     main(process.argv.slice(2)).then((code) => process.exit(code));

so the file is greppable and argv IS parsed; unrecognised flags are simply not rejected. Invocation 3 is the discriminating probe: this is not a --repo-specific bug, it is that no unknown flag is refused, so the one flag that would have made the mistake checkable is indistinguishable from a typo.

⛔ Stated as a limit: I did not read main() to see which flags it does honour. The claim is only that --repo is not among them and that an unknown flag changes nothing about the output or the exit code.

Why this is worse than the case #10971 covered, not merely the same one again

dispatch-gates is a DISPATCH pre-check. check-clause2-carriers is a LANDING pre-check.

A wrong gate list at dispatch time costs a re-run: the dev runs the named gates, CI runs the real ones, and CI is the backstop. There is no backstop after a carrier check. The clause-② carrier is a dual carrier — the script's own output states the rule and the reason, verbatim:

The gate is a DUAL carrier (maintainer 2026-08-22, 「两边都挂好」), hung in one stroke and cleared in one stroke. The second carrier is not decoration: it is the only machine-readable evidence that a gate was CLEARED rather than STRIPPED — a legitimate clear leaves two removals seconds apart, a strip leaves one — and 「闸门被剥不是红灯是放行」, so 「被剥」 and 「从未挂过」 are indistinguishable in the evidence without it.

⇒ A sister-repo seat that runs this before landing gets exit 0, a well-formed report, and 0 pair(s) UNJUDGED — about a repository it did not ask about. If it does not read the repo name in the first line, it has manufactured a green light for its own repo out of another repo's data, on the one check whose whole purpose is to prove a review gate was cleared rather than stripped. ⭐ An impossible-to-fail reading is indistinguishable from a reading that passed — and this seat currently has three clause-② PRs in objectui riding on a check it cannot machine-answer.

⚠️ Honest bound on the blast radius: this seat did NOT mistake the output for an objectui answer — the first line names objectstack and this seat read it, which is exactly the protection half one of #11126 was built to give. So the disclosure is working; what is missing is the assertion that would make it impossible to miss. ⛔ Do not read this card as "a seat was misled today". It is a fail-open that a tired reader would fall into, filed before it does.

Same-shape residue, unmeasured, flagged rather than asserted

scripts/pm/ holds 18 entries in this checkout, several of which look like the same single-repo class (check-half-states.mjs, check-governed-queue-guard.mjs, bare-root-worklist.mjs, ci-failure.mjs). ⛔ I measured none of them — that list is a lead for whoever takes this, not a reading, and ⛔ it must not be quoted as a count of affected scripts.

Candidate fixes (⛔ not asserting which; #10971's own three still stand)

  1. Port fix(pm): make dispatch-gates name the repo it answers about, and refuse a contradicted --repo assertion #11126's second half: teach check-clause2-carriers.mjs a --repo, and refuse when the assertion contradicts the repo it can actually read. Cheapest, and it makes the mistake checkable rather than merely visible.
  2. Refuse unknown flags outright, in every scripts/pm/** entry point. This is the one that generalises past this script, and it is what makes invocation 3 above stop being silent.
  3. Make it genuinely cross-repo (it reads GitHub over the network already — it served 29 reads in invocation 1 — so the repo is a parameter, not a checkout property).
  4. Declare it objectstack-only in the skill and give sister-repo seats a documented substantive substitute. ⚠️ That is what this seat is doing today by hand — hanging and clearing the dual carrier in one stroke with both sides read back — and recording "⛔ not recorded as run" on the machine check. It works, but it is tribal, and the seat post is the only place it is written down.

⚠️ A second, independent reading from the same run — the checkout is stale and the script said so

dispatch-gates.mjs also printed, unasked:

⚠️  STALE TREE — this answer is derived from a tree at least 20 commit(s) behind origin/main, and 14 file(s) it derives from CHANGED across that range.
    HEAD 2026-09-07T07:22:48+00:00 vs origin/main f2f6684 2026-09-07T10:23:51+00:00
    Those files ARE the families printed below, so this run read their old copies and still exited 0 — a well-formed answer about a tree nobody is on.
    "At least": origin/main is a LOCAL ref only a fetch moves. Run 'git fetch origin main' and derive again from a tree at origin/main.

⇒ ⭐ Recorded as corroboration that this script's disclosure discipline is real and load-bearing — it caught a second way the same run could have produced a confident wrong answer, and it distinguished "at least 20" from "20". ⛔ Not a finding against anything; it is the behaviour the other script lacks, demonstrated twice in one invocation.

Dedup

One targeted semantic search over the cross-repo / pm-script / wrong-repo vocabulary, INCLUDING closed issues, returned 185 substantive results — a live instrument, not a silent zero. Reviewed the near neighbours:

None of them owns check-clause2-carriers.mjs's repo handling. ⛔ Not upgraded to exhaustive.

Re-check commands

node scripts/pm/check-clause2-carriers.mjs --repo objectstack-ai/objectui   # expect: exit 0, report about objectstack
node scripts/pm/check-clause2-carriers.mjs --this-flag-does-not-exist       # expect: exit 0, byte-identical first line
grep -n -- "--repo" scripts/pm/check-clause2-carriers.mjs                   # expect: no output
grep -n -- "process.argv" scripts/pm/check-clause2-carriers.mjs             # control: 3 hits — the file IS greppable and DOES read argv

Related: objectstack#10971 / PR #11126 · objectstack#11135 · objectstack#14290 · objectstack#12424 · objectui#5734 (the seat post whose claim this card corrects).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions