Skip to content

feat(vibewatch-sentiment): --asset sBTC|STX picks the 402 option to pay - #431

Open
sonic-mast wants to merge 2 commits into
aibtcdev:mainfrom
sonic-mast:feat/vibewatch-sentiment-asset-select
Open

sonic-mast wants to merge 2 commits into
aibtcdev:mainfrom
sonic-mast:feat/vibewatch-sentiment-asset-select

Conversation

@sonic-mast

Copy link
Copy Markdown
Contributor

Why

Four Tier 1 winners of the Stacks Vibe Index paid-query bounties (aibtc board mtt3jjrgcf0aa8fb225c, mttjxw940faa8df06de7, mtwd0kcs90cb2cfcf830, mtwd0cet93a16ab82498) and Vibewatch-io/vibewatch-mcp#16 asked for the same two things: a way to pay the index's advertised STX price, and docs saying that the 402 lists sBTC first. The engine's interceptor always paid the first stacks: option, so a wallet holding STX but no sBTC signed an sBTC transfer it could not fund, and the only way to pay in STX was to bypass the interceptor.

What

  • src/lib/services/x402.service.tscreateApiClient(baseUrl, tool, { preferredAsset }). New exported selectStacksPaymentOption(accepts, preferredAsset?): no preference keeps the old first-Stacks-option rule; "STX" picks the first Stacks option whose asset is literally STX; "sBTC" picks the first one passing the builders' contract-id test. A preference the challenge does not offer rejects before anything is signed and names the offered assets; an invalid value rejects when the client is created (same place the payment mode is validated). Works in sponsored and direct modes; the selected option flows into the existing builders unchanged.
  • vibewatch-sentiment--asset sBTC|STX (default sBTC) on project, evidence, delta, validated before the free preflight and before any wallet access.
  • SKILL.md / AGENT.md — a "Choosing the asset" / "Paying in STX instead of sBTC" section (402 lists sBTC first, engine default = first option, --asset STX to pay the STX option, select accepts[] by asset not by position), an error-table row for the unoffered-asset refusal, and one line on settlement latency (winners observed the paid payload under 60 s after broadcast).

Default behaviour is unchanged: without preferredAsset every existing caller pays exactly what it paid before.

Verification

  • bun run typecheck, bun run validate (204/204) — pass.
  • bun test src/lib — 160 pass, 0 fail, including 10 new tests: the pure selection rules, fake-server runs paying sBTC and STX in both orders and both modes (decoded tx is a ContractCall for sBTC and a TokenTransfer for STX, accepted.asset matches), the unoffered-asset refusal (no paid request, no Stacks API hit), and the invalid-value refusal at client creation.
  • Local review gate (scripts/gemini-review.py --base origin/main): no findings.
  • Not exercised: a live mainnet STX payment.

Related: a server-side change in vibewatch-app refuses an unscored panel slug with a free 422 before the 402, so the client-side --allow-unscored guard here stops being the only protection once that deploys.

🤖 Generated with Claude Code

sonic-mast and others added 2 commits September 16, 2026 14:06
The engine's interceptor always paid the first `stacks:` option of a 402
challenge. The Vibewatch Stacks Vibe Index lists sBTC first and STX second,
so a wallet holding only STX signed an sBTC transfer it could not fund, and
there was no way to pay the advertised STX price short of bypassing the
interceptor (Vibewatch-io/vibewatch-mcp#16).

- x402.service: `createApiClient(baseUrl, tool, { preferredAsset })` selects
  the first Stacks option carrying that asset (`STX` literally, sBTC by the
  same contract-id test the builders use). No preference keeps the old
  first-option rule byte for byte. A preference the challenge does not
  offer fails before anything is signed and names the offered assets; an
  invalid value fails when the client is created. Applies to sponsored and
  direct modes alike.
- vibewatch-sentiment: `--asset sBTC|STX` (default sBTC) on `project`,
  `evidence`, `delta`, validated before the free preflight.
- SKILL.md / AGENT.md: how to pay in STX, that the 402 lists sBTC first,
  and observed settlement latency (<60 s from broadcast to paid payload).
- Tests: pure selection rules plus fake-server runs for both assets in both
  modes, the unoffered-asset refusal, and the invalid-value refusal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CI typechecks test files; getStacksChainId returns string, not NetworkV2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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