OpenWOP openwop.dev
Status: Draft · RFC details
FieldValue
RFC0220
Titlean extension family graduates on evidence a script can read
StatusDraft
Author(s)David Tufts (@davidscotttufts)
Created2026-09-27
Updated2026-09-27: filed Draft. The 7-day comment window runs to 2026-10-04 and is not waived, because §D changes which bundles count as maturity evidence. The mechanism lands with this filing. It only makes check-ext-status-coherence stricter and records new rows, so it changes no status on its own.
Affectsspec/v2/declaration.json + declaration.schema.json (extensionName on the 13 anchor: ext rows; restTransport witness claims-check → witnessable-gated, adoption none → single-witness; a2uiSurface witness claims-check → seam-gated) · spec/v2/core/capabilities.md §3.2 and §6 · spec/v2/core/runs.md §"Caching and encoding" (one key spelling) · spec/v2/ext/README.md (the tier half of Stable; the Note label) · the 13 ext family READMEs and the 3 note READMEs · new evidence/host-tiers.json · scripts/check-ext-status-coherence.mjs, scripts/check-declaration.mjs · conformance: new lib/ext-claims.ts (+ self-test), new scenarios v2-ext-family-claims, v2-ext-rest-transport, one new leg in v2-a2ui-v09-surface (suite 2.42.7)
Compatibilityadditive, plus a Class 3 correction (§A). No wire shape, error code or status changes. §D makes a corpus gate stricter and fail closed.
Supersedes—
Superseded by—

Summary

spec/v2/ext/README.md defines Stable as a predicate over certified evidence, and scripts/check-ext-status-coherence.mjs enforces it. On 2026-09-27 all 17 pages on openwop.dev/spec/v2/ under "Extensions" read Draft, and none could ever become Stable:

  • The predicate needs an executed-pass row under openwop.family.<key>. No scenario records one, for any family, core or ext. gateFamily records only inapplicable or skipped, under openwop.profile.family.<key>.
  • The ext READMEs say to advertise extensions.<org>.<key> with a camelCase key such as restTransport. extensionsKeyPattern is kebab-case, so no host can emit that key. The two hosts that serve a family already use the kebab form: MyndHyve's myndhyve.rest-transport and myndhyve.chat.
  • The README requires a host at evidence tier 2 or better. The checker accepts any certified bundle, including a loopback reference host.
  • Four of the 17 pages are not families. Three are notes, outside the rule; portability is a core family, and #1671 retired its page. The notes still said Draft, which promised a graduation they can never have.

This RFC does four things:

  • names the key (§A);
  • defines the witness for each class of ext family (§B, §C);
  • makes the tier half of the predicate machine-checkable (§D);
  • gives notes their own label (§E).

Motivation

Facts as of origin/main 54588558, 2026-09-27:

  • **No openwop.family.* row in any bundle.** None of the eight committed bundles in evidence/v2-host-bundles/ has one. They carry 153 requirement, 63 scenario, 25 it and 20 floor rows (openwop-workflow-engine), and the same prefixes elsewhere.
  • The advertised keys. app.openwop.dev serves extensions = {openwop-app.host, openwop-app.host-surfaces, openwop-app.ai-providers}. Its host-surface list names host.canvas, host.chat, host.kanban, host.knowledge, host.launchStudio, host.messaging and host.webResearch, but none as an extension record. MyndHyve's v2 document serves myndhyve.chat and myndhyve.rest-transport (conditionalRunGet: true, contentEncodings: ["gzip","br"]), derived by its v2 projection as myndhyve.<kebab(key)>.
  • The a2uiSurface witness exists but is invisible. Its behavioral witness v2-a2ui-v09-surface is executed-pass in two certified bundles, the openwop-app side revision rfc0199 and the loopback reference host, both tier 1. No row ties it to the family.
  • restTransport has behavior but no v2 witness. RFC 0115's behavioral scenario run-transport-economy exists only at major 1, reading the v1 root key.

Proposal

§A. The advertised key

§A.1 An ext family's declaration row carries extensionName, the kebab-case form of its key. A core row MUST NOT carry one (check-declaration.mjs). The family is advertised as extensions["<org>.<extensionName>"] (capabilities.md §3.2), never at the root.

§A.2 (Class 3 correction.) The ext READMEs, capabilities.md §3.2 and §6, and runs.md now use the kebab spelling. The camelCase key they printed matched no key a conforming host could emit, so no conforming host moves. Each README header now agrees with its declaration row by value (witness, technical, adoption) and names its advertised key. check-declaration.mjs checks both.

§A.3 a2uiSurface is the exception. Its contract is admitted through schemaVersions.kinds["ui.a2ui-surface"], and only its deprecated deltaTransport facet is an extensions record.

§B. The claims-check witness for the 11 reservations

A reservation defines no portable operation, so the witness can only check the claim. v2-ext-family-claims records openwop.family.<key> for each of brand, canvas, chat, coordination, dataIntegration, entities, kanban, knowledge, launchStudio, messaging and webResearch:

  • executed-pass when every claim is well formed:

- the key matches extensionsKeyPattern; - the org is registered and not reserved; - the record is an object; - the family key is not also a root member.

  • executed-fail when any claim is malformed.
  • inapplicable when no registered org advertises the family.

A record under an unregistered org is not a claim on the family.

The gate is the claim itself, not behaviorGate. An ext family is outside every profile, so strict mode has nothing to demand of a host that does not serve it.

§C. Behavioral witnesses for restTransport and a2uiSurface

§C.1 restTransport's witness becomes witnessable-gated. spec/v2/ext/restTransport/README.md defines the record's two facets and what the claim adds to runs.md §"Caching and encoding":

  • conditionalRunGet: true turns that section's SHOULD ETag into a MUST on every 200.
  • Each listed coding MUST be produced when it is the only one requested.

v2-ext-rest-transport witnesses both under openwop.family.restTransport.

§C.2 a2uiSurface's witness becomes seam-gated. A new last leg of v2-a2ui-v09-surface records openwop.family.a2uiSurface, with an admitted positive and a refused control in one run.

§D. The tier half of Stable is machine-checked

§D.1 evidence/host-tiers.json lists hosts by discovery origin, with the tier GOVERNANCE.md §"Acceptance evidence tiers" gives each. An unlisted origin never qualifies. Adding a row is a governance act and cites the governance line.

§D.2 check-ext-status-coherence.mjs counts an openwop.family.<key> pass toward Stable only from a bundle that meets both conditions:

  • it certifies a profile;
  • its discovery.url origin is listed at tier 2 or better.

It also enforces these rules:

  • A Stable doc's declaration row says technical: stable, and a Draft doc's does not.
  • A tier-1-only witness is reported as NOT YET, never as graduable.

§E. Notes

A directory under spec/v2/ext/ with no declared family carries Status: Note. and says it is not a declared family. A note is outside the maturity rule and never Stable. A declared family is never a note. The three notes are grpc-transport, provider-idempotency and sandbox-runtime-notes. A retired page with no family (today portability) carries a Superseded by: or Retired by: line.

Compatibility

  • §A is a Class 3 correction. The corrected text named a key that fails the discovery schema, so no host could have conformed to it. Both hosts that serve an ext family already emit the corrected spelling.
  • §B–§C are additive: new scenarios, one new leg, and new rows. A host that advertises no ext family records inapplicable throughout.
  • §D–§E are corpus-gate changes. They fail closed, and no page changes status because of them.
  • No wire change. No schema property, required entry, error code or status moves. declaration.schema.json gains one optional property.

Conformance

  • src/lib/ext-claims.test.ts (self-test, server-free) has nine cases:

- two well-formed claims; - absent, including a camelCase key and an unregistered org; - malformed: a reserved org, an array, a scalar or null record, a root copy of the key; - two orgs classified separately; - the corpus fact that every claims-check family has exactly one leg.

Removing the reserved-org or root-copy check turns its case red. Both sabotages were run.

  • v2-ext-family-claims (major 2): one leg per reservation, recorded under openwop.family.<key>.
  • v2-ext-rest-transport (major 2): conditional GET (a strong ETag, 304 with no body, rotation across a real transition, and the parked ETag not matching the completed run) and the per-coding byte round trip, both under openwop.family.restTransport.
  • v2-a2ui-v09-surface: the new family-witness leg.

Falsifiability — one row per normative requirement

RequirementObservableWho can cause the conditionVerdict
§A.1 kebab extensionName on every ext row, none on corethe declarationthe corpuswitnessable — corpus gate (check-declaration.mjs)
§A.2 README header agrees with its row and names its keythe README and the rowthe corpuswitnessable — corpus gate (check-declaration.mjs)
§B a claim is well formedthe v2 discovery extensions objectthe hostwitnessable — gated on the claim (openwop.family.<key>)
§C.1 conditionalRunGet: true ⇒ an ETag on every run-snapshot 200, and each listed coding is producedresponse headers and bytes of GET /runs/{runId}the suite, on the conformance-approval and conformance-noop fixtureswitnessable — gated on the claim
§C.2 the a2ui family legthe emit-surface seam's answersthe suite, through the seamwitnessable — seam-gated
§D a Stable label rests on a tier-2+ certified rowbundles and host-tiers.jsonthe corpuswitnessable — corpus gate (check-ext-status-coherence.mjs)
§E a note is labeled Note, and a family never isthe READMEsthe corpuswitnessable — corpus gate (check-ext-status-coherence.mjs)

Alternatives considered

  1. Accept tier-1 evidence for ext families. openwop-app is tier 1 and already serves 7 of the 11 reservations as host surfaces. Rejected: the README's tier-2 bar is the only thing separating Stable from "the steward says so". Lowering it for the families least able to show interoperation would invert the rule.
  2. A per-family witnessIds list in the declaration (count an existing row such as openwop.scenario.v2-a2ui-v09-surface). This would graduate on bundles already committed, with no re-cut. Rejected: it names a second vocabulary for the same fact, and a scenario row folds every leg, so one unrelated leg could deny or grant the family.
  3. Write a portable contract for each reservation first (for example chat's operations). This is the honest route to interoperation. It is deferred rather than rejected: each is its own RFC, and a reservation's Stable is defined narrowly in ext/README.md so the label does not overclaim in the meantime.
  4. Retire the reservations nobody serves. Not needed: MyndHyve, the tier-2 host, implements all 11 (Implementation notes).

Unresolved questions

  1. chat is the reservation with the clearest cross-host demand. Does it get a portable contract (an RFC of its own) before or after its claims-check graduation?
  2. MyndHyve serves canvas, kanban, brand and entities from canvas-runtime, an origin that does no OpenWOP version negotiation. A reservation promises no operation, so advertising it from the v2 document promises nothing that origin must honour. Whether MyndHyve records that in the record body is its decision.

Implementation notes (non-normative)

  • MyndHyve's v2 projection (services/workflow-runtime/src/routes/discoveryV2.ts) already re-homes a non-core v1 root key to extensions["myndhyve.<kebab(key)>"]. Its v2 document drops schemaVersions.kinds, so a2uiSurface's floor never reaches a v2 reader. It also serves no emit-surface seam, and its seams profile is deliberately off.
  • openwop-app lists the families as host-surfaces entries, not extensions records.

Acceptance criteria

  • [x] Filed: the key, the witnesses, the tier table and the notes, with the self-test sabotage-proved.
  • [ ] Active: the comment window closes (2026-10-04) with no unresolved objection, and suite 2.42.7 is published.
  • [ ] Per family, Draft → Stable (a separate promotion PR with its own 7-day window): check-ext-status-coherence reports the family GRADUABLE from a committed, certified MyndHyve bundle cut against the DEPLOYED revision.

- Expected first: restTransport, chat, and the other reservations once MyndHyve advertises them. - a2uiSurface waits on MyndHyve's seam (gap G1).

  • [ ] Accepted: every row above witnessed. The §B/§C host rows are executed-pass on a tier-2 certified bundle.

References

  • spec/v2/ext/README.md; RFC 0174 (governance predicates); RFC 0177 (the extension tail); RFC 0169 §B–§C (the declaration file, witness classes); RFC 0144 (extension-class families); RFC 0115 (run transport economy); RFC 0209 (A2UI v0.9 surfaces).
  • GOVERNANCE.md §"Acceptance evidence tiers".