diff --git a/ide/src/connectionEditorWebview.ts b/ide/src/connectionEditorWebview.ts
index d83cdca26c..235d9c9393 100644
--- a/ide/src/connectionEditorWebview.ts
+++ b/ide/src/connectionEditorWebview.ts
@@ -296,9 +296,12 @@ export function connectionEditorScript(token: string, p: ConnectionEditorScriptI
// One entry per message its hosts post: at least connectionEditor.ts and configEditors.ts use
// this page. Both post "error"; only connectionEditor.ts answers with "fields". A field is a
// FieldDescriptor from connectionForm.ts: its declared fields with their types. The two
- // unknown-typed values (value, defaultValue) and the carried-through envDefault and
- // preserveValue are not typed there either. choices is required and may be null; envKey, cast
- // and a group's description may be absent and are never null.
+ // unknown-typed values (value, defaultValue, envDefault, preserveValue) are not typed there
+ // either, and their presence is not checked: all four are optional there, and FieldDescriptor
+ // says why value and defaultValue can be absent, so a presence check would discard ordinary
+ // "fields" messages. This page does not read envDefault or preserveValue at all. choices is
+ // required and may be null; envKey, cast and a group's description may be absent and are never
+ // null.
// control and type are closed sets: the lists FieldControl and ParamType are derived from.
const FIELD_CONTROLS = ${embedJson(FIELD_CONTROLS)};
const PARAM_TYPES = ${embedJson(PARAM_TYPES)};
diff --git a/ide/src/connectionForm.ts b/ide/src/connectionForm.ts
index 613ec667a8..a2da84edd4 100644
--- a/ide/src/connectionForm.ts
+++ b/ide/src/connectionForm.ts
@@ -152,10 +152,19 @@ export interface FieldDescriptor {
choices: unknown[] | null;
/** The default, as display text for a hint. "" when the setting has no default. */
placeholder: string;
- /** The default, raw (a checkbox needs its unset position). RENDER-ONLY — never post it. */
- defaultValue: unknown;
- /** The record's literal value, or undefined when the record does not set this key. */
- value: unknown;
+ /**
+ * The default, raw (a checkbox needs its unset position). RENDER-ONLY — never post it. Optional
+ * because it can be undefined (at least for a key the schema does not describe, and for a schema
+ * param that carries no `default`), and posting the descriptor to the webview serializes it as
+ * JSON, which drops an undefined member: the page can receive a field with no `defaultValue`.
+ */
+ defaultValue?: unknown;
+ /**
+ * The record's literal value. Undefined at least when the record does not set this key, when the
+ * key is a secret, and when it holds an env reference -- so undefined does not mean "unset". Optional
+ * for the same reason as `defaultValue`: an undefined value is absent from what the webview receives.
+ */
+ value?: unknown;
isEnvRef: boolean;
/** The referenced environment key, when `isEnvRef`. */
envKey?: string;
diff --git a/ide/src/test/suite/webview-receivers.test.ts b/ide/src/test/suite/webview-receivers.test.ts
index 2baa00927d..7e4b5b4895 100644
--- a/ide/src/test/suite/webview-receivers.test.ts
+++ b/ide/src/test/suite/webview-receivers.test.ts
@@ -376,12 +376,22 @@ const SCHEMA: ConnectionSchema = {
const FIELDS_OK = {
command: "fields",
groups: buildForm(SCHEMA, "mllp", "outbound", {
- host: { env: "PEER_HOST", cast: "str" },
+ host: { env: "PEER_HOST", cast: "str", default: "localhost" },
port: 6661,
+ // An inline secret literal, so one field carries preserveValue.
+ tls_key_password: "x",
legacy_key: "kept",
}),
};
+/** FIELDS_OK with no field carrying `value` or `defaultValue`: both are optional (FieldDescriptor). */
+const FIELDS_BARE = variant(FIELDS_OK, (c) => {
+ for (const f of c.groups.flatMap((g: Payload) => g.fields)) {
+ delete f.value;
+ delete f.defaultValue;
+ }
+});
+
const connection: Receiver = {
panel: "connection editor",
key: "command",
@@ -407,7 +417,7 @@ const connection: Receiver = {
${SENTINEL}
`,
),
- wellFormed: { error: [ERROR_OK], fields: [FIELDS_OK] },
+ wellFormed: { error: [ERROR_OK], fields: [FIELDS_OK, FIELDS_BARE] },
malformed: {
error: badMessages(ERROR_OK),
fields: [
@@ -814,6 +824,11 @@ suite("webview receivers discard a malformed payload and render a well-formed on
const fields = FIELDS_OK.groups.flatMap((g) => g.fields);
assert.ok(fields.some((f) => typeof f.envKey === "string" && typeof f.cast === "string"));
assert.ok(fields.some((f) => !("envKey" in f) && !("cast" in f)));
+ const bare = FIELDS_BARE.groups.flatMap((g: Payload) => g.fields);
+ assert.ok(fields.some((f) => f.value !== undefined) && fields.some((f) => f.defaultValue !== undefined));
+ assert.ok(fields.some((f) => "envDefault" in f) && fields.some((f) => !("envDefault" in f)));
+ assert.ok(fields.some((f) => "preserveValue" in f) && fields.some((f) => !("preserveValue" in f)));
+ assert.ok(bare.length > 0 && bare.every((f: Payload) => !("value" in f) && !("defaultValue" in f)));
assert.ok(FIELDS_OK.groups.some((g) => typeof g.description === "string"), "no group has a description");
assert.ok(FIELDS_OK.groups.some((g) => !("description" in g)));
assert.ok(fields.some((f) => f.choices === null) && fields.some((f) => Array.isArray(f.choices)));
diff --git a/messagefoundry/__main__.py b/messagefoundry/__main__.py
index 9e9ba60159..d77a5db8bb 100644
--- a/messagefoundry/__main__.py
+++ b/messagefoundry/__main__.py
@@ -206,9 +206,10 @@ def _refuse_toolkit_command(command: str, argv: Sequence[str]) -> int:
``_emit_error`` writes JSON to stdout or one line to stderr, never both, and its own return value
is 1, so it is not returned. ``argv_wants_json`` is the one pre-parse JSON rule both commands use.
- The line names no install command. The toolkit distribution is not yet on PyPI, and
- tests/test_install_instruction_provenance.py refuses shipped text that tells a user to install
- an unclaimed name.
+ The line names no install command. It was written while the toolkit name was unclaimed on PyPI,
+ when tests/test_install_instruction_provenance.py refused shipped text telling a user to
+ install it by name. ADR 0201 slice 3 recorded the name as claimed; the docs that give a
+ bare-name install arrive with slice 4.
"""
_emit_error(
f"`{command}` is not a messagefoundry command. It is an authoring command: run it as "
diff --git a/tests/test_install_instruction_provenance.py b/tests/test_install_instruction_provenance.py
index 1f9074cf24..636e11cc62 100644
--- a/tests/test_install_instruction_provenance.py
+++ b/tests/test_install_instruction_provenance.py
@@ -41,27 +41,36 @@
#: Distributions this repository builds that are **not yet published to any index**. Until a name is
#: claimed, no shipped text may tell a user to install it by bare name.
#:
-#: **`messagefoundry-toolkit` is here from ADR 0201 slice 2 until its first upload claims the name.**
-#: The other three distributions this repo builds are registered on PyPI (`messagefoundry`,
-#: `messagefoundry-harness`, `messagefoundry-webconsole` -- the last claimed 2026-07-29 by the first
-#: `webconsole-v*` release, closing the ASVS 15.2.4 exposure where README.md instructed an index
-#: install of an unclaimed name). ADR 0201 slice 3 moves the toolkit to `_PUBLISHED_DISTRIBUTIONS`
-#: after a probe of the first tag's JSON API URL, with the harness as a control, shows the upload
-#: landed. An empty set is the goal state, not a disabled guard.
+#: Every distribution this repo builds is registered on PyPI, so the set is empty. An empty set
+#: is the goal state, not a disabled guard. `messagefoundry-webconsole` was claimed 2026-07-29 by the
+#: first `webconsole-v*` release, closing the ASVS 15.2.4 exposure where README.md instructed an index
+#: install of an unclaimed name.
#:
-#: This matches an install VERB only, so it cannot see text that names the toolkit without one. ADR
-#: 0201 section 1 does not rely on it to close the window: the release job uploads the toolkit before
-#: the engine, so no engine naming the toolkit reaches PyPI while the name is unclaimed.
+#: This matches an install VERB only, so it cannot see text that names a listed distribution without
+#: one, such as a `Requires-Dist` line or prose saying a package depends on it.
#:
#: The set exists for the NEXT distribution: add its name here the moment `packaging//` lands and
#: before any doc references it, then remove it once the first release claims the name.
#: `test_a_new_distribution_must_be_classified` fails if a packaged distribution is neither listed here
#: nor demonstrably published, so this cannot silently rot back to vacuous.
-_UNPUBLISHED_DISTRIBUTIONS: frozenset[str] = frozenset({"messagefoundry-toolkit"})
+_UNPUBLISHED_DISTRIBUTIONS: frozenset[str] = frozenset()
#: Distribution names registered on PyPI and published by this repo's release workflow. Listing one
#: here asserts the name is CLAIMED -- the property that makes a bare-name install instruction safe.
-_PUBLISHED_DISTRIBUTIONS = frozenset({"messagefoundry-harness", "messagefoundry-webconsole"})
+#:
+#: **`messagefoundry-toolkit` moved here in ADR 0201 slice 3**, from `_UNPUBLISHED_DISTRIBUTIONS`,
+#: where slice 2 put it. The probe, read 2026-10-27: PyPI's JSON API returned 200 for
+#: `/pypi/messagefoundry-toolkit/json`, `/pypi/messagefoundry-toolkit/0.5.0/json` and
+#: `/pypi/messagefoundry-toolkit/0.5.1/json`, and for the harness control
+#: `/pypi/messagefoundry-harness/0.5.1/json`; it returned 404 for the controls
+#: `/pypi/messagefoundry-toolkit/0.0.0.dev999/json` and an unregistered project name. The project
+#: lists releases 0.5.0, 0.5.1 and 0.5.2, and each wheel's provenance names publisher
+#: `MEFORORG/MessageFoundry`, workflow `release.yml`. So every release on the project is this repo's
+#: upload. That is a reading on that date, not something this test re-checks: no test here reaches
+#: the network.
+_PUBLISHED_DISTRIBUTIONS = frozenset(
+ {"messagefoundry-harness", "messagefoundry-toolkit", "messagefoundry-webconsole"}
+)
#: Files whose install commands are shipped to, or executed by, someone other than a maintainer.
#: CI workflows and internal handoffs are excluded deliberately: they install from the source tree by