feat: M2 quote gates + M3 buzz loop - #3
Merged
Merged
Conversation
…val, migrations 0003/0004
Rebuilt from the pre-refactor M2 work (0ff2e8f) on the studio-types
conventions:
studio-types grows the quote wire contract — NewQuote/MilestoneSpec/
PayoutDestination/Split/ChannelParams/Quote/QuoteStatus and GatePolicy/
GateSpec with schemars derives; schemas/quote.json and
schemas/gate-policy.json are now generated (just schemas) and covered by
the drift test. The gate-policy edges map keeps its strict edge-key
patternProperties via a schema_with hook built from ProjectState::ALL, so
the pattern cannot drift from the enum. ProjectState/Edge/EdgePattern move
to studio-types as shared vocabulary. FieldError.field becomes an owned
path (milestones[1].amount). NewRfq/Rfq reserve buyer_signature (recorded,
not verified — archy's RFQ-replay upgrade path), migration 0003.
studio-core keeps the logic: gate engine eval (fail-closed, denial as
first-class outcome, loud operator overrides, escalation windows),
gates_for + commitment_hash as engine fns, and quote::issue(new, rfq_id,
id, now) — the pure issuance path computing the policy commitment hash, so
CLI/MCP surfaces share it; the HTTP handler only supplies uuid and clock.
studio-api: POST /api/v1/rfqs/{id}/quote (studio bearer token, fail-closed
when unconfigured; 401/404/409/422 with field errors) and free
GET /api/v1/rfqs/{id}/quote (status fail-closed against sweep lag);
discovery index updated. studio-store::quotes with the one-quote-per-RFQ
singleton (migration 0004) surfaced as 409; scarced runs the quote expiry
sweep (SCARCED_SWEEP_SECONDS, default 30s).
Verified: just ci green (fmt, clippy -D warnings, 106 tests incl. 18
quote/gate-policy conformance cases against the generated schemas); live
curl demo: 401 no-auth, 201 issue, 409 duplicate, free buyer read,
schemas/gate-policy served.
- justfile: `install` recipe (pay-repo convention) — `just install scarce` cargo-installs the scarced binary; extra args forward to cargo install - README: env var table, self-describing-API walkthrough (index, schema discovery, RFQ capture, quote issue/409/free read), milestone status line
- config precedence: defaults <- YAML <- SCARCED_* env (nested keys join with `__`, e.g. SCARCED_BUZZ__RELAY_URL); env names unchanged - new `buzz.relay_url` key — recorded and logged now, consumed by the M3 orchestrator; scarced.example.yaml points at the scarce community relay - clap for the binary surface (--config, --version, --help) - actionable failures: unknown keys named with valid alternatives, missing file, zero sweep, non-ws relay URL; empty token still disables (fail-closed)
Shortcut per channel decision: pay.sh intake parked, buzz visibility first. - studio-buzz: BuzzPort trait + RelayBuzz (buzz-sdk builders + buzz-ws-client NIP-42 transport, git-pinned to block/buzz@22be8bb35) + recording mock; publishes wait for the relay OK so returned event ids are storable evidence. NIP-OA auth tag support for managed-agent identities (buzz.auth_tag) - POST /api/v1/rfqs/{id}/quote/accept — buyer, free, once; fail-closed vs expiry via core pure fn quote::accept + atomic UPDATE guard; ACCEPTED is sticky and stands in for FUNDED while payments are stubbed (PLAN §6) - scarced mirror task: demand/quote/accepted beats -> ops channel; acceptance creates proj-<slug>-<shortid> workroom, posts contract-starting there, and stores the channel-create event id as FUNDED->WORKROOM_ACTIVE evidence (workrooms table, migration 0005 rebuilds quotes for the widened CHECK) - config: buzz.{private_key,ops_channel,auth_tag}; auth tag env bypasses figment's lenient JSON parsing (read raw) Verified: just ci green (122 tests) + live loop against wss://scarce.communities.buzz.xyz — see PR body for event ids.
- workrooms (and any BuzzPort channel) are created private — commercial terms, members only; the mirror adds the RFQ buyer as member right after the evidence row, best-effort with its own loud log - studio_buzz::pubkey_hex: full bech32 npub decode (fails at the port, not the relay); MockBuzz records AddMember; RelayBuzz::set_visibility (9002) - examples/channel_admin: operator one-offs (set-visibility, add-member) - install ring as process-level rustls CryptoProvider at every binary entry point (same pattern/pin as buzz-cli) — feature unification had been picking a provider for scarced by accident and the example panicked - scarced.example.yaml points at the new private studio-ops (cdc47f86)
lgalabru
force-pushed
the
feat/m3-buzz-loop
branch
from
August 1, 2026 17:57
00a52fa to
d15c5cc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M2 + M3, rebased on main after #2 (M1) merged — 5 commits, tree identical to the previously verified
00a52fa.M2 — quote flow + gate engine
Quote/GatePolicy/GateSpecwire types instudio-types, schemas generated viajust schemas(+ drift test)quote::issueinstudio-core;POST/GET /api/v1/rfqs/{id}/quote(bearer, fail-closed, 409 singleton); expiry sweepjust install scarce+ README quickstart; figment config (defaults ← YAML ←SCARCED_*env),scarced --configM3 — buzz loop
studio-buzzBuzzPort (buzz-sdk + buzz-ws-client pinned to block/buzz@22be8bb35, NIP-42 + NIP-OA auth)POST /api/v1/rfqs/{id}/quote/accept(free, once, refuses lapsed; migration 0005)proj-<slug>-<shortid>, auto-adds the buyer, posts contract-start; channel-create event id stored as FUNDED→WORKROOM_ACTIVE evidenceVerified
just ci— 122 tests, clippy clean; live loop onwss://scarce.communities.buzz.xyz(workroomproj-helius-webhook-replay-debugger-2651b127, buyer auto-added). Rebase is tree-identical, so results carry over; CI re-running on the new head.🤖 Generated with Claude Code