Skip to content

feat(flutter): hire detail screen (S06, /hires/:id) that polls getHire until final and shows the agent's result, plus PULS3_HIRE for the Pages build - #150

Merged
moises-cisneros merged 1 commit into
feat/136-app-wallet-sign-infrom
feat/28-hire-detail-screen
Oct 10, 2026
Merged

moises-cisneros merged 1 commit into
feat/136-app-wallet-sign-infrom
feat/28-hire-detail-screen

Conversation

@XxHugheadxX

@XxHugheadxX XxHugheadxX commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Refs #28 (deliverables 3 and 4). Stacked on #149, which handles the wallet sign-in getHire needs. Review #149 first; this PR's diff is only the S06 commit.

What changes

HireGateway.getHire(hireId, consumer) → HireProgress

  • ServerHireGateway calls client.hire.getHire. It maps HireNotFound, HireNotOwned and InvalidHireId to the new HireNotFound, NotAuthenticated to HireNotSignedIn, and chain or persistence outages to HireBackendUnavailable.
  • The payment hash is hire.paymentTransaction, or the latest submission when its purpose is fund. The explorer link is taken only from that fund submission, never from a createJob or submit one.
  • HireStage derives what S06 shows from status + runtimeStatus + result: awaitingPayment, queued, running, delivered, runFailed, submitted, completed, rejected, expired. A succeeded run stays running on the server until its submit lands, so funded + result reads as Result ready. isFinal is true for runFailed, completed, rejected and expired.
  • FakeHireGateway (demo) creates no hire, so getHire returns HireNotFound.

/hires/:id: HireDetailScreen (container) and HireDetailView (organism)

  • Polls every 4 s until the stage is final.
  • If a refresh fails, the screen keeps the last status with a Retry banner and stops polling until Retry.
  • The status chip uses the feat: Hire lifecycle state machine #10 names: Funded · Queued / Running / Result ready / Run failed, Submitted, Completed, Rejected, Cancelled (a reject from open), Expired.
  • Steps: paid into the escrow → agent run (shows failureReason when it failed) → result recorded on Stellar → approved.
  • The result is scrollable, with Copy. The task is shown, along with the price held in escrow and View payment on StellarExpert.
  • With no wallet, the screen shows Connect wallet. With no session, it shows Sign in with wallet: SEP-10 through ensureSignedIn, then it loads the hire. An unknown or foreign hire, or an id that is not a number, shows Hire not found and makes no call.

Hire sheet: the confirmed state gets View hire, which closes the sheet and opens /hires/<id>. The demo result never shows it.

Pages: scripts/cloudflare-pages-build.sh passes PULS3_HIRE as --dart-define=HIRE=…. If it is unset, the build stays the labelled demo. The script test covers the new flag, and the server README says when to set it.

Docs: screens.md and flows.md mark S06 as partly built (read-only).

Not in this PR

Gates

  • dart format: no changes.
  • flutter analyze: 0 issues.
  • flutter test: 290 passed.
  • flutter build web --release --dart-define=HIRE=server: OK.
  • bash scripts/tests/cloudflare-pages-build.test.sh: 8/8.
  • Mutation check: 11 of 12 mutants fail a test. The survivor was a redundant demo guard on View hire (the demo view never shows the button), so I removed the guard.
    • The mutants cover: delivered stage, isFinal, cancel, fund-only link, HireNotOwned mapping, polling stop, wallet listener, invalid id, server explorer link, failed-run step, and last status kept on a refresh error.

Demo use

  1. Once the server is redeployed with wallet sign-in and the runtime (chore: deploy server to Serverpod Cloud and Flutter web app #31), set PULS3_HIRE=server and PULS3_API_URL in the Pages project.
  2. Hire Support Relay (registry 13), tap View hire, and the result appears when the run finishes.

…the agent's result

- HireGateway.getHire returns HireProgress (from HireDetail): escrow
  status, run status, failure reason, result and the fund tx. HireStage
  derives Funded/Queued/Running/Result ready/Run failed/Submitted/
  Completed/Rejected (Cancelled from open)/Expired; a succeeded run that
  is still `running` with a result reads as Result ready.
- ServerHireGateway maps HireNotFound/HireNotOwned/InvalidHireId to
  HireNotFound and takes the payment link from the fund submission only.
- /hires/:id (HireDetailScreen + HireDetailView): polls every 4 s until
  the hire is final, keeps the last status with a Retry banner when a
  refresh fails, asks to connect or sign in (SEP-10) when needed, copies
  the result, opens the payment on StellarExpert.
- The hire sheet's confirmed state gets "View hire".
- Pages build: PULS3_HIRE sets the HIRE dart-define (server or demo).
- Approve and Reject are not offered: they need the server-signed
  submit (#97).

Refs #28
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Deploying puls3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 85595d6
Status: ✅  Deploy successful!
Preview URL: https://c4bbb85a.puls3-4lw.pages.dev
Branch Preview URL: https://feat-28-hire-detail-screen.puls3-4lw.pages.dev

View logs

@moises-cisneros
moises-cisneros merged commit 576b705 into feat/136-app-wallet-sign-in Oct 10, 2026
1 check passed
@moises-cisneros
moises-cisneros deleted the feat/28-hire-detail-screen branch October 10, 2026 12:37
moises-cisneros added a commit that referenced this pull request Oct 10, 2026
…e with challenge checks, session on the Serverpod client) (#149)

* feat(flutter): sign in with the wallet (SEP-10) before a real hire

Since #147 the server only accepts hire calls from a wallet-bound session.
The app only read the Freighter address, so with HIRE=server every hire
ended in "Sign-in with your wallet is not available yet". It also could not
sign the challenge: FreighterWallet.signTransaction requires every source to
be the connected account (#69), but a SEP-10 challenge's source is the
server.

Wallet:
- WalletPort.signChallenge(SignInChallenge), with assertSep10Challenge:
  sequence 0 (never submittable), only manage_data operations, the first one
  "<home domain> auth" from the connected account, every other one from the
  server (the transaction source, never the account), time bounds that
  include now, and the server key and home domain when the app knows them.
  After signing, the body must be unchanged and carry a new signature from
  the connected account (shared with signTransaction).
- MockWallet and WalletController implement it.

Session:
- WalletSession: createChallenge, wallet signature, verifyChallenge, then
  the AuthSuccess is stored by the client's FlutterAuthSessionManager, which
  sends it on every call. A session is reused only for the wallet that
  opened it; a challenge issued for another wallet is never signed.
- main.dart sets client.authSessionManager and reads serverSigningKey and
  homeDomain from config.json auth when the server publishes them.

Hire flow:
- HireGateway.ensureSignedIn / forgetSession (no-op in the demo). The flow
  signs in after connecting and before createHire (new step signingIn). A
  session the server refuses (NotAuthenticated, AuthenticationUnavailable)
  or a refused sign-in (ChallengeExpired, ChallengeConsumed,
  ChallengeNotFound, InvalidWalletSignature: HireSignInFailed) is dropped,
  so Try again signs in afresh. ChallengeRateLimited maps to "too many
  sign-in attempts".

Tests: SEP-10 challenges built like the server's codec, signed by a real
key through the fake Freighter bridge (valid, and every refusal); session
reuse, wallet switch, wrong-wallet challenge, declined and refused
sign-ins; the sheet signs in before createHire and recovers after a refusal;
readAuthConfig. 267 app tests pass, web release build succeeds, and
breaking each of the 10 guards fails a test.

Refs #136

* feat(flutter): hire detail screen (S06) that polls getHire and shows the agent's result (#150)

- HireGateway.getHire returns HireProgress (from HireDetail): escrow
  status, run status, failure reason, result and the fund tx. HireStage
  derives Funded/Queued/Running/Result ready/Run failed/Submitted/
  Completed/Rejected (Cancelled from open)/Expired; a succeeded run that
  is still `running` with a result reads as Result ready.
- ServerHireGateway maps HireNotFound/HireNotOwned/InvalidHireId to
  HireNotFound and takes the payment link from the fund submission only.
- /hires/:id (HireDetailScreen + HireDetailView): polls every 4 s until
  the hire is final, keeps the last status with a Retry banner when a
  refresh fails, asks to connect or sign in (SEP-10) when needed, copies
  the result, opens the payment on StellarExpert.
- The hire sheet's confirmed state gets "View hire".
- Pages build: PULS3_HIRE sets the HIRE dart-define (server or demo).
- Approve and Reject are not offered: they need the server-signed
  submit (#97).

Refs #28

* fix(env): document PULS3_HIRE read by the Pages build script

---------

Co-authored-by: Moises Cisneros <110249336+moises-cisneros@users.noreply.github.com>
Co-authored-by: moises-cisneros <moisescisnerosdl@gmail.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.

2 participants