Skip to content

fix: enforce the id rules, correct four contract claims, and fix two plugin behaviours - #100

Merged
rmyndharis merged 2 commits into
mainfrom
fix/contract-and-manifest-consistency
Aug 12, 2026
Merged

fix: enforce the id rules, correct four contract claims, and fix two plugin behaviours#100
rmyndharis merged 2 commits into
mainfrom
fix/contract-and-manifest-consistency

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

A batch of smaller corrections, grouped so each is reviewable on its own commit.

Catalogue gates the standard already asked for

PLUGIN-STANDARD gives plugin ids a shape (/^[a-z0-9][a-z0-9._-]*$/i) and a reserved list. Nothing checked either. Both are now enforced from the standard's own wording — a paraphrase would drift from it. Proven: an id of -bad and an id of baileys are each refused by name.

Four claims that did not match the corpus

  • sdkVersion was the string "1" in four manifests while the standard writes it as a number.
  • The contract described net.allow as "host:port"; most entries are a bare host, which matches any port. The comment now describes both, because both are in use.
  • The contract header gives one alignment version while later comments describe newer host behaviour it has not been re-verified against. It now distinguishes the two.
  • chatwoot-adapter's ingress comment read as though its returned status reaches Chatwoot. The host ignores it and computes the reply from the manifest; the value really controls retry. The numbers stay, the comment stops overclaiming.

The i18n check now records its scope: it compares field keys, which carry title. No plugin translates a field description, so requiring it would fail every locale at once.

Two plugin behaviours

gsheets-logger reported healthy whenever a client existed, so a spreadsheet that had stopped accepting appends showed green while rows piled up. healthCheck now reports unhealthy while a flush is failing, and names the last error.

after-hours rejected any window whose open was not before its close, so 22:00-06:00 and 00:00-00:00 were refused — and since the parser throws, one such day made the whole schedule unparseable. The comparison now understands a wrapped window: open late and early, closed in between. Verified across six clock positions.

Verification

  • 586 tests pass, typecheck clean, catalog up to date.

…orrect four contract claims

PLUGIN-STANDARD gives plugin ids a shape and a reserved list. Nothing checked either, so a plugin could
take an id the standard forbids and the catalogue would publish it. Both are now enforced from the
standard's own wording rather than a paraphrase.

Four claims that did not match the corpus:

- `sdkVersion` was sent as the string "1" by four manifests while the standard writes it as a number.
- The contract described `net.allow` as "host:port" entries; most of the catalogue uses a bare host,
  which matches any port. The comment now describes both, because both are in use.
- The contract header gave one alignment version while later comments describe newer host behaviour it
  has not been re-verified against. It now says which is which.
- chatwoot-adapter's ingress comment read as though its returned status reaches Chatwoot. The host
  ignores it and computes the provider's reply from the manifest; what the value really controls is
  retry. The numbers stay, the comment no longer claims more than they do.

The i18n check now records its scope: it compares field keys, which carry `title`. No plugin translates
a field `description`, so requiring it would fail every locale at once — the guarantee is titles.
gsheets-logger reported healthy whenever a client existed. A spreadsheet that had stopped accepting
appends therefore showed a green tile while rows piled up in the buffer — the one state an operator has
to see. healthCheck now reports unhealthy while a flush is failing and names the last error.

after-hours rejected any window whose open was not before its close, so "22:00-06:00" and "00:00-00:00"
were refused — and because the parser throws, one such day made the entire schedule unparseable. Both
are ordinary business hours. The comparison now understands a wrapped window: open late and early,
closed in between. A window whose open equals its close is still refused, with the all-day spelling
named in the message.
@rmyndharis
rmyndharis merged commit 1bd0740 into main Aug 12, 2026
1 check passed
@rmyndharis
rmyndharis deleted the fix/contract-and-manifest-consistency branch August 12, 2026 11:10
rmyndharis added a commit that referenced this pull request Aug 15, 2026
… OpenWA 0.19.0 (#102)

The 2026-08-12 audit's G-44 flagged that four manifests sent sdkVersion as the string "1"
while PLUGIN-STANDARD.md spelled it as the number 1, and PR #100 aligned the manifests to
the standard without checking which side the host agrees with. The host reads the field as
a string: its ingress validation runs sdkVersion.split('.'), so a manifest carrying the
number throws at load and the plugin comes up ERROR. chatwoot-adapter and supabase-otp-hook
declare ingress routes, so the throw is theirs on every host since 0.7.18 — a load failure,
not a degraded mode. http-action and typebot-connector declare no ingress, so the host never
reads the field for them today; theirs was a latent violation of the same contract. No
released zip carried the number (the tags predate the change), so nothing was broken in the
wild — this closes the gap before a release can ship it.

The standard now spells it "1" the way the host types it, and the catalogue gate refuses a
manifest whose sdkVersion is not a string like "1" or "1.2" — the same class of check as the
id rules, in the same gate. It earned its place immediately: it caught the two no-ingress
manifests the host validator cannot reach, because validateIngressManifest returns early
when a manifest declares no routes. The vendored contract gains sdkVersion and the typed
ingress route (PluginIngressRoute, IngressSignatureSpec, IngressResponseContract), shipped
by the host since 0.7.18 and silently omitted by every earlier alignment; a manifest field
the vendored types don't carry is a field nothing checks.

Compatibility with OpenWA 0.19.0 was verified by running every manifest and entry point
through the host's own load-time validators — the same validatePluginManifest,
validateIngressManifest and main-entry containment the boot scanner and the installer run.
All ten pass, all ten bundles build and load under the loader contract, and the 0.19
breaking changes (API master-key length, removed REST endpoints, the plain-http install
pin) touch no surface a plugin binds. testedOpenWAVersion moves 0.14.0 -> 0.19.0 across
the catalog, with the verification method recorded in each changelog; minOpenWAVersion
floors are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant