Skip to content

fix(pm): route check-half-states' probe through the session proxy - #13574

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-13544-half-states-probe-proxy
Aug 31, 2026
Merged

fix(pm): route check-half-states' probe through the session proxy#13574
os-project-manager merged 1 commit into
mainfrom
claude/issue-13544-half-states-probe-proxy

Conversation

@claude

@claude claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #13544

The defect

scripts/pm/check-half-states.mjs --probe reported PREREQUISITE NOT MET in
containers that reach api.github.com perfectly well, and prescribed a
credential the caller does not need. Node's fetch ignores HTTPS_PROXY, so
the script sent the agent proxy's 14-character prox… placeholder straight to
GitHub, earned a 401, and reported that refusal as the container's verdict.

Re-derived in this container before writing a line — same host, same token, same
second, three endpoints, two routes:

endpoint node, bypassed node, routed through the proxy
GET /rate_limit 401 Bad credentials 200, core limit 15000, server: github.com
GET /user 401 200, the real login
GET /repos/objectstack-ai/objectstack 401 200, real x-github-request-id

curl answered 200 on all three throughout, because it honours the proxy. This
is the #4690 class inverted: readable looks like unreadable, and a probe
that says "you can't" is not argued with.

The shape, and why it is this one

The card offered two shapes and asked for the choice to be measured. Both
were measured, and the result rewrites the tradeoff the card stated:

So live modes re-exec once with --use-env-proxy when a proxy is configured and
the process is not already routed through it. scripts/pm/check-governed-merges.mjs
already carries exactly this mechanism for exactly this reason; this file keeps
its own copy rather than importing it, because the patrol pair is copied
verbatim and must not acquire a dependency on a file outside that pair.

What did NOT change — the three constraints

  • The refusal survives. A failed probe still prints PREREQUISITE NOT MET
    and still exits 3. Nothing was weakened so the sweep could proceed on a
    failed probe.
  • Exit 3 still means NOT MEASURED. EXIT_PREREQUISITE_NOT_MET is untouched,
    and the "nothing was swept" paragraph is untouched.
  • classifyTransportProbe is unchanged for its other importer. The route
    reading is an optional field; handed none, every verdict is byte-identical
    — pinned as a self-test case, so scripts/pm/ci-failure.mjs is unaffected.

The stage-2 repo-scoped probe (#9946) also stays exactly as it is. The class it
was built for refused repo-scoped reads through the proxy; this container
allows them. Both readings are true, of different routes, and the proxy's
repo-scope policy is per-container — so the discriminator is still needed.

Two wordings the fix falsified, and corrected

  1. A bypassed reading now leads with the ROUTE (node --use-env-proxy …)
    and demotes the credential remedy to If the route is not the problem: ….
    The old text sent the reader after a secret they do not need.
  2. A routed refusal no longer reassures the reader that "node's fetch does
    not use HTTPS_PROXY" — that stopped being true the moment this file routes
    it. A stale reassurance inside a refusal is the same defect one layer down.

Verification — the control runs BOTH ways

Every leg below is a real run in a proxied agent container with the placeholder
token unchanged
(len 14, prefix proxy). No real credential was supplied.

Leg A — reachable direction, --probe: EXIT=0

✓ check-half-states: transport prerequisite met — api.github.com is reachable and the token authenticates.

Leg A2 — non-vacuity, the FULL sweep against the live board: SWEEP_EXIT=0,
465 lines of findings, run 2026-08-31T01:31:51Z01:33:39Z:

check-half-states: swept 235 open pm-/p0-labeled issue(s), 339 open issue(s) in the
unscoped pass (H13–H15, H18), 5 open PR(s) … and 397 recently-merged PR(s) in
objectstack-ai/objectstack — 207 half-state(s) found.

(The card's reference reading was 217 at 2026-08-30T19:3xZ; the board has moved
since. Non-zero against the live board, which is what the acceptance asked for —
no fixture, no recorded response.)

Leg B — genuinely unreachable transport, the control that matters (proxy
pointed at a dead port; the re-exec still fires, the child still cannot reach the
host): EXIT=3, and the sweep leg wrote 0 bytes to stdout.

check-half-states: PREREQUISITE NOT MET — `api.github.com` is not reachable from node in this container
  `GET /rate_limit` did not complete: ECONNREFUSED
  This request DID go through the configured proxy (`http://127.0.0.1:1`) — the
  #13544 re-exec routed it — so the route is not the missing piece here …

Leg C — the GitHub Actions runner shape (no proxy env): a proven no-op. With
HTTPS_PROXY/https_proxy unset, the probe output is byte-identical to the
pre-change baseline
captured before the first edit — 1232 bytes, diff clean,
EXIT=3 both. No child process, no extra line. This is the leg that keeps the
file safe to copy verbatim, and it is measured rather than argued.

Leg D — proxy configured but the re-exec did not happen (the older-node /
failed-spawn fallback, forced via the guard env): EXIT=3, and the report leads
with the route remedy while keeping the credential remedy as a named alternative.

Leg E — the workflow's own invocation (--format=markdown --provenance=…):
EXIT=0, 59939 bytes on stdout, and 0 occurrences of the re-exec notice in
stdout. The notice goes to stderr, because that stdout is redirected into an
issue body.

Gates — union run at f892cd8a6 (final commit, clean tree)

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived
31 families + 2 convention-triggered (this diff edits a gate script), harvested
with --commands so neither spelling is dropped. 31 green, 2 NOT MEASURED:

  • node scripts/check-test-completeness.mjsexit 3, its own text: "the
    local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is
    nothing here to fix."
    CI passes it a real log.
  • node scripts/check-partof-closing-keyword.mjs (bare) → exit 2, its own
    text: "NOT WIRED — neither PR_BODY nor PR_NUMBER is set … This is a wiring or
    usage failure, NOT a verdict."
    The wired spelling pnpm check:partof-closing-keyword
    is green, and this body was additionally run through it with PR_BODY set.

Also green: node scripts/pm/check-half-states.mjs --self-test1584 cases,
30 of them new and covering the re-exec decision in every branch, the corrected
wording in both directions, the unchanged-for-ci-failure guarantee, and the
refusal-survives control. node scripts/pm/ci-failure.mjs --self-test (the other
importer) and node scripts/pm/check-governed-merges.mjs --self-test (230
assertions, the file this mechanism is mirrored from) are green too.

Full-repo pnpm lint (eslint . --no-inline-config) run under the shared
verify lock: os-verify-lock: VERDICT command-exit 0 · held the lock 102s.

No changeset: this diff touches only scripts/pm/** and one workflow docblock,
publishing nothing from any package — skip-changeset applies and is labelled.

Scope

#13526 is not addressed here and remains open — no bulk relabel, no sweep.
This card restores the probe; that population is its own card. The scheduled
patrol is untouched: it is right for the #9844 reason, which was never a
transport reason.


Generated by Claude Code

`check-half-states --probe` reported PREREQUISITE NOT MET in containers that
reach api.github.com perfectly well. Node's `fetch` ignores `HTTPS_PROXY`, so
the script sent the agent proxy's 14-character placeholder token straight to
GitHub, earned a 401, and reported that refusal as the container's verdict —
the #4690 class inverted: readable looks like unreadable.

Live modes now re-exec once with `--use-env-proxy` when a proxy is configured
and the process is not already routed through it. It has to be a re-exec:
assigning `process.env.NODE_USE_ENV_PROXY` from inside is too late (measured).
Same mechanism `check-governed-merges.mjs` already carries, kept as its own
copy because this file is copied verbatim into sibling repos.

The refusal itself is untouched: a failed probe still prints PREREQUISITE NOT
MET and still exits 3. Two wordings that the fix falsified are corrected — a
bypassed reading now prescribes the ROUTE instead of a credential nobody needs,
and a routed refusal no longer reassures the reader that node ignores the proxy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 02:11
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit afacebb Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-13544-half-states-probe-proxy branch August 31, 2026 02:29
zhuangjianguo pushed a commit that referenced this pull request Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants