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
check-half-states --probe reports PREREQUISITE NOT MET in a container that reaches the API — Node's fetch bypasses the agent proxy, and the refusal reads as the container's verdict #13544
scripts/pm/check-half-states.mjs --probe refuses to run in the triage Routine container, and its refusal is wrong. The container reaches api.github.com fine; the script does not use the channel that reaches it.
The probe's verdict here
check-half-states: PREREQUISITE NOT MET — the token in the environment is not a valid GitHub credential
`GET /rate_limit` with GITHUB_TOKEN/GH_TOKEN = prox… (len 14) -> HTTP 401.
…
Fix: export GITHUB_TOKEN=<a real GitHub token> and re-run
The same request, same token, same container, via curl
request
result
GET /rate_limitwith$GITHUB_TOKEN
HTTP 200 — core.limit 15000, remaining 14998
GET /rate_limitwithout a token
HTTP 200, same body
positive control, same host & token: GET /repos/objectstack-ai/objectstack/issues?labels=pm:retriage&per_page=3
HTTP 200, 3 issues returned
The probe reports 401 for the endpoint that answers 200 one process away. The token really is the proxy placeholder (len 14, prefix proxy) — that part of the diagnosis is correct — but the conclusion drawn from it is not: this container's outbound HTTPS goes through an agent proxy that supplies the real credential. curl honours HTTPS_PROXY; Node 22's fetch does not, by default. So the script sends the placeholder straight to GitHub and gets the 401 it then reports as the container's verdict.
The fix, measured
$ NODE_USE_ENV_PROXY=1 node scripts/pm/check-half-states.mjs --probe
✓ check-half-states: transport prerequisite met — api.github.com is reachable and the token authenticates.
$ NODE_USE_ENV_PROXY=1 node scripts/pm/check-half-states.mjs # EXIT=0
… swept 227 open pm-/p0-labeled issue(s), 335 open issue(s) in the unscoped pass,
11 open PR(s) … — 217 half-state(s) found.
One environment variable turns "this container needs a real credential" into a complete 217-row sweep.
Why this is p1 rather than a nit
A diagnostic that reports the opposite of the truth, on the exact question of whether a reading is possible. The repo already has a name for the adjacent failure — #4690, "could not read the input" must never look like "input is clean". This is that class inverted: readable looks like unreadable. The inverted direction is the more durable one, because it costs the tool nothing and costs only the caller: a probe that says "you can't" is not argued with.
No lane can get the data on demand. The scheduled patrol runs 4×/day and lands a body that (this run) rendered 70 of 266 rows, with "196 further row(s) omitted to fit GitHub's issue-body limit". On-demand self-service is the release valve for that trim, and the probe closes it.
⛔ Not claimed here: that the trim has ever silently dropped a row that mattered. I tried to establish exactly that and failed to — see "What I could not establish" below.
Scope, and one constraint that must not be missed
⚠️ This file is copied VERBATIM into sibling repos (#11217, HALF_STATE_ANCHOR_ISSUE). Any fix must be a no-op on a GitHub Actions runner, where no proxy env vars exist. NODE_USE_ENV_PROXY=1 satisfies that (it configures from env vars that are absent there), but it emits an experimental warning:
(node:1754) [UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental
⇒ Two shapes, executor's choice, but the choice must be measured, not argued:
(a) set NODE_USE_ENV_PROXY=1 from within the script (or its shebang/wrapper) — smallest diff, carries the experimental warning;
(b) construct an undiciProxyAgent/EnvHttpProxyAgent explicitly and pass it as the dispatcher — no warning, more code, and pins a dependency shape.
Whichever lands, the probe's failure text must also be corrected: it currently prescribes export GITHUB_TOKEN=<a real token>, which is the wrong remedy in a proxied container and will send the next reader looking for a credential they do not need.
Acceptance
--probe returns ✓ in a proxied agent container with the placeholder token unchanged — ⛔ not by supplying a real token, which would be fixing the message rather than the defect.
--probe still returns ✓ on a GitHub Actions runner (the existing scheduled run stays green).
--probe still returns the PREREQUISITE NOT MET path, with exit 3, when the transport genuinely cannot reach api.github.com — the refusal must survive, or the fix has replaced a false negative with a false positive.
⭐ Non-vacuity, on the real configuration. Prove (1) by running the full sweep in that container and showing a non-zero half-state count against the live board — ⛔ a fixture, a recorded response, or a probe-only ✓ does not discharge this. The reference reading taken while filing: 217 half-state(s), exit 0, 2026-08-30T19:3xZ.
What I could not establish, stated so nobody inherits it as fact
I expected to find that the anchor's size trim had silently swallowed H18 — the predicate that names the triage lane's own duty — because the published body (13:42:19Z) renders zero H18 rows while the live board carries 13.
Measured, and it does not hold. The three old carriers (#12104, #12237, #12892) were labeled pm:retriage at 15:01:28Z–15:01:35Z, and every bare-label carrier was filed from 14:12Z onward — all after the 13:42Z sweep. The only possible carrier at that moment (#13216) was ~1.4h old against a 2h threshold, so H18 was correctly silent. The trim is real and self-declared; it is not established that it has ever hidden an H18 row, and this card does not claim it.
Provenance
Filed by the triage seat (#6015) from readings taken in-container at 2026-08-30T19:3xZ, origin/main at 74049254. ⛔ Nothing was edited and no PR was opened. ⚠️ Filed over the REST channel to preserve the body intact (#12886 / #13385 truncate on the MCP channel); that channel records the author as claude[bot] rather than os-project-manager — same seat, different transport.
What I measured
scripts/pm/check-half-states.mjs --proberefuses to run in the triage Routine container, and its refusal is wrong. The container reachesapi.github.comfine; the script does not use the channel that reaches it.The probe's verdict here
The same request, same token, same container, via
curlGET /rate_limitwith$GITHUB_TOKENcore.limit 15000,remaining 14998GET /rate_limitwithout a tokenGET /repos/objectstack-ai/objectstack/issues?labels=pm:retriage&per_page=3The probe reports 401 for the endpoint that answers 200 one process away. The token really is the proxy placeholder (
len 14, prefixproxy) — that part of the diagnosis is correct — but the conclusion drawn from it is not: this container's outbound HTTPS goes through an agent proxy that supplies the real credential.curlhonoursHTTPS_PROXY; Node 22'sfetchdoes not, by default. So the script sends the placeholder straight to GitHub and gets the 401 it then reports as the container's verdict.The fix, measured
One environment variable turns "this container needs a real credential" into a complete 217-row sweep.
Why this is p1 rather than a nit
A diagnostic that reports the opposite of the truth, on the exact question of whether a reading is possible. The repo already has a name for the adjacent failure — #4690, "could not read the input" must never look like "input is clean". This is that class inverted: readable looks like unreadable. The inverted direction is the more durable one, because it costs the tool nothing and costs only the caller: a probe that says "you can't" is not argued with.
Two concrete consequences, both live today:
half-state-patrol.ymlboth justify the move to a scheduled Actions caller with: "the live sweep cannot run inside a PM session container at all ([finding]scripts/pm/check-half-states.mjslive sweep cannot run in a PM session container — direct api.github.com is 403 with or without the token, and the docblock's "unauthenticated works" is false here #7412 class 1 — api.github.com refuses that egress in both directions, with and without a token)". Whatever was true when that was measured, it is not true in this container now — and the same header already names the triage Routine container as reachable ([finding]scripts/pm/check-half-states.mjslive sweep cannot run in a PM session container — direct api.github.com is 403 with or without the token, and the docblock's "unauthenticated works" is false here #7412 class 2). The two halves of the docblock disagree and the probe was the thing that should have caught it.⛔ Not claimed here: that the trim has ever silently dropped a row that mattered. I tried to establish exactly that and failed to — see "What I could not establish" below.
Scope, and one constraint that must not be missed
HALF_STATE_ANCHOR_ISSUE). Any fix must be a no-op on a GitHub Actions runner, where no proxy env vars exist.NODE_USE_ENV_PROXY=1satisfies that (it configures from env vars that are absent there), but it emits an experimental warning:⇒ Two shapes, executor's choice, but the choice must be measured, not argued:
NODE_USE_ENV_PROXY=1from within the script (or its shebang/wrapper) — smallest diff, carries the experimental warning;undiciProxyAgent/EnvHttpProxyAgentexplicitly and pass it as the dispatcher — no warning, more code, and pins a dependency shape.Whichever lands, the probe's failure text must also be corrected: it currently prescribes
export GITHUB_TOKEN=<a real token>, which is the wrong remedy in a proxied container and will send the next reader looking for a credential they do not need.Acceptance
--probereturns ✓ in a proxied agent container with the placeholder token unchanged — ⛔ not by supplying a real token, which would be fixing the message rather than the defect.--probestill returns ✓ on a GitHub Actions runner (the existing scheduled run stays green).--probestill returns the PREREQUISITE NOT MET path, with exit 3, when the transport genuinely cannot reachapi.github.com— the refusal must survive, or the fix has replaced a false negative with a false positive.2026-08-30T19:3xZ.half-state-patrol.yml) where they assert the sweep cannot run in a PM/agent container. ⛔ Do not delete the scheduled caller — it is right for its own reasons (check-half-states is a healing mechanism with no standing caller — eight malformed claims and an unenumerated two-lane backlog went unseen until the maintainer asked #9844: an alarm nobody runs is silence); this only restores the on-demand path beside it.What I could not establish, stated so nobody inherits it as fact
I expected to find that the anchor's size trim had silently swallowed H18 — the predicate that names the triage lane's own duty — because the published body (13:42:19Z) renders zero H18 rows while the live board carries 13.
Measured, and it does not hold. The three old carriers (#12104, #12237, #12892) were labeled
pm:retriageat 15:01:28Z–15:01:35Z, and every bare-label carrier was filed from 14:12Z onward — all after the 13:42Z sweep. The only possible carrier at that moment (#13216) was ~1.4h old against a 2h threshold, so H18 was correctly silent. The trim is real and self-declared; it is not established that it has ever hidden an H18 row, and this card does not claim it.Provenance
Filed by the triage seat (#6015) from readings taken in-container at 2026-08-30T19:3xZ,⚠️ Filed over the REST channel to preserve the body intact (#12886 / #13385 truncate on the MCP channel); that channel records the author as
origin/mainat74049254. ⛔ Nothing was edited and no PR was opened.claude[bot]rather thanos-project-manager— same seat, different transport.Generated by Claude Code