Uh oh!
There was an error while loading. Please reload this page.
fix(init): stop pinning the gateway listen port so LLP 0114's default applies - #433
Conversation
… applies `hyp init` wrote an explicit `listen: '127.0.0.1:8787'` into the `@hypaware/ai-gateway` slice from both config writers: the picker walkthrough (`composePickerConfig`) and the `claude-and-otel-local` preset. Both predate LLP 0114, so a fresh install landed on the wrong port and, worse, opted itself out of the EADDRINUSE safety net: an explicit `listen` is a stated port requirement, so `compileConfig` marks it `listenConfigured` and the source fails loudly instead of falling back to `127.0.0.1:0` (LLP 0114 #explicit-listen-fails-loudly). Every wizard-created install therefore reached the loud-failure branch it never asked for, feeding #137. Subtractive fix: neither writer emits `listen`, so `DEFAULT_LISTEN` (`127.0.0.1:18521`) applies and `listenConfigured === false` re-arms the default-only fallback. One consequence needed handling: the walkthrough attaches clients before the finale restarts the daemon, so the gateway is usually not bound in this process and `configuredGatewayEndpoint()` now returns undefined. The old `http://127.0.0.1:0` placeholder is not a usable address (attach failed with "cannot derive port from endpoint"), so the walkthrough now falls back to the fixed default via a new core `DEFAULT_GATEWAY_ENDPOINT`, pinned against the plugin's `DEFAULT_LISTEN` by test. Manual `hyp attach` is untouched: it still reads the proven port from status.json rather than guessing (LLP 0086). LLP 0114 gains a #init-writes-no-listen consequence recording both. Co-Authored-By: Claude <noreply@anthropic.com>
philcunliffe
commented
Jul 28, 2026
Verdict: clean (no actionable findings)Reviewed 1. The subtractive fix itself
2. Scope: was |
Uh oh!
There was an error while loading. Please reload this page.
Two resolutions, one textual and one semantic. Neither reverses a landed decision. 1. `claude-desktop/hypaware.plugin.json` (the only git-reported conflict). #445 (`74aea66`) deleted the `attach_probe` block from `contributes.client`, restoring the Accepted decision at LLP 0115#no-attach-on-join and LLP 0135#no-probe. This branch added `transcript_entrypoints` as a sibling of that block, so the two edits collided on the same closing brace and nothing more. Took master's deletion whole and kept only the new field: "agent_dir": ".claude/agents", "transcript_entrypoints": ["claude-desktop", "claude-desktop-3p"] The probe stays gone. `transcript_entrypoints` is a backfill-ownership label read by `resolveOwnersForRun` (LLP 0140), never by `probeClientAttachFromDescriptor` or `detachClientFromDisk`, so it does not reintroduce an attach-on-join path and `test/plugins/ claude-desktop-detach.test.js`'s no-probe assertion still holds. 2. `test/core/compose-picker-config.test.js` (a silent semantic conflict git auto-merged). #433 (`8eccc04`) stopped `composePickerConfig` pinning the gateway `listen` so LLP 0114's fixed default applies to a wizard-created install. The `claude-desktop composes ...` case added here predates that and still expected `listen: '127.0.0.1:8787'`, so it was the one gateway slice in the file carrying a `listen` and it failed on the merge. Dropped the pin to match master's landed decision and the other eleven cases; the assertion this test exists for, that the Desktop row composes both plugins its `configure_command` needs, is untouched. `src/core/daemon/status.js` merged clean and reads coherently: master's no-probe guard (`descriptor.attachProbe ? probe : { attached: false }`) sits directly above this branch's `client_attach_missing` repair fix. Post #445 that fix matters more, not less: with no probe at all, a configured claude-desktop always reports `attached: false`, so the repair line is always printed, and the generic `hyp attach --client claude-desktop` it used to print answers `unknown client`. npm test: 2873 pass / 8 fail, exactly the pre-existing test/core/leave-command.test.js set. npm run typecheck clean. Smokes walkthrough_picker_to_first_query, cli_bundled_plugins_activated, status_diagnostics ok. Co-Authored-By: Claude <noreply@anthropic.com>
What was wrong
Both
hyp initconfig writers hardcoded an explicitlisten: '127.0.0.1:8787'into the@hypaware/ai-gatewayslice:src/core/cli/walkthrough.js(composePickerConfig, the interactive /--yespath)hypaware-core/plugins-workspace/claude/src/index.js(theclaude-and-otel-localpreset)Both predate LLP 0114. The wrong port is the visible half; the serious half is that an explicit
listenis a stated port requirement, socompileConfigsetslistenConfigured: trueand the gateway source takes the loud-failure branch (LLP 0114 §explicit-listen-fails-loudly) instead of the default-only ephemeral fallback. Every wizard-created install was opted out of the "the daemon always starts" invariant without the user ever naming a port, which is what feeds #137.The fix
Subtractive: neither writer emits
listen.DEFAULT_LISTEN(127.0.0.1:18521) applies andlistenConfigured === falsere-arms the fallback.One knock-on needed handling. The walkthrough attaches clients before the finale restarts the daemon, so the gateway is usually not bound in-process and
configuredGatewayEndpoint()now returnsundefined. The pre-existinghttp://127.0.0.1:0placeholder is not a usable address, and attach failed withcannot derive port from endpoint 'http://127.0.0.1:0'(caught by the picker smoke). The walkthrough now falls back to the fixed default through a new core constantDEFAULT_GATEWAY_ENDPOINTinsrc/core/config/gateway_endpoint.js, pinned against the plugin'sDEFAULT_LISTENby a test so the two cannot drift. Manualhyp attachis deliberately untouched: it still discovers the proven port fromstatus.jsonrather than guessing (LLP 0086 §manual-attach-reads-the-live-port).LLP 0114 gains a
#init-writes-no-listenconsequence recording both, and the code carries@refs to it.Regression test
New
test/core/init-gateway-listen-default.test.jsdrives the real writers (composePickerConfigover the real bundled manifests, and the realclaude-and-otel-localpreset via the plugin'sactivate()), asserts the gateway slice has nolistenkey, and round-trips the emitted slice throughcompileConfigto prove it yields127.0.0.1:18521withlistenConfigured === false.Before the fix: both writer tests fail with
must not pin a gateway listen address / true !== false. After: 3/3 pass.Assertions updated (they encoded the old behaviour)
test/core/compose-picker-config.test.js- 11 golden picker configs dropped the pinnedlisten.hypaware-core/smoke/flows/walkthrough_picker_to_first_query.js-goldenPickerConfigdroppedlisten; the post-init attach assertions now expect the default18521/http://127.0.0.1:18521instead of the wizard-pinned 8787.hypaware-core/smoke/flows/walkthrough_to_first_query.js-goldenConfigdroppedlisten; stale "standard defaults (8787, 4318)" comment corrected.llp/0010-config-model.spec.md- the illustrative config example no longer shows a pinned listen.Deliberately not changed:
config_load_validate.js:50andstatus_diagnostics.jsstill write an explicitlisten: 127.0.0.1:8787in their hand-authored fixtures. Those are not writer output; they exercise the still-supported explicit-listen path, and an explicit port there is the point. Same for the explicit-listen fixtures injoin-command,attach-endpoint-fallback,config-merge, anddaemon-attach-seamtests.Checks
npm test: 2777 pass / 8 fail. The 8 aretest/core/leave-command.test.js, failing identically on unmodifiedorigin/masterin this container (nosystemctlon PATH).npm run typecheck: clean.walkthrough_picker_to_first_queryOK (fails before this change),core_boot_noop,status_diagnostics,client_attach_idempotent,gateway_claude_capture,daemon_foreground_start_stop,walkthrough_backfill_client_history,ai_gateway_passthroughall OK.walkthrough_to_first_query,config_load_validate,claude_attach_detach,client_attach_on_joinfail, but each fails identically on unmodifiedorigin/masterhere (verified by stashing) at an assertion unrelated to the listen port.Fixes#431