fix(worker): dial control.json's bindAddress instead of hardcoding loopback - #30
Merged
Merged
Conversation
…opback `worker.rs` built its base URL as `http://127.0.0.1:{port}`, ignoring `control.json`'s `bindAddress`. When the app binds a specific address (the mobile-client remote-access path) that is a single-socket bind, so loopback is not listening: every `POST /queues/<q>/claim` fails with ConnectionRefused and the worker exits before claiming any task. The failure is near-silent from the outside. Worker panes sit `running`/`idle` and tasks stay `queued` with `claimedBy: null`, which reads as an empty queue or a bad fan-out rather than a config fault — you only see it by reading a worker pane's tail. Observed against a tailnet bind, where it made every queue fan-out in the goals system drain nothing. `control_cli::base_url` already encodes the right rule and is reused here rather than duplicated: dial a specific address directly (bracketing IPv6), keep loopback for an unspecified (`0.0.0.0`/`::`) or absent bind, so legacy control.json files are byte-for-byte unaffected. The expression lives in `control_base()` so the tests exercise the same path `run()` does — reverting it to a hardcoded loopback turns them red, which a test that rebuilt the expression itself would not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eyalm321
added a commit
that referenced
this pull request
Sep 18, 2026
The backport of 47c6adf left `locate` returning `Option<(ResolveResult, Option<u32>, Option<u32>, usize, usize, usize, f32, f32)>`, which clippy refuses as `type_complexity` — so `lint (rs/crates/terminal-widget)` went red. The push workflow on main runs tests but no lint job, so main looked green and PR #30 was the first thing to surface it. An eight-field positional tuple with three bare `usize` is a swap waiting to happen anyway, so it becomes a `PathHit` struct with documented fields rather than a `type` alias that would only quiet the lint. One call site. Also runs rustfmt over the crate: the backport drifted `font.rs`, and only core was fmt-checked at merge time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eyalm321
added a commit
that referenced
this pull request
Sep 18, 2026
… branches Two independent CI faults, both observed on 2026-09-17. **Pushes to main skipped half the gate.** `lint` and `build-gui` live in verify.yml, which was `pull_request`-only, so anything landed straight on main was unlinted and its GUI build unverified. Main went green while carrying a clippy `type_complexity` error and, later, a tree where `cargo build --locked` on the app crate failed; a PR opened against main was the first thing to notice either. verify.yml now also runs on `push: branches: [main]`. build-gui's `contains(github.event.pull_request.labels.*.name, ...)` clause is null-safe on push, and the `needs.changes.outputs.rust` path filter drives it as before. **test.yml fired twice for every PR-branch push.** A bare `push:` plus `pull_request:` produced two runs whose `github.ref` differs (refs/heads/<branch> vs refs/pull/N/merge), so the concurrency group could not dedupe them; the surplus run raced the real one and cancelled jobs in it. The `verify` gate reads `cancelled` as failure, which is the whole reason PR #30 sat red for a month on `lint=cancelled` with nothing wrong in its code. `push:` is now `branches: [main]`, so PR branches get exactly one run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eyalm321
added a commit
that referenced
this pull request
Sep 18, 2026
The app crate reports its own version through `CARGO_PKG_VERSION` — the About block, the crash reporter and the self-updater all read it — so it has to match the tag or a v0.0.29 build claims to be 0.0.28 and the updater keeps offering an "upgrade" the user already has. What lands since v0.0.28: - Security: `control.json` carried the master control token world-readable (0644 under the process umask); `control-token` and `device-tokens.json` were chmod'd 0600 only after the rename, leaving a window. All three now go through `write_atomic_private`, which creates the temp file 0600 before the first byte. Verified on a live instance, not just in tests. - Rendering: glyph coverage blends in linear light rather than sRGB bytes (thin strokes were rendering grey); synthetic bold no longer draws `i` as a smudge; CJK full-width glyphs are centred in their 2-cell span instead of left-clamped. - Terminal: soft-wrapped links hit-test as one link; Shift+Enter reaches a pane over the control API; a CLI piped into `head` exits quietly instead of a crash dialog. - Sessions: a re-attached pane no longer comes back blank; one directory reached two ways is one project again; two threads writing atomically no longer race for a temp name. - Worker: dials `control.json`'s `bindAddress` instead of hardcoding loopback, so queue fan-out works on a non-loopback bind (#30). - Perf: per-frame snapshot Vec and per-glyph mask clone removed from the render path; the GPU stack is behind an opt-in `gpu` feature. - Features: per-pane dictation (push-to-talk) and AT-SPI text for the terminal grid — both still want a live pass. Ten of the fixes are backported from bshuler/avada-terminal and three from xiaoyuan0459/hyperpanes, authorship preserved on each. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
worker.rsbuilt its control-API base URL ashttp://127.0.0.1:{port}, ignoringcontrol.json'sbindAddress:When the app binds a specific address (the mobile-client remote-access path) that is a
single-socket bind, so loopback is not listening. Every
POST /queues/<q>/claimfails withConnectionRefusedand the worker exits before claiming anything.Why it is worth fixing rather than working around
The failure is near-silent from the outside. Worker panes sit
running/idleand tasks stayqueuedwithclaimedBy: null— which reads as an empty queue or a botched fan-out, not a configfault. The only tell is a worker pane's tail:
Observed against a tailnet bind, where it made every queue fan-out in the goals system drain
nothing. It was diagnosed only after mistaking it for bad task payloads.
The fix
control_cli::base_urlalready encodes exactly the right rule, so it is reused rather thanduplicated: dial a specific address directly (bracketing IPv6), keep loopback for an unspecified
(
0.0.0.0/::) or absent bind.bindAddressis#[serde(default)], so legacycontrol.jsonfiles are byte-for-byte unaffected.
Tests
Three tests in
worker::tests, driven from realcontrol.jsonpayloads:http://100.120.216.17:41419http://[fd7a::1]:41419(bracketed)0.0.0.0and::→http://127.0.0.1:41419The expression lives in a
control_base()helper so the tests call the same pathrun()does.Verified by mutation: reverting
control_baseto the hardcoded loopback turns the twospecific-address tests red (17 passed / 2 failed), and restoring it returns 19/19. A test that
rebuilt the expression itself would have passed either way — that earlier draft is what prompted
the refactor.
Full app-crate suite green: 160 + 2 passed, 0 failed.
worker.rsis rustfmt-clean (several otherfiles in the crate have pre-existing fmt diffs; untouched).
Not covered here
The npm MCP bridge (
hyperpanes-mcp,dist/control/discovery.js) has the identical defect — italso builds
http://127.0.0.1:<port>and dropsbindAddress, somcp__hyperpanes__*tools failthe same way. That lives outside this repo.