Skip to content

docs: add Fusor adapter authoring guide - #233

Open
Tom Tang (qwerzl) wants to merge 1 commit into
mainfrom
agent/add-fusor-adapter-guide
Open

docs: add Fusor adapter authoring guide#233
Tom Tang (qwerzl) wants to merge 1 commit into
mainfrom
agent/add-fusor-adapter-guide

Conversation

@qwerzl

@qwerzl Tom Tang (qwerzl) commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • add a standalone guide for building external Fusor-backed Spectrum adapters
  • use Telegram as the reference for inbound Fusor delivery and direct outbound API calls
  • document current definePlatform, verification, mapping, send, lifecycle, testing, and webhook-registration patterns
  • update CONTRIBUTING.md to replace retired provider API names and link the new guide

Why

The existing contributor checklist referenced retired APIs such as space.resolve, events.messages, and actions.send. External adapter authors also lacked a complete, current walkthrough of the Fusor provider contract and its security and delivery semantics.

Impact

Documentation only. No runtime or public API behavior changes.

Validation

  • bun run check
  • type-checked the guide's consolidated example against the current workspace API
  • git diff --cached --check
  • verified all new repository-relative Markdown links resolve

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Note

Low Risk
Markdown-only changes; no runtime or public API behavior is modified.

Overview
Adds FUSOR_ADAPTERS.md, a end-to-end guide for publishing external Fusor-backed Spectrum adapters (Telegram as the reference: Fusor inbound, direct platform API outbound). It documents the current provider surface—definePlatform, fusor + verify, typed FusorMessages, top-level send, space.create, lifecycle/webhook registration—and covers security (sign rawBody, replay windows), delivery semantics (app.messages vs app.webhook()), testing, and common pitfalls including retired APIs.

CONTRIBUTING.md is updated to link that guide for webhook-driven external packages and to replace the outdated minimum contract (space.resolve, events.messages, actions.send) with user.resolve, space.create, top-level messages / send, and optional lifecycle.destroyClient.

Reviewed by Cursor Bugbot for commit 56594d7. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Documentation
    • Expanded contribution guidance for platform providers, including updated minimum provider capabilities and links to adapter resources.
    • Added a comprehensive guide for building, testing, and publishing external Fusor-backed Spectrum adapters.
    • Documented configuration, payload validation, request verification, message handling, user and space resolution, outbound delivery, webhook setup, and publishing checks.
    • Included common pitfalls and reference implementation guidance for adapter developers.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a comprehensive guide for building external Fusor-backed Spectrum adapters. It also updates contribution guidance to use the current provider contract, including user.resolve, space.create, messages, and send.

Changes

Fusor adapter authoring

Layer / File(s) Summary
Adapter foundations and provider contract
CONTRIBUTING.md, FUSOR_ADAPTERS.md
Documents adapter architecture, platform identifiers, package structure, configuration, dependencies, and the updated minimum provider interface.
Inbound validation and message mapping
FUSOR_ADAPTERS.md
Documents payload schemas, raw-body HMAC verification, replay protection, challenge handling, and inbound message mapping.
Resolution, dispatch, and provider wiring
FUSOR_ADAPTERS.md
Documents user and space resolution, outbound text and control operations, client lifecycle handling, provider construction, and registration.
Webhook operations and publication checks
FUSOR_ADAPTERS.md
Documents webhook registration, delivery semantics, testing requirements, common mistakes, publication checks, and references.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: underthestars-zhy

Poem

A rabbit hops through adapter code,
With schemas checked and secrets stowed.
Messages map and webhooks sing,
Providers send each useful thing.
The guide is packed and ready to go.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a guide for authoring Fusor adapters.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/add-fusor-adapter-guide

Comment @coderabbitai help to get the list of available commands.

@qwerzl
Tom Tang (qwerzl) marked this pull request as ready for review August 6, 2026 05:12
Copilot AI review requested due to automatic review settings August 6, 2026 05:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pkg-pr-new

pkg-pr-new Bot commented Aug 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

@spectrum-ts/core

npm i https://pkg.pr.new/@spectrum-ts/core@233

@spectrum-ts/elysia

npm i https://pkg.pr.new/@spectrum-ts/elysia@233

@spectrum-ts/express

npm i https://pkg.pr.new/@spectrum-ts/express@233

@spectrum-ts/fastify

npm i https://pkg.pr.new/@spectrum-ts/fastify@233

@spectrum-ts/hono

npm i https://pkg.pr.new/@spectrum-ts/hono@233

@spectrum-ts/imessage

npm i https://pkg.pr.new/@spectrum-ts/imessage@233

@spectrum-ts/imessage-local

npm i https://pkg.pr.new/@spectrum-ts/imessage-local@233

@spectrum-ts/slack

npm i https://pkg.pr.new/@spectrum-ts/slack@233

spectrum-ts

npm i https://pkg.pr.new/spectrum-ts@233

@spectrum-ts/telegram

npm i https://pkg.pr.new/@spectrum-ts/telegram@233

@spectrum-ts/terminal

npm i https://pkg.pr.new/@spectrum-ts/terminal@233

@spectrum-ts/whatsapp-business

npm i https://pkg.pr.new/@spectrum-ts/whatsapp-business@233

commit: 56594d7

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem vs fix

What problem is this PR trying to solve?

Two related documentation problems:

  1. CONTRIBUTING.md was steering people at retired APIs. Before this change, the “add a platform provider” checklist still told contributors to implement space.resolve, events.messages, and actions.send. Those names are gone from the current provider contract (user.resolve / space.create, top-level messages / send). That is not a style nit — it would send a new adapter author down a dead path.

  2. External Fusor adapter authors did not have a complete, current walkthrough. Fusor-backed providers have non-obvious rules: verify the exact rawBody, keep platform IDs consistent across routing, type messages as FusorMessages so overload selection works, understand at-least-once delivery, and treat inbound Fusor vs direct outbound API correctly. Telegram is the reference implementation, but reading that package alone is a high bar for someone publishing an independent adapter.

Is that problem valid?

Yes. The old CONTRIBUTING.md checklist is objectively wrong against today’s code. The Fusor authoring surface is also complex enough that “read Telegram and guess” is a real gap — especially for security-sensitive pieces like signature verification and replay windows.

Is the proposed fix the right solution, or only a patch over a deeper issue?

For what this PR claims to fix, this is the right kind of fix — not a symptom patch.

  • Updating the contributor checklist is the direct cure for the stale API names. There is no deeper code change needed for that bug; the docs were simply left behind after the provider API refactor.
  • Adding FUSOR_ADAPTERS.md is also the direct cure for the missing walkthrough. It matches the real Telegram wiring (definePlatform + fusor + typed messages + top-level send), and it correctly calls out pitfalls that are easy to miss in the type system (especially the FusorMessages overload selection rule).

That said, there is a deeper process issue underneath the stale checklist: docs can drift when the provider contract changes. This PR repairs the immediate damage, but it does not fully close that loop:

  • The published custom platforms guide already has a Fusor section, and its example still uses an untyped inline messages arrow — the exact footgun this new guide warns against.
  • The new guide lives at the repo root and is linked from CONTRIBUTING.md, but it is not wired into the docs site nav, so consumers who only read docs.photon.codes may still miss the better guidance.

So: ship this — it solves a real, current docs failure. As a follow-up, sync (or replace) the Fusor example in docs/custom-platforms.mdx.vel and decide whether this walkthrough should also live on the published docs site, so we do not end up with two sources of truth that drift again.

Verdict

Approve on substance. Valid problem, correct documentation-shaped solution, low risk (Markdown only). Residual risk is future doc drift / incomplete discoverability, not that this PR is papering over a runtime bug.

Open in Web View Automation 

Sent by Cursor Automation: PR analyze

Comment thread FUSOR_ADAPTERS.md
- [Telegram inbound mapping](./packages/telegram/src/inbound/messages.ts)
- [Telegram outbound dispatch](./packages/telegram/src/outbound/send.ts)
- [Telegram webhook registration](./packages/telegram/src/webhook.ts)
- [Fusor provider types](./packages/core/src/fusor/types.ts)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small residual risk, not a blocker: this guide correctly warns about typing messages as FusorMessages, but the Fusor example in docs/custom-platforms.mdx.vel still shows an untyped inline messages arrow. Until that published page is synced (or clearly points here), some readers may still hit the overload footgun this section is trying to prevent.

Comment thread CONTRIBUTING.md
- `lifecycle.createClient`; `lifecycle.destroyClient` is optional
- top-level `messages` — either a long-lived async iterable or a Fusor payload
handler
- top-level `send` — the outbound content dispatcher

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the important direct fix. The old checklist (space.resolve / events.messages / actions.send) was actively wrong against the current definePlatform contract, so correcting these names here is solving the root docs bug rather than working around it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@FUSOR_ADAPTERS.md`:
- Around line 177-191: Update acmePayloadSchema so unknown type values are
accepted through a catch-all envelope branch, while the message.created and
endpoint.challenge branches continue validating their required fields. Ensure
makeVerify passes unknown events to handleMessages for an undefined mapping
result, but malformed payloads with known event types remain rejected.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 93ce6f16-0298-45cc-bfb0-c67fdcafee98

📥 Commits

Reviewing files that changed from the base of the PR and between c210674 and 56594d7.

📒 Files selected for processing (2)
  • CONTRIBUTING.md
  • FUSOR_ADAPTERS.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Cursor Automation: PR analyze
🧰 Additional context used
🪛 LanguageTool
FUSOR_ADAPTERS.md

[style] ~55-~55: Consider removing “of” to be more concise
Context: ...atform ID The same ID must be used for all of the following: 1. the first argument to `d...

(ALL_OF_THE)

🔇 Additional comments (2)
CONTRIBUTING.md (1)

127-140: LGTM!

FUSOR_ADAPTERS.md (1)

98-100: 🗄️ Data Integrity & Integration

Keep the spectrum-ts package references.

spectrum-ts and @spectrum-ts/core are both published at 12.7.0. The metapackage exports spectrum-ts and spectrum-ts/authoring, so the shown peer dependencies and imports resolve correctly. @spectrum-ts/core is an alternative, not a required replacement.

			> Likely an incorrect or invalid review comment.

Comment thread FUSOR_ADAPTERS.md
Comment on lines +177 to +191
export const acmePayloadSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("message.created"),
eventId: z.string().min(1),
messageId: z.string().min(1),
chatId: z.string().min(1),
senderId: z.string().min(1),
text: z.string().min(1),
sentAt: z.iso.datetime(),
}),
z.object({
type: z.literal("endpoint.challenge"),
challenge: z.string(),
}),
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Allow unknown event types to reach the mapper.

makeVerify parses this schema before handleMessages runs. The closed discriminated union rejects every unknown event type, so the mapper cannot return undefined as promised later in the guide. A harmless platform extension becomes a rejected Fusor delivery.

Add a catch-all envelope branch and validate known event shapes before mapping them. Keep malformed known events rejected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@FUSOR_ADAPTERS.md` around lines 177 - 191, Update acmePayloadSchema so
unknown type values are accepted through a catch-all envelope branch, while the
message.created and endpoint.challenge branches continue validating their
required fields. Ensure makeVerify passes unknown events to handleMessages for
an undefined mapping result, but malformed payloads with known event types
remain rejected.

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.

2 participants