Summary
Two connection-pack rules are MUSTs that no party can observe on a production host. A second registration of a bare provider id must be refused with connection_provider_conflict, and a qualified reference <packName>#<id> must resolve only to the named pack. Installing a pack is not a protocol operation, so the suite reaches both rules only through the RFC 0095 test seams, and conformance.md §Witness class says a MUST whose only witness is a seam must get a normative observation path or be demoted. This RFC mints the path: an optional read of the host's provider registry and of the registrations it refused, plus a resolve read for one reference, behind connections.providerRead. A host causes the conflict operator-side by installing a declared conformance fixture pair at deploy, and the suite reads the outcome.
Motivation
The corpus breaks its own witness rule. spec/v2/core/conformance.md §Witness class: "A MUST whose only witness is seam-gated MUST either mint a normative observation path before the cut or be demoted to SHOULD." connection-packs.md §Provider identity states two MUSTs (refuse a second claimant; never choose by version) and §The qualified form states how a qualified reference resolves. v2-provider-conflict witnesses them only through POST /conformance/seams/sample/connection-packs/{install,resolve}. packs.md names the publication paths that install a pack (the canonical registry, a vendor registry's write API, a mirror ingest), and none is an operation on a host's protocol surface. The connections family is declared witnessable-gated, which is not true of these rules.
It now blocks a production certification. openwop-app declared connections at major 2 (its ADR 0809), and its seam-free production cut on suite 2.45.11 (build 451a665e8) records three blocked rows from v2-provider-conflict. A bundle with a blocked row certifies nothing (RFC 0168 §E.1). The host implements the rule and passes the scenario in-process with seams on (its session's report, 2026-10-03). The same cut is the evidence RFC 0230 and RFC 0232 wait on.
The alternatives were considered and rejected by the maintainer (2026-10-03). Withdrawing the advert would under-state behaviour the host honours. Recording a seam-only leg inapplicable on a seam-free host would let any host advertise packsSupported unchecked. Demoting the MUSTs to SHOULD would weaken a rule that stops two packs from silently claiming one provider.
Proposal
§A. The facet
connections gains one optional member:
"connections": { "packsSupported": true, "providerRead": true }
providerRead: truemeans the host serves §B and §C.- A host advertises it only with
packsSupported: true.
§B. The registry read
GET /connection-providers (listConnectionProviders), v2 only.
- A host advertising
connections.providerReadMUST serve it. A host that does not answers404 not_found. - Host-global. The provider registry is one per host (
connection-packs.md§Provider identity). The read carries no tenant data and is the same for every caller. Scope:manifest:read. - The page lists every provider definition the host resolves against, built-ins included:
| Field | Type | Meaning |
|---|---|---|
id | string | the bare provider id |
source | builtin | pack | where the definition comes from |
packName | string, when source: pack | the installed pack that defines it |
- Uniqueness is observable. Each bare
idMUST appear at most once. - The page also lists
refusals, the registrations the host refused under §Provider identity:
| Field | Type | Meaning |
|---|---|---|
packName | string | the pack whose registration was refused |
providerId | string | the bare id it claimed or referenced |
code | connection_provider_conflict | connection_provider_unresolved | why |
heldBy | string, for a conflict | the packName holding the id, or builtin |
- Content-free. A row carries ids, pack names and codes only: no endpoint URLs, no scope catalog, no credential material.
- Lifetime.
refusalsreflects the installs the serving process attempted. A host MAY recompute it at every start and need not store it durably: the outcome is a function of the installed pack set.
§C. The resolve read
GET /connection-providers/{providerId} (resolveConnectionProvider), v2 only, scope manifest:read. The optional query parameter pack names a pack: ?pack=<packName> is the qualified form <packName>#<providerId>. The # never travels on the wire, because a front door that decodes %23 would truncate the path.
- Without
pack, the bare id resolves only when exactly one definition exists. Withpack, it resolves only to that pack's definition. - Resolved:
200with one §B.3 row. - Not resolvable:
404withconnection_provider_unresolved. A qualified reference to a pack that does not define the id, or is not installed, is not resolvable.
§D. The conformance fixture pair
conformance/fixtures/connection-packs/ gains connection-pack-acme-widgets-rival.json: a well-formed pack named core.openwop.connections.acme-widgets-rival that also defines bare acme-widgets.
- An operator MAY install
connection-pack-acme-widgets(core.openwop.connections.acme-widgets) through the host's normal install path. A host that has it installed MUST also have attempted the rival after it, through the same path, before it serves §B, so a read never shows a half-installed pair (openwop-app's review, 2026-10-03). - The registry is then self-describing:
acme-widgetsonce,source: pack, held by the first pack, and arefusalsrow foracme-widgetswithconnection_provider_conflictandheldBythe first pack. - The pair is not advertised in discovery
fixtures: those ids name workflow fixtures, whichv2-advertised-fixtures-existcreates runs of (RFC 0168). - The provider is fictional and has no endpoints anyone calls.
§E. Prose
connection-packs.md gains a short §Observation: the facet, the two reads, and §B.4–§B.7 and §C as rules. The kernel budget is the constraint; the schema carries the field tables.
§F. Conformance
v2-provider-conflict gains a normative-surface path, run when the host advertises connections.providerRead. It runs alongside the seam path where both are offered, as trigger-bridge-delivery does since 2.45.8.
- fail-closed (
openwop.requirement.0177.provider-conflict.fail-closed): with the §D fixture installed, the registry listsacme-widgetsexactly once, held by the first pack, andrefusalscarries aconnection_provider_conflictrow foracme-widgetsheld by it. - qualified-form (
openwop.requirement.0177.provider-conflict.qualified-form):acme-widgets?pack=<first>resolves withsource: pack;acme-widgets?pack=<rival>answers404 connection_provider_unresolved; bareacme-widgetsresolves to the first pack. - uniqueness (new id
openwop.requirement.0233.provider-registry.unique): the page is schema-valid and no bare id appears twice. It runs on any host with the facet.
Dispositions (§Decisions 2): no packsSupported, or no providerRead and no seam ⇒ inapplicable. providerRead without the §D fixture installed ⇒ legs 1–2 inapplicable, leg 3 still runs. A host advertising providerRead that does not serve it ⇒ the leg fails. A host advertising packsSupported SHOULD advertise providerRead.
Falsifiability — one row per normative requirement
| Requirement | Observable — what an outside party sees | Who can cause the condition | Verdict |
|---|---|---|---|
§B.1, §B.4, §B.6 a host advertising the facet serves a schema-valid, content-free registry with each bare id once — openwop.requirement.0233.provider-registry.unique | 200, the closed page schema, no duplicate id | the suite, unaided | witnessable — executed-pass required on a host bundle |
§B.5 a refused registration appears with its code — RFC 0177's openwop.requirement.0177.provider-conflict (leg .fail-closed) | the refusals row for acme-widgets held by the first fixture pack, and the id listed once | the operator, by installing the §D fixture pair | witnessable — executed-pass required on a host bundle |
§C.1, §C.3 a qualified reference resolves only to the named pack; an unresolvable one is 404 connection_provider_unresolved — RFC 0177's openwop.requirement.0177.provider-conflict (leg .qualified-form) | the resolve answers for both fixture packs and for the bare id | the operator, by installing the §D fixture pair | witnessable — executed-pass required on a host bundle |
| §B.7 refusals reflect the serving process's installs | none from outside: a restart is not observable | nobody, from outside | unwitnessable — a process boundary is not on the wire (MAY, not a MUST) |
The §B.5 and §C rows name RFC 0177's parent id, not its two legs. This RFC makes that requirement witnessable without a seam; it is not a new one. Naming the legs would declare them as requirements of their own, and check-accepted-predicate would then stop counting them toward RFC 0177. Acceptance still needs both legs executed-pass (§Acceptance criteria).
Compatibility
additive.
- Two optional v2 reads behind one optional facet, one new fixture, one new requirement id.
- No existing field, endpoint, error code or rule changes.
connection_provider_conflictandconnection_provider_unresolvedkeep their meanings and statuses. - The reads are v2 only. v1 resolves a collision by version precedence (
spec/v1/connection-packs.md), so the v1 rule is different and gets no read here.
Alternatives considered
- Demote the two MUSTs to SHOULD (
conformance.md§Witness class allows it). Fast. Rejected by the maintainer: it weakens the rule that stops two packs from claiming one provider. - Record the seam-only legs
inapplicableon a seam-free host. Rejected by the maintainer: any host could advertisepacksSupportedand never be checked. - A dry-run install endpoint (
POST /connection-packs:validateanswers what an install would do). The suite could cause the conflict unaided, which is stronger. But it observes a validation function, not the install path, and the two can diverge; it is also a write-shaped surface. Deferred (§Decisions 3). - An install endpoint on the protocol surface. Makes installation a protocol operation, with the signing, supply-chain and authorization questions
packs.mdsettles for registries. Out of scope. - Advertise the fixture pair as a discovery
fixturesid. Rejected in review:v2-advertised-fixtures-existwould try to run it as a workflow.
Decisions
An /architect review decided these on 2026-10-03, under the maintainer's directive to settle design questions that way. None remain open.
- Scope:
manifest:read. The registry is host-installed definitions with no tenant data, so it reads like the manifest reads. No new scope widens the authorization vocabulary. Notpacks:read, which is the registry-side tarball read. - Without the gate, a leg is
inapplicable.conformance.mddefineswitnessable-gatedas "observed when the host advertises the gating capability"; here the gate isproviderRead. This matches RFC 0232's attempt leg at major 2. To keep the gap visible rather than silent, a host advertisingpacksSupportedSHOULD advertiseproviderRead(risk R4). - No dry-run endpoint now (gap G4 stays externally gated).
refusalscovers pack registrations only. Connectors register through a different surface (RFC 0045).- Wire shape.
- Two reads: the list witnesses uniqueness and refusals; the resolve witnesses resolution. - The qualified form is ?pack=, never a # in the path. - providerRead is boolean, like packsSupported. - No paging: the set is bounded by the installed packs. - The fixture pair is self-describing in the registry, not a discovery fixtures id (§D.3). - The seam path stays as a second path.
- The path is
/connection-providers, not/connections/providers.check-manifest-top-level-segmentsshowedconnectionswould be a new top-level name, and openwop-app already serves/connectionsunversioned as a browser page and OAuth return path.versioning.md§5 would then flip header-less requests on that name to the protocol at v1 end-of-support.connection-providersis unused by every matrix host (checked in openwop-app, openwop-examples and MyndHyve source, 2026-10-03).
Implementation notes (non-normative)
- openwop-app (its session, 2026-10-03): both install paths already compute refusals (
loadConnectionPacks()returns{ installed[], errors[] }with{ pack, code, details: { provider, definedBy } }), and the registry records each provider's owning pack. The work is keeping the outcome instead of dropping it, and serving it read-only. An in-memory, per-process record is enough (§B.7). - Sequencing. Draft → Active with the schema, facet, reads and prose; the scenario path and its scratch-double proof in the same suite release; openwop-app serves the reads and installs the fixture pair; then its certified production cut.
Acceptance criteria
- [x]
Active: the questions are decided (§Decisions). The comment window was waived by STEWARD OVERRIDE of RFC 0147 §A.6 on 2026-10-03, and the RFC 0156 §B review is owed. - [x] The facet is in the v2 declaration; both reads are in
api/v2/openapi.yaml; the page schema validates inspec-corpus-validity; the fixture is catalogued infixtures.md;connection-packs.md§Observation is merged;CHANGELOG.mdrecords it. - [x]
v2-provider-conflict's normative-surface path ships (#1877, suite 2.45.12; proven againstlib/provider-registry-double.tsand a standalone double), each row failing on its sabotage: a registry listing one id twice, a missing refusal, a qualified reference resolving to the wrong pack, a rival reference that resolves, a row carrying an endpoint URL. - [x]
Accepted(provisional; the RFC 0156 §B review is owed): a host advertisingconnections.providerRead, with the §D fixture installed, recordsopenwop.requirement.0177.provider-conflict.fail-closed,openwop.requirement.0177.provider-conflict.qualified-formandopenwop.requirement.0233.provider-registry.uniqueexecuted-passon a certified production bundle with no test seams served. (openwop-app's certified production major-2 bundle (build983976bbc, suite 2.45.12, sha2566908ebc7d168…, signedopenwop-app-bundle-2, strict mode, no test seams served;evidence/v2-host-bundles/openwop-workflow-engine.json).)
References
spec/v2/core/conformance.md§Witness class;spec/v2/core/connection-packs.md§Provider identity, §The qualified form;spec/v2/core/packs.md(publication paths);spec/v1/host-sample-test-seams.md§10.- RFC 0095 (connection packs), RFC 0177 §D.1 (fail-closed provider identity), RFC 0003 (fixture advertisement), RFC 0168 §E.1 (a blocked row denies certification), RFC 0232 (the observation-path pattern this follows).
conformance/src/scenarios/v2-provider-conflict.test.ts; openwop-app's 2.45.11 production cut (build451a665e8), threeblockedrows.- Registers:
gaps,risks.