From d37bcdac71145004e4e75ff4eee582722b5378ca Mon Sep 17 00:00:00 2001 From: XxHugheadxX Date: Fri, 25 Sep 2026 23:58:23 -0400 Subject: [PATCH 1/2] docs: add MVP user flows with screen IDs, error paths and demo paths docs/blueprints/flows.md covers the seven MVP flows (connect wallet, discover, agent detail, create and register, hire and pay, track a hire, rate), each with a Mermaid flowchart and a step table: screen ID, user action, system response and error path. Steps follow ADR-0002, ADR-0003, ADR-0004 and the domain model, and reuse the app's existing screens (S01-S04, S07, S08); S05, S06, S09 and S10 are new. Includes the under-15-second grant video path (#32) and the under-2-minute Serverpod video path mapped to the vision's five demo criteria (#4). --- docs/blueprints/flows.md | 245 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 docs/blueprints/flows.md diff --git a/docs/blueprints/flows.md b/docs/blueprints/flows.md new file mode 100644 index 0000000..e5c267c --- /dev/null +++ b/docs/blueprints/flows.md @@ -0,0 +1,245 @@ +# MVP user flows + +- **Issue:** #22 · **Date:** 2026-09-26 +- **Sources:** [vision](../vision.md) (MVP scope), [ADR-0002](../adr/0002-agent-registry-on-soroban.md) (registry), [ADR-0003](../adr/0003-payment-rail-and-custody.md) (payments), [ADR-0004](../adr/0004-agent-manifest-and-deployment.md) (manifest and deploy), [domain model](../domain/model.md) + +What the user does, step by step, including what happens when something fails. The wireframes (#23), the API contract (#8), and the app screens (#25–#28) derive from these flows. Agent Studio details (drafts, test run, edit, my agents) are added to this file by #36. + +Screen IDs reuse the app's existing screens where they exist (`puls3_flutter/lib/src/screens/`). The full list is at the end. + +**Reading the tables:** *Error / edge path* says what the user sees and where they end up. "Stays on" means no navigation; the user can retry. + +## Coverage of the MVP scope + +| MVP "In" item ([vision](../vision.md#in)) | Flows | +|---|---| +| Create an agent | F4 | +| Discover agents | F2, F3 | +| Hire and pay an agent | F1, F5 | +| Get the result and rate it | F6, F7 | + +--- + +## F1. Connect wallet + +Needed before hiring (F5), deploying (F4) or rating (F7). The app asks for it at that moment; browsing never requires a wallet. + +```mermaid +flowchart TD + A["Any screen: Connect wallet"] --> B["S09 wallet picker"] + B --> C{"Wallet extension installed?"} + C -- No --> C1["Show install link, stay on S09"] + C -- Yes --> D["Wallet asks the user to approve"] + D --> E{"Approved?"} + E -- No --> E1["Connection cancelled, back to previous screen"] + E -- Yes --> F{"Wallet on testnet?"} + F -- No --> F1["Ask to switch the wallet to testnet"] + F -- Yes --> G["Address shown in the app bar"] +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S09-wallet-connect` | Taps **Connect wallet** from any screen | Opens the wallet picker | — | +| 2 | `S09-wallet-connect` | Picks a wallet | Asks the wallet to connect | Wallet not installed: show its install link, stay on S09 | +| 3 | `S09-wallet-connect` (wallet popup) | Approves | Reads the public address | User rejects: "Connection cancelled", back to the previous screen | +| 4 | `S09-wallet-connect` | — | Checks the wallet's network | Not testnet: "Switch your wallet to Testnet", stay on S09 | +| 5 | `S09-wallet-connect` closes | — | Shows the shortened address in the app bar and resumes the action that asked for the wallet (F4, F5, F6 or F7) | — | + +## F2. Discover agents + +```mermaid +flowchart TD + A["S01 landing: Explore agents"] --> B["S02 marketplace loads the catalog"] + B --> C{"Catalog loaded?"} + C -- No --> C1["Error banner with Retry, stay on S02"] + C -- Yes --> D["Grid of agent cards"] + D --> E["User searches or filters by skill"] + E --> F{"Any match?"} + F -- No --> F1["Empty state: clear filters"] + F -- Yes --> G["Tap a card: go to F3"] +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S01-landing` | Taps **Explore agents** | Navigates to `/market` | — | +| 2 | `S02-marketplace` | — | Loads the catalog: agents registered on-chain, with name, skills, price and rating | Load fails (network, backend down): error banner with **Retry**, stay on S02 | +| 3 | `S02-marketplace` | Types in search | Filters by name and description as they type | — | +| 4 | `S02-marketplace` | Taps skill chips | Filters to agents with every selected skill | No match: empty state with **Clear filters** | +| 5 | `S02-marketplace` | Taps an agent card | Opens F3 | — | + +## F3. View agent detail + +```mermaid +flowchart TD + A["S02 card tapped"] --> B["S03 agent detail loads"] + B --> C{"Agent exists?"} + C -- No --> C1["Agent not found, link back to S02"] + C -- Yes --> D["Price, skills, reputation, on-chain identity"] + D --> E{"On-chain data reachable?"} + E -- No --> E1["Show cached data with a warning, disable Hire"] + E -- Yes --> F["Hire button enabled: go to F5"] + D --> G["View on explorer link"] +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S03-agent-detail` | Opens `/agent/:id` | Loads the agent: name, description, skills, price in USDC, rating and number of paid hires | Unknown id: "Agent not found" with a link back to S02 | +| 2 | `S03-agent-detail` | — | Shows the on-chain identity: agent id, owner and payment wallet, and a **View on explorer** link to the Identity Registry | Chain data unreachable: show the last known data with a warning; **Hire** is disabled until it loads | +| 3 | `S03-agent-detail` | Taps **View on explorer** | Opens stellar.expert in a new tab | — | +| 4 | `S03-agent-detail` | Taps **Hire** | Opens F5 | Agent paused (`active: false`, ADR-0004): **Hire** hidden, "Not accepting hires" | + +## F4. Create (register) an agent + +The Studio form fields map 1:1 to the manifest (ADR-0004). Deploy steps follow ADR-0004 and ADR-0003. Test runs and drafts are detailed in #36. + +```mermaid +flowchart TD + A["S07 studio: fill in the agent"] --> B{"Form valid?"} + B -- No --> B1["Inline errors, stay on S07"] + B -- Yes --> C["Deploy: S08 deploy sheet"] + C --> D{"Wallet connected?"} + D -- No --> D1["F1 connect wallet, then resume"] + D -- Yes --> E["Server stores the version and creates the agent wallet"] + E --> F["Builder signs register_full"] + F --> G{"Signed and confirmed?"} + G -- "Rejected" --> G1["Deploy paused at this step, Retry"] + G -- "Tx failed or URI taken" --> G2["Error with reason, Retry"] + G -- Yes --> H["Builder signs the wallet authorization for set_agent_wallet"] + H --> I{"Confirmed?"} + I -- No --> I1["Deploy paused at this step, Retry"] + I -- Yes --> J["Agent active: link to S03 and to the explorer"] +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S07-studio` | Fills in name, description, skills, model, system prompt, price | Validates as they type (domain rules I6–I11, ADR-0004 limits) | Invalid field: inline error; **Deploy** disabled | +| 2 | `S07-studio` | Taps **Test run** (optional) | Runs the draft once, no payment, no on-chain write (#36) | Daily test quota used: "Quota resets at …" | +| 3 | `S07-studio` | Taps **Deploy** | Opens `S08-deploy-sheet` with the steps listed | No wallet: F1 first, then resumes here | +| 4 | `S08-deploy-sheet` | — | Server stores the manifest version, its salted hash, and creates the agent's wallet (testnet custody, ADR-0003) | Server error: step marked failed, **Retry** | +| 5 | `S08-deploy-sheet` (wallet popup) | Signs `register_full` | Sends it and waits for confirmation; reads the new `agent_id` from the `Registered` event | Rejects: step paused, **Retry**. `UriAlreadyRegistered` or tx failed: reason shown, **Retry** | +| 6 | `S08-deploy-sheet` (wallet popup) | Signs the authorization entry for `set_agent_wallet` (`signAuthEntry`) | Server submits it with the agent account as source and waits for confirmation | Rejects or fails: step paused, **Retry** from this step (the agent stays registered) | +| 7 | `S08-deploy-sheet` | — | Publishes the registration file and activates the agent. Shows **View agent** (S03) and **View on explorer** | — | + +## F5. Hire and pay an agent + +Rail and checks follow ADR-0003: a USDC transfer to the agent's muxed address, verified by the server before the agent runs. + +```mermaid +flowchart TD + A["S03: Hire"] --> B["S04 hire sheet: task input and price"] + B --> C{"Wallet connected?"} + C -- No --> C1["F1 connect wallet, then resume"] + C -- Yes --> D{"Enough USDC and a USDC trustline?"} + D -- No --> D1["Show balance and how to get testnet USDC"] + D -- Yes --> E["Confirm: server creates the hire"] + E --> F["Wallet asks to sign the USDC transfer"] + F --> G{"Signed?"} + G -- No --> G1["Hire not paid, back to review"] + G -- Yes --> H["Server verifies the payment on-chain"] + H --> I{"All checks pass?"} + I -- "Not yet visible" --> I1["Keep checking, show Verifying"] + I -- No --> I2["Payment rejected with reason, hire stays unpaid"] + I -- Yes --> J["Agent runs: go to F6"] +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S04-hire-sheet` | Types the task input | Shows the price in USDC and the character limit | Input over the agent's limit: inline error, **Confirm** disabled | +| 2 | `S04-hire-sheet` | — | Checks the wallet is connected and holds enough USDC | No wallet: F1. Not enough USDC or no trustline: shows the balance and how to get testnet USDC, **Confirm** disabled | +| 3 | `S04-hire-sheet` | Taps **Confirm and pay** | Server creates the hire (hire id, price and manifest version fixed) and returns the payment: USDC contract, the agent's muxed address, amount | Server error: "Could not create the hire", **Retry** | +| 4 | `S04-hire-sheet` (wallet popup) | Signs the USDC transfer | Submits it; shows **Paying…** | Rejects: "Payment cancelled", hire stays unpaid, back to review. Tx fails (fees, balance changed): reason shown, **Retry** | +| 5 | `S04-hire-sheet` | — | Server verifies: success, sent by the USDC contract, to the agent's wallet, exact amount, this hire's id, hash not used before. Shows **Verifying payment…** | Not visible yet: keeps checking. A check fails: "Payment does not match this hire", shows the tx link, hire stays unpaid | +| 6 | `S04-hire-sheet` | — | Payment confirmed: shows the tx hash with an explorer link, and the hire moves to running | — | + +## F6. Track a hire and see its result + +States follow the hire lifecycle (#10): Requested → Paid → InProgress → Delivered → Rated. + +```mermaid +flowchart TD + A["S04: payment confirmed"] --> B["S06 hire detail: InProgress"] + B --> C{"Agent finished?"} + C -- "Failed or timed out" --> C1["Hire marked failed, reason shown"] + C -- Yes --> D["Delivered: result shown"] + D --> E["Copy result, or Rate: go to F7"] + F["S05 my hires"] --> B +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S04-hire-sheet` | Taps **View hire** | Opens `S06-hire-detail` (`/hires/:id`) | — | +| 2 | `S06-hire-detail` | — | Shows status (Paid, InProgress), the agent, the price paid and the payment tx link | — | +| 3 | `S06-hire-detail` | Waits | Updates the status while the agent runs | Agent fails or times out: status **Failed** with the reason. Refunds are out of the MVP ([vision](../vision.md#out)): the screen says so and keeps the payment link | +| 4 | `S06-hire-detail` | — | Status **Delivered**: shows the result | Result larger than the screen: scrollable, with **Copy** | +| 5 | `S05-my-hires` | Opens `/hires` later | Lists the user's hires with status, newest first | Wallet not connected: F1 (hires are tied to the paying address) | + +## F7. Rate an agent (P1) + +Feedback rules come from the domain (I16, I17) and ADR-0002: a feedback entry needs the authorization the server records for a verified paid hire, and each hire can be rated once. + +```mermaid +flowchart TD + A["S06: Rate"] --> B["S10 rate sheet: score 1 to 5, optional comment"] + B --> C{"Hire delivered and not rated yet?"} + C -- No --> C1["Rate button hidden or already rated message"] + C -- Yes --> D["Wallet asks to sign the feedback"] + D --> E{"Signed and accepted?"} + E -- "Rejected" --> E1["Rating not sent, stay on S10"] + E -- "Not authorized or already rated" --> E2["Reason shown, back to S06"] + E -- Yes --> F["Rating saved, shown on S03"] +``` + +| # | Screen | User action | System response | Error / edge path | +|---|---|---|---|---| +| 1 | `S06-hire-detail` | Taps **Rate** | Opens `S10-rate-sheet` | Hire not delivered, or already rated: **Rate** hidden, or "You already rated this hire" | +| 2 | `S10-rate-sheet` | Picks a score and writes an optional comment | Validates: score 1–5, comment ≤ 500 characters | Comment too long: inline error, **Send** disabled | +| 3 | `S10-rate-sheet` (wallet popup) | Signs the feedback | Submits `give_feedback` for this hire | Rejects: "Rating not sent", stay on S10 | +| 4 | `S10-rate-sheet` | — | Waits for confirmation | `HireNotAuthorized` or `HireAuthorizationConsumed`: reason shown, back to S06 | +| 5 | `S06-hire-detail` | — | Status **Rated**; the new score counts in the agent's rating on S03 | — | + +--- + +## Demo paths + +### Grant video, under 15 seconds (#32) + +The shortest path that shows find → pay → result. Uses the demo shell's mock wallet, so no signing popups. + +| Time | Screen | Action | +|---|---|---| +| 0–2 s | `S01-landing` | Tap **Explore agents** | +| 2–5 s | `S02-marketplace` | Tap one skill chip; the grid narrows | +| 5–7 s | `S03-agent-detail` | Tap a card, show price and rating, tap **Hire** | +| 7–11 s | `S04-hire-sheet` | Type a short task, **Confirm and pay**, payment confirmed with tx hash | +| 11–15 s | `S06-hire-detail` | Result appears | + +### Serverpod video, under 2 minutes (#4) + +Covers the five demo criteria of the [vision](../vision.md#7-demo-success-criteria), with real testnet transactions. + +| Time | Screen | Action | Vision criterion | +|---|---|---|---| +| 0:00–0:10 | `S01-landing` | One-line pitch | — | +| 0:10–0:40 | `S07-studio` → `S08-deploy-sheet` | Fill in a prompt-only agent, **Deploy**, sign twice, open the explorer on the new agent | 1 | +| 0:40–0:55 | `S02-marketplace` | The new agent appears in the catalog, served by Serverpod | 2 | +| 0:55–1:25 | `S03-agent-detail` → `S04-hire-sheet` | **Hire**, sign the USDC transfer, **Verifying payment…**, open the tx on the explorer | 3 | +| 1:25–1:40 | `S06-hire-detail` | Status moves to **Delivered**, result shown | 4 | +| 1:40–1:55 | `S10-rate-sheet` → `S03-agent-detail` | Rate 5, the rating shows on the agent | 5 | +| 1:55–2:00 | — | Closing line | — | + +--- + +## Screen IDs + +| ID | Route / kind | Exists in the app today | Used in | +|---|---|---|---| +| `S01-landing` | `/` | Yes (`landing_screen.dart`) | F2, demos | +| `S02-marketplace` | `/market` | Yes (`market_screen.dart`) | F2, F3, demos | +| `S03-agent-detail` | `/agent/:id` | Yes (`agent_detail_screen.dart`) | F3, F4, F5, F7, demos | +| `S04-hire-sheet` | Bottom sheet over S03 | Yes (`hire_sheet.dart`) | F5, F6, demos | +| `S05-my-hires` | `/hires` | No, new | F6 | +| `S06-hire-detail` | `/hires/:id` | No, new | F6, F7, demos | +| `S07-studio` | `/studio` | Yes (`studio_screen.dart`) | F4, demo | +| `S08-deploy-sheet` | Bottom sheet over S07 | Yes (`deploy_sheet.dart`) | F4, demo | +| `S09-wallet-connect` | Dialog over any screen | No, new (the app has a mock `WalletPort`) | F1, F4, F5, F6 | +| `S10-rate-sheet` | Bottom sheet over S06 | No, new | F7, demo | From c6fa672fdacf327516c476c08f151858f0876dce Mon Sep 17 00:00:00 2001 From: XxHugheadxX Date: Mon, 28 Sep 2026 17:18:33 -0400 Subject: [PATCH 2/2] docs(flows): address review on F4 and F7: manifest input/output, registration URI policy, authorize_feedback step F4 now lists every manifest field (input and output with their limits) and cites I2 for the price. It defines what happens when the registration URI is already taken: the server checks agent_id_by_uri before register_full and resumes the builder's own registration or issues a new draft URI, so a retry never repeats a failing call. F5 triggers authorize_feedback once the payment is verified, and F7 checks it is confirmed on-chain (submitting it as the configured authorizer if missing) and that the paying wallet signs give_feedback, per ADR-0002. --- docs/blueprints/flows.md | 47 ++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/docs/blueprints/flows.md b/docs/blueprints/flows.md index e5c267c..a2b563f 100644 --- a/docs/blueprints/flows.md +++ b/docs/blueprints/flows.md @@ -90,7 +90,15 @@ flowchart TD ## F4. Create (register) an agent -The Studio form fields map 1:1 to the manifest (ADR-0004). Deploy steps follow ADR-0004 and ADR-0003. Test runs and drafts are detailed in #36. +The Studio form fields map 1:1 to the manifest (ADR-0004): name, description, skills, model, system prompt, input (type and maximum characters), output (type and maximum characters), and price. Deploy steps follow ADR-0004 and ADR-0003. Test runs and drafts are detailed in #36. + +**Registration URI.** The server gives every Studio draft its own registration URI (the URL of the agent's public registration file, ADR-0004), built from a server-generated draft id. Before the builder signs `register_full`, the server calls `agent_id_by_uri` on the Identity Registry: + +- **Not registered:** continue with `register_full`. +- **Registered by this builder:** an earlier attempt already landed (for example, the app lost the confirmation). The deploy **resumes** with that `agent_id` at step 7; nothing is registered twice. +- **Registered by another address:** the server issues a **new draft id and URI**, rebuilds the transaction, and the builder signs it. + +Retrying with an unchanged URI therefore never repeats a failing `register_full`. ```mermaid flowchart TD @@ -100,10 +108,14 @@ flowchart TD C --> D{"Wallet connected?"} D -- No --> D1["F1 connect wallet, then resume"] D -- Yes --> E["Server stores the version and creates the agent wallet"] - E --> F["Builder signs register_full"] + E --> P{"Registration URI already registered?"} + P -- "By this builder" --> H + P -- "By another address" --> P1["Server issues a new draft URI"] + P1 --> F + P -- No --> F["Builder signs register_full"] F --> G{"Signed and confirmed?"} G -- "Rejected" --> G1["Deploy paused at this step, Retry"] - G -- "Tx failed or URI taken" --> G2["Error with reason, Retry"] + G -- "Tx failed" --> G2["Error with reason, Retry"] G -- Yes --> H["Builder signs the wallet authorization for set_agent_wallet"] H --> I{"Confirmed?"} I -- No --> I1["Deploy paused at this step, Retry"] @@ -112,13 +124,14 @@ flowchart TD | # | Screen | User action | System response | Error / edge path | |---|---|---|---|---| -| 1 | `S07-studio` | Fills in name, description, skills, model, system prompt, price | Validates as they type (domain rules I6–I11, ADR-0004 limits) | Invalid field: inline error; **Deploy** disabled | +| 1 | `S07-studio` | Fills in name, description, skills, model, system prompt, input (type, max characters), output (type, max characters) and price | Validates as they type: skills (I6, I9, I10), name (I7), description (I8), price as whole USDC stroops (I2) and greater than zero (I11), input ≤ 8,000 and output ≤ 16,000 characters (ADR-0004) | Invalid field: inline error; **Deploy** disabled | | 2 | `S07-studio` | Taps **Test run** (optional) | Runs the draft once, no payment, no on-chain write (#36) | Daily test quota used: "Quota resets at …" | | 3 | `S07-studio` | Taps **Deploy** | Opens `S08-deploy-sheet` with the steps listed | No wallet: F1 first, then resumes here | | 4 | `S08-deploy-sheet` | — | Server stores the manifest version, its salted hash, and creates the agent's wallet (testnet custody, ADR-0003) | Server error: step marked failed, **Retry** | -| 5 | `S08-deploy-sheet` (wallet popup) | Signs `register_full` | Sends it and waits for confirmation; reads the new `agent_id` from the `Registered` event | Rejects: step paused, **Retry**. `UriAlreadyRegistered` or tx failed: reason shown, **Retry** | -| 6 | `S08-deploy-sheet` (wallet popup) | Signs the authorization entry for `set_agent_wallet` (`signAuthEntry`) | Server submits it with the agent account as source and waits for confirmation | Rejects or fails: step paused, **Retry** from this step (the agent stays registered) | -| 7 | `S08-deploy-sheet` | — | Publishes the registration file and activates the agent. Shows **View agent** (S03) and **View on explorer** | — | +| 5 | `S08-deploy-sheet` | — | Server checks the draft's registration URI with `agent_id_by_uri` (see **Registration URI** above) | Already registered by this builder: resume at step 7 with that `agent_id`. Registered by another address: new draft URI, then step 6 | +| 6 | `S08-deploy-sheet` (wallet popup) | Signs `register_full` | Sends it and waits for confirmation; reads the new `agent_id` from the `Registered` event | Rejects: step paused, **Retry**. Tx failed: reason shown, **Retry**. `UriAlreadyRegistered` (another deploy took the URI between step 5 and now): back to step 5, which resumes or issues a new URI | +| 7 | `S08-deploy-sheet` (wallet popup) | Signs the authorization entry for `set_agent_wallet` (`signAuthEntry`) | Server submits it with the agent account as source and waits for confirmation | Rejects or fails: step paused, **Retry** from this step (the agent stays registered) | +| 8 | `S08-deploy-sheet` | — | Publishes the registration file and activates the agent. Shows **View agent** (S03) and **View on explorer** | — | ## F5. Hire and pay an agent @@ -149,7 +162,7 @@ flowchart TD | 3 | `S04-hire-sheet` | Taps **Confirm and pay** | Server creates the hire (hire id, price and manifest version fixed) and returns the payment: USDC contract, the agent's muxed address, amount | Server error: "Could not create the hire", **Retry** | | 4 | `S04-hire-sheet` (wallet popup) | Signs the USDC transfer | Submits it; shows **Paying…** | Rejects: "Payment cancelled", hire stays unpaid, back to review. Tx fails (fees, balance changed): reason shown, **Retry** | | 5 | `S04-hire-sheet` | — | Server verifies: success, sent by the USDC contract, to the agent's wallet, exact amount, this hire's id, hash not used before. Shows **Verifying payment…** | Not visible yet: keeps checking. A check fails: "Payment does not match this hire", shows the tx link, hire stays unpaid | -| 6 | `S04-hire-sheet` | — | Payment confirmed: shows the tx hash with an explorer link, and the hire moves to running | — | +| 6 | `S04-hire-sheet` | — | Payment confirmed: shows the tx hash with an explorer link, and the hire moves to running. In the background, the server (the configured feedback authorizer) submits `authorize_feedback` for this hire (see F7) | Authorization tx fails: the server retries it; F7 step 2 checks it again before rating | ## F6. Track a hire and see its result @@ -175,14 +188,19 @@ flowchart TD ## F7. Rate an agent (P1) -Feedback rules come from the domain (I16, I17) and ADR-0002: a feedback entry needs the authorization the server records for a verified paid hire, and each hire can be rated once. +Feedback rules come from the domain (I16, I17) and ADR-0002. The Reputation Registry accepts `give_feedback` only for a hire that the **configured feedback authorizer** (our server) registered first with `authorize_feedback(hire_id, agent_id, client_address)`, after verifying the payment in F5. `hire_id` is a 32-byte id the server derives from the hire record and its payment transaction. `give_feedback` must be signed by that same `client_address` (the address that paid), and it consumes the authorization, so each hire is rated once. ```mermaid flowchart TD A["S06: Rate"] --> B["S10 rate sheet: score 1 to 5, optional comment"] B --> C{"Hire delivered and not rated yet?"} C -- No --> C1["Rate button hidden or already rated message"] - C -- Yes --> D["Wallet asks to sign the feedback"] + C -- Yes --> K{"Connected wallet is the address that paid?"} + K -- No --> K1["Switch to the paying wallet"] + K -- Yes --> Z{"Authorizer registered this hire with authorize_feedback?"} + Z -- No --> Z1["Server submits authorize_feedback and waits for confirmation"] + Z1 --> D + Z -- Yes --> D["Wallet asks to sign the feedback"] D --> E{"Signed and accepted?"} E -- "Rejected" --> E1["Rating not sent, stay on S10"] E -- "Not authorized or already rated" --> E2["Reason shown, back to S06"] @@ -192,10 +210,11 @@ flowchart TD | # | Screen | User action | System response | Error / edge path | |---|---|---|---|---| | 1 | `S06-hire-detail` | Taps **Rate** | Opens `S10-rate-sheet` | Hire not delivered, or already rated: **Rate** hidden, or "You already rated this hire" | -| 2 | `S10-rate-sheet` | Picks a score and writes an optional comment | Validates: score 1–5, comment ≤ 500 characters | Comment too long: inline error, **Send** disabled | -| 3 | `S10-rate-sheet` (wallet popup) | Signs the feedback | Submits `give_feedback` for this hire | Rejects: "Rating not sent", stay on S10 | -| 4 | `S10-rate-sheet` | — | Waits for confirmation | `HireNotAuthorized` or `HireAuthorizationConsumed`: reason shown, back to S06 | -| 5 | `S06-hire-detail` | — | Status **Rated**; the new score counts in the agent's rating on S03 | — | +| 2 | `S10-rate-sheet` | — | Checks that the connected wallet is the address that paid, and that the server's `authorize_feedback` for this hire is confirmed on-chain. If it is missing (the background call from F5 failed), the server, as the configured authorizer, submits `authorize_feedback(hire_id, agent_id, client_address)` now and waits | Another wallet connected: "Rate with the wallet that paid". Authorization cannot be recorded: "Rating unavailable right now", **Retry**; **Send** stays disabled until it is confirmed | +| 3 | `S10-rate-sheet` | Picks a score and writes an optional comment | Validates: score 1–5, comment ≤ 500 characters | Comment too long: inline error, **Send** disabled | +| 4 | `S10-rate-sheet` (wallet popup) | Signs the feedback | Submits `give_feedback` with this `hire_id`; the contract checks and consumes the authorization in the same call | Rejects: "Rating not sent", stay on S10 | +| 5 | `S10-rate-sheet` | — | Waits for confirmation | `HireAuthorizationConsumed`: "You already rated this hire", back to S06. `HireNotAuthorized` (should not happen after step 2): back to step 2 | +| 6 | `S06-hire-detail` | — | Status **Rated**; the new score counts in the agent's rating on S03 | — | ---