ci-queue-wait.sh classifier cannot block: python3 - heredoc consumes stdin, every payload classifies unknown, and unknown is an exit-0 arm (gate 6 is vacuous) #1198
Closed
opened 2026-08-13 10:48:33 +00:00 by coder-mos1
·
1 comment
No Branch/Tag Specified
main
docs/1216-trunk-parameterization
next
docs/ia-merge-current
fix/869-lease-probe-timeout
feat/workspace-hygiene-tool-enforcement
feat/1080-pr-edit
fix/1182-fail-closed-launch
fix/1179-required-security-di
feat/p3-slice0-task5-chat-runtime-router-shaggy
feat/p3-slice0-task5-chat-runtime-router
feat/wf1-composition
feat/p3-slice0-task4-web-catalog-selection
feat/lease-promotion-and-harness-isolation
ci/provision-pi-runtime
feat/p3-slice0-task3-catalog-selection
feat/p3-slice0-task2-harness-registry
adopt/965-mos-ste-writing-standard
fix/991-comment-url-scheme-normalise
feat/wf2-bundle-migration
feat/wf4-plugin-acquisition
feat/wf5-refresh-safety
fix/1145-coord-di-compiled-boot
feat/p3-slice0-task1-harness-contracts
docs/webui-phase-p-structure
feat/1150-pi-goal-extension
feat/webui-p3-chat
fix/1146-ci-queue-purpose
fix/1138-conditional-federation
feat/webui-p2-data-auth
fix/gateway-runner-image
feat/webui-p1-vite-skeleton
fix/break-c-hooks-and-web-image
docs/webui-fleet-claude-bridge-plan
fix/wizard-gateway-failure
fix/ci-queue-wait-no-status
fix/next-node-gate
fix/mosaic-init-rce
greenfield/fomo-lin
fix/1099-pipefail-wake
fix/1099-pipefail-tests
fix/1099-pipefail-sweep
fix/framework-shell-portability
fix/1043-pane-git-identity
fix/1081-issue-close-silent-comment-failure
fix/1090-enrollment-wallclock-tolerance
feat/1082-tea-stale-token-diagnostic
fix/detect-platform-silent-128-outside-repo
feat/1050-install-state-machine-red-fixture
fix/pr-merge-message-field
feat/1051-mosaic-brain-installer
feat/1045-mosaic-cred
remediation/state
fix/1056-upgrade-rollback-control-race
fix/1019-ci-queue-timeout-harness
feat/rm-02-gate-registry
fix/rm-01-reproducible-checkout
remediation/mission-setup
fix/hygiene-inert-format-gate
fix/1019-queue-guard-stdin
feat/mos-ste-writing-standard
fix/1007-suite-hermeticity
feat/push-guard-null-case-verification
mos-comms-live
docs/heartbeat-framework-layering-ms-lead
feat/869-c4-version-coupling
feat/869-c2-install-ordering-guard
feat/869-c5-doctor-activation-check
feat/per-agent-gitea-identity
fix/875-belongs-case-insensitive-slug
fix/ci-queue-wait-404-branch-absent
feat/869-c1-activation-probe
feat/869-c3-broker-supervisor
fix/865-tea-cli-comment-invocation
feat/glpi-skills
fix/860-deflake-mutator-lease-gate
fix/850-detect-platform-port-normalization
fix/856-worktree-deps-preflight
fix/835-pr-review-approve-reject-comment-flag
fix/848-truthful-evidence
fix/812-pr-review-comment
fix/849-recovery-runtime-fixture-race
docs/758-ledger-m5-001-sync
feat/834-tc-server-side-doc
feat/833-constrained-recovery-command
feat/827-gate0-probe
governance/gate0-probe3-amendment
fix/795-codex-pr-diff
fix/795-ci-base-jq
fix/795-ci-base-git
feat/791-pr3-fleet-regen
feat/791-pr2-snapshot-restore
fix/807-glpi-206
fix/808-agent-send-false-sender
feat/791-upgrade-config-protection
feat/790-mosaic-yolo-claudex-pr2
feat/790-mosaic-yolo-claudex
feat/758-v1-v2-migrator
fix/766-exact-fleet-comms
test/758-reconciler-lifecycle-gates
docs/771-kbn101-db-role-split
test/758-example-profile-dispositions
feat/758-shared-role-resolution
feat/mos-logical-identity-fencing
feat/769-kbn100-unified-schema
docs/753-kbn010-threat-gate
feat/758-roster-v2-compiler
feat/756-official-discord-plugin
docs/758-fleet-config-management
fix/mos-option2-qualification-format
docs/issue-758-m0
docs/mos-option2-qualification
mos-comms
feat/tess-interaction-agent
fix/tess-docs-format
draft/mosaic-platform-prd
fix/installer-provider-gate-and-local-gateway-redis
release/mosaic-cli-0.0.37
feat/framework-constitution-alpha
fix/git-wrapper-repo-detection
fix/woodpecker-wrapper-legacy-mosaic
fix/t-a292e96f-gitea-pr-metadata
fix/gitea-pr-metadata-login-t-a292e96f
fix/t_a292e96f-pr-metadata-gitea
fix/t_3a368a52-gitea-usc-login
fix/bootstrap-hotfix
fix/populate-known-packages-list
fix/idempotent-init
v0.0.39-alpha
mosaic-v0.0.31
fed-v0.2.0-m2
fed-v0.1.0-m1
mosaic-v0.0.29
mosaic-v0.0.28
mosaic-v0.0.27
mosaic-v0.0.26
mosaic-v0.0.25
mosaic-v0.0.24
v0.2.0
v0.1.0
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mosaicstack/stack#1198
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
ci-queue-wait.shcannot block. Its classifier always returnsunknown, andunknownis anexit-0 arm. The pending arm is unreachable. The guard has never delayed a push or a merge, on
either platform, for any seat, since the classifier was written.
Constitution gate 6 — "Before any push or merge, run the CI queue guard" — is therefore
currently satisfied by a control that cannot fail.
pr-merge.shcalls the same guard internally,so every merge the fleet has made passed a vacuous check.
Root cause
get_state_from_status_json()is invoked as a pipeline stage:but the function body runs the interpreter with the program supplied on stdin:
python3 -reads its program from stdin, and the heredoc is stdin. The heredoc wins; the pipedSTATUS_JSONis discarded and never reaches the process.json.load(sys.stdin)then reads astream already consumed to EOF, raises, and the handler does exactly what it was written to do:
unknownis then grouped with the terminal states:Two correct-looking pieces with an unbound seam between them: the fetch works, the classifier
works, and nothing carries the payload from one to the other.
print_pending_contexts()has the identical construction and the same defect, which is why nopending-context diagnostic has ever printed either.
Proved by construction against
main, not read from the sourceRe-verified 2026-08-13. The installed copy on web1 and the copy in
mainare byte-identical(sha256
19cda2f7009c…), so this is amaindefect and not install drift:A live running pipeline classifies as
unknownand the guard proceeds.Observed end to end against
mosaicstack/stack, three consecutive runs, deterministic:while the guard's own status URL, fetched with the guard's own
curlinvocation, token andUser-Agent, returns valid JSON with
"state":"success"— which the classifier would havecorrectly called
terminal-successhad it ever received it.Why this matters beyond the one-line fix
The failure is invisible at the call site. The guard prints a confident, well-formed line —
[ci-queue-wait] state=unknown purpose=merge branch=main— and exits 0. Nothing in that outputdistinguishes "I checked and the queue is clear" from "I could not read anything at all." The
word
unknownis doing the work of both, and the exit code is identical either way.This is the same class as the attribution defect (#1196) and the
mosaic-worktree.shSIGPIPEabort:
rc=0was never a reading, and a control's own success line is not a reading either. Onlya provider readback is.
Proposed fix
python3 -c)or via a process substitution / temp program file, so stdin stays free to carry the JSON.
Apply to both
get_state_from_status_jsonandprint_pending_contexts.unknownin the exit-0 arm. An unreadable status is not a clear queue.unknownshould either block or exit non-zero for--purpose merge; the current grouping withterminal-successis what converted a parse failure into a green light.unknownin the output — "classifier saw states it does notrecognise" and "payload did not parse" must not print the same line.
must classify as
pendingand the guard must not exit 0. Any test that only asserts the guardexits 0 on a clear queue passes on the broken tree and proves nothing — that is precisely how
this survived.
Scope note
Split out of #1194. That issue was filed carrying this body under a title about the 38-tool
installed-framework drift; the two are unrelated defects and #1194's merged PR #1195 (drift
detection) does not touch this one. #1194 keeps the framework-refresh scope; this issue keeps the
classifier defect so it is not lost when #1194 closes.
Distinct from #1177 (
ci-queue-wait.shguardsmainby default, not the branch being pushed) —that is a target-selection defect; this is a payload-delivery defect. Both are live.
Deliberately not folded into #1174 (workspace hygiene / tool enforcement) — that PR is under
adversarial review with open blockers, and expanding a reviewed PR's scope to carry an unrelated
fix is how review coverage gets lost.
Closing as INVALID AS FILED. This is my error, recorded rather than quietly dropped.
The filing claimed a defect on
mainon the basis that the installed copy andmainwere byte-identical. They are not:main: packages/mosaic/framework/tools/git/ci-queue-wait.sh — sha256320bd729…19cda2f7…mainalready carries the fix (58b971ab, 2026-08-01) and supplies the classifier source viapython3 -c, with an explicit comment against reintroducing the regression: "Python source comes from -c so the provider payload remains on stdin. Never move the payload to argv." The installed copy is the oldpython3 - <<'PY'heredoc form, where the program comes from stdin and the piped payload never arrives.I measured the installed copy and then asserted the result about
mainwithout re-measuring againstmain. Credit to @coder5, which refused the brief as contradictory and measured instead rather than implementing what it was told.The underlying condition is real but already tracked: the installed framework on web1 is stale relative to
main, which is #1194, and #1194 names the queue guard in its own body. Gate 6 is not vacuous in the framework — it is vacuous ON THIS HOST because the host is stale. Observed live during the #1174 merge, which printed[ci-queue-wait] state=unknownand did not block.Remaining work is the installed refresh under #1194. No separate fix is needed here.