Do not race answerersThe approval waterfall is the correct seam, but listener order, a Preset suffix, and a shared status file are not authorization or exactly-once settlement.

Bind one decision owner.

Presentation observersWeb + Feishu

Multiple surfaces may display status, but they do not automatically gain response authority.

Session bindingone channel instance

A trusted immutable binding selects the only answerer allowed to settle this approval.

Authenticate the whole authority edge.

tenantapp
→
actorrole
→
channelchat
→
Sessionapproval
→
nonceexpiry

Make click and timeout one race.

Atomic winner

Both paths compare-and-set the same pending revision. Only one transition succeeds.

Stale loser

A late click or timer observes the terminal record and cannot return a second outcome.

Derived card

Message patching reflects the result; patch failure can never reopen or change it.

Card UX cannot carry hidden authority.

What rc.2 suppliesTool + reason + call id

Do not claim the approver reviewed exact command arguments when the seam did not provide them.

What the adapter recordsSchema + message id

Patch the exact original card family. Never guess a different schema after an error.

Package the integration outside managed DSH files.

Normal plugin

Register one scoped answerer through the supported out-of-tree plugin boundary.

Versioned companion

Pin the dsh-im callback contract, compatible versions, card schema, and removal path.

Provider-neutral core

Core owns identity and settlement; the Feishu adapter owns webhook auth and card UX.

Six high-value proof gates.

Primary evidence.

Keep the complete interaction contract.

The canonical guide covers SDK requests, channel ownership, authenticated card authority, atomic settlement, schema-stable projection, restart recovery, packaging, and thirty-six conformance gates.

Read the complete guide