Repository navigation
Conversation
…tings page
The channel select offered only `[unset, 3457, 3458]` — production 3456 was filtered
out on the reasoning that choosing the default was the same as leaving the field alone.
That made `port: 3456` unrepresentable: it is the schema default and a legal stored
value, so the select carried a value no option had. The control rendered with nothing
selected and read as broken, which is the reported "the plugin's UI that chooses
dev/stage/prod doesn't work".
Production is now a first-class option and the sentinel is labelled for what it does:
unset — keep the endpoint field (defaults to :3456)
production — role-model (:3456)
stage — role-model-stage (:3457)
development — role-model-dev (:3458)
The unset sentinel stays distinct from a deliberate choice of production, so an
explicit `endpoint` (a remote host) still survives.
Tests: every `RUNTIME_CHANNELS` port must appear in `CHANNEL_OPTIONS`, and the rendered
option list must be `[0, 3456, 3457, 3458]`. Suite: 406 passing; tsc and biome clean.
Pressing Apply could sit on "Applying…" forever with no message and no way back but a page reload. Root cause is on the host side, not in this plugin: the settings write runs inside the loader's exclusive queue (`config-editor` line 153 -> `hmr.runExclusive`), and that queue is an unbounded FIFO (`hmr/src/index.ts:139-147`) — every exclusive operation awaits all queued ones with no deadline. A write issued while another profile mutation is in flight (a plugin install, a reload) is therefore *queued* rather than refused, and its Remote promise does not settle until that operation finishes. Observed live: `package.json.lock` created at the same second as `dsh plugin --profile web add @try-works/dsh-stt@0.2.0`, held for minutes, while the write never landed and the profile still held the old value. The plugin cannot fix the host queue, but it must not hang on it: - `writeConfig` now bounds its wait (default 20s, overridable) and returns a message explaining that the host has not answered and another profile change may be holding the configuration queue. - `onApply` clears `saving` outside the happy path, so a rejected, failed, or timed-out write all return the button to a usable state instead of freezing it. Tests: a write the host accepts but never answers yields a message rather than hanging (RED before the fix: the test timed out). Suite: 407 passing; tsc and biome clean.
Pressing Apply reported `cannot get property "remote" without inject`, and the form
rendered every field empty.
The guard that was supposed to make the Remote optional assumed `ctx.remote` answers
`undefined` when the service is absent. On the real client context it does not: a service
property that is not declared in `inject` **throws**, and `ctx.get` is the lookup that
answers `undefined`. So `settingsRemote(ctx)` threw on both the read and the write path:
- `readConfig` rejected, and because the panel called it as `void readConfig(ctx).then(...)`
with no `catch`, the rejection was silent and the form never seeded — hence the blank
endpoint/selectedAlias/providerRoute/requestTimeoutMs fields.
- `writeConfig` rejected before its own `try`, so Apply surfaced the raw Cordis message.
It only became visible now because the previous change made `onApply` report failures
instead of leaving the button stuck.
`settingsRemote` now reads `ctx.get("remote")` inside a guard, so an absent or
unreadable Remote degrades to "unavailable" rather than throwing. The client half keeps
injecting only `slots`; this is the lookup Cordis provides for services a caller must be
able to do without.
Tests: the client context double is now faithful to Cordis (a throwing `remote` property
plus a working `get`), which reproduces the reported error exactly — six specs failed with
`cannot get property "remote" without inject` before this fix. Suite: 409 passing; tsc and
biome clean.
This branch has not been deployed
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.
Three defects in the role-model settings panel, all reported from live use.
1. The channel select could not represent production
The select offered only
[unset, 3457, 3458]— production 3456 was filtered out, on the reasoning that choosing the default was the same as leaving the field alone. That madeport: 3456unrepresentable, so the<select>carried a value no<option>had and the control rendered with nothing selected.Production is now first-class and the sentinel says what it does:
2. Apply could sit on "Applying…" forever
Host-side root cause: the settings write runs inside the loader's exclusive queue (
config-editor:153->hmr.runExclusive), an unbounded FIFO (hmr/src/index.ts:139-147) with no deadline. A write issued while another profile mutation is in flight is queued rather than refused, and its promise does not settle until that operation finishes. Observed live:package.json.lockcreated the same second asdsh plugin --profile web add @try-works/dsh-stt@0.2.0, held for minutes, profile still holding the old value.The plugin cannot fix the host queue but must not hang on it:
writeConfignow bounds its wait (20s default), andonApplyclearssavingoutside the happy path.3.
cannot get property "remote" without injectThe guard meant to make the Remote optional assumed
ctx.remoteanswersundefinedwhen absent. On the real client context it throws;ctx.getis the lookup that answersundefined. SosettingsRemotethrew on both paths:readConfigrejected, and it was called asvoid readConfig(ctx).then(...)with nocatch, so the failure was silent and the form never seeded — hence the blankendpoint/selectedAlias/providerRoute/requestTimeoutMsfields.writeConfigrejected before its owntry, so Apply surfaced the raw Cordis message. It only became visible because fix 2 madeonApplyreport failures rather than freeze.settingsRemotenow readsctx.get("remote")inside a guard. The client half keeps injecting onlyslots.Note on the control: the Remote is injectable — the harness's own
ui-agent-presetinjects'remote'and'remote.settings'. This half stays onctx.getbecause it must still render when the Remote is missing.Tests
RUNTIME_CHANNELSport must appear inCHANNEL_OPTIONS(RED: "channel 3456 is not selectable")[0, 3456, 3457, 3458], with production/stage/development labelsremoteproperty plus a workingget— which reproduced the reported error exactly: six specs failed withcannot get property "remote" without injectbefore the fixSuite: 409 passing;
tscand biome clean.