Skip to content

Feat/27 deploy flow - #112

Merged
TOMOKI977 merged 4 commits into
mainfrom
feat/27-deploy-flow
Oct 8, 2026
Merged

TOMOKI977 merged 4 commits into
mainfrom
feat/27-deploy-flow

Conversation

@Pericena

@Pericena Pericena commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Refs #27. Part B of the #109 split; supersedes #109.

Missing for #27: testnet end to end with GIF and tx hash. It needs the deploy endpoint (#18) and the relay (#96). Wiring them means a DeployGateway implementation over the generated client, plus the set_agent_wallet signature, draftId and session polling from api.md.

What it does

  • DeployFlow + DeployFlowController: preparing → waiting for signature → registering on-chain → activating → live. Never runs twice, resumes from the failed step, keeps the prepared and signed transaction across a lost response, verifies backend answers, stops if closed mid-run.
  • Errors with recovery and no reload: signature rejected, transaction failed, backend error, wallet unavailable, wrong network, account changed, connection lost/timeout, invalid response, expired preparation (prepares again), wallet that never answers (times out; the sheet can be closed while the wallet prompt is open).
  • Embeddable: DeployFlow takes an AgentDraft only, no dependency on the Studio form.

Review fixes from #109

  • Demo gateway labelled: banner during the flow, result "Demo deploy only", no "live on Stellar", no explorer link, no agent page, no made-up hash. Demo agents are not listed.
  • WalletPort.signTransaction signs and returns the signed XDR; the server submits. MockWallet returns a marked signed placeholder, not a hash.
  • Real agents start at 0.0; stellarExpertTxUrl; unused SuccessPanel, DeployProgressView, AppScope.ids removed.

Tests

Every progress and error state with MockWallet and a fake client, plus the review fixes (demo labelling, signed envelope, expired preparation, wallet timeout, cancellation).

flutter analyze: 0 issues · flutter test: 121/121

Stacked on the theme and shell PR (part A): base is feat/flutter-theme-shell so the diff shows only the deploy flow. I'll retarget to main after A merges.

@TOMOKI977 TOMOKI977 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on the split, and thanks for addressing the #109 review so carefully. The blockers from that round are resolved:

  • The demo is labelled everywhere: isDemo is required on the gateway, the app shows "Demo deploy only", and there's no fake hash or explorer link.
  • WalletPort now only signs and returns the signed XDR, matching the relay.
  • PreparationExpired re-prepares on retry.
  • The explorer URL uses stellarExpertTxUrl.
  • Disposal is guarded with _Cancelled.

The controller's resume logic and the tests around it are solid.

I'm requesting changes for one thing only:

A wallet that never answers connect() traps the user. _wallet.connect() runs inside the preparing step without a timeout (deploy_flow_controller.dart:189). busy only lets the user leave during awaitingSignature (deploy_flow.dart:109-111), so the Close button is hidden and PopScope blocks back, and the sheet is isDismissible: false with enableDrag: false (deploy_sheet.dart:20-21). With a real wallet (#113), an ignored or stuck connect popup leaves the restart as the only way out. Either put the same timeout on connect() that signTransaction has, or let the user close while the wallet connect is pending. Please add a test with a connect() that never completes, like the one you have for signing.

Not blocking, but worth keeping in mind for #18/#96:

  • retry() decides what to discard by error kind, not by step (deploy_flow_controller.dart:169-176). Per api.md:163,169, set_agent_wallet can fail with TransactionFailed/AuthorizationExpired while the agent stays registered. If the real activate maps those to the same kinds, Retry would drop _registration and register a second agent.
  • When the signature times out, the original signTransaction future keeps running, so a retry can open a second wallet prompt.
  • The permanent submit rejections (EnvelopeMismatch, InvalidSignedEnvelope, PreparationNotFound, SubmissionInProgress) have no error kind, so they'd fall into "retry is safe" and resend the same payload.
  • activate() has no second signature for set_agent_wallet (api.md:157), so the claim in deploy_gateway.dart that the real gateway drops in "without touching the flow" won't hold.
  • Copy is split between deploy_copy.dart and literals in three organisms. The step-timeout message is built in the controller and prints "null s" when there's no timeout (deploy_flow_controller.dart:320). User-facing text cites "#18".
  • The agent ID and transaction rows are built twice (flow view and success view), and isDemo is passed separately from result.isDemo.
  • The non-demo publish path in DeploySheet._publish has no test.
  • The title "Feat/27 deploy flow" isn't a conventional commit title. Suggested: feat(flutter): deploy flow UI with stepper and error recovery, plus the area: frontend / type: feat labels.

@TOMOKI977

Copy link
Copy Markdown
Contributor

@Pericena Heads-up: CI changes when #114 (Serverpod 4.0.4) merges. Once it lands on main:

  1. Upgrade locally to Flutter 3.44.4 (Dart 3.12.2) and serverpod_cli 4.0.4. The steps are in docs/operations/serverpod-4-migration.md.
  2. Update your branch from main and run dart pub get again. The lockfile moves to Serverpod 4.0.4, and checks run with the new toolchain.
  3. Generated code and migrations: if you touch a *.spy.yaml or an endpoint, run serverpod generate with 4.0.4 and commit the output, since CI fails on any generated diff. Create new migrations with 4.0.4 too, so they sort after upgrade-4-0.
  4. Two new required CI jobs:
    • docker builds the server image.
    • scripts runs the offline tests in scripts/tests/, including the env inventory: every PULS3_* variable read in code must be listed in .env.example. Secrets stay commented out there, with an entry in docs/infra/secrets.md.
  5. Cloudflare Pages builds with scripts/cloudflare-pages-build.sh, which pins the same Flutter version as CI.

Specific to this PR: #111 was squash-merged, so please rebase onto main and drop 2dcfc9a (the original theme/shell commit) when you push the connect() timeout fix.

Pericena and others added 3 commits October 7, 2026 23:55
Part B of the #109 split. Refs #27: the testnet end-to-end criterion
waits for the deploy endpoint (#18) and the relay (#96).

- DeployFlow + DeployFlowController: preparing -> waiting for signature
  -> registering on-chain -> activating -> live. Never runs twice,
  resumes from the failed step, keeps the prepared and signed
  transaction across a lost response, verifies every backend answer,
  and stops if the flow is closed mid-run.
- Presentational widgets: DeployStepper, phase card, error panel and
  success, with skeletons until values arrive.
- Errors with a recovery action and no reload: signature rejected,
  transaction failed, backend error, plus wallet unavailable, wrong
  network, account changed, connection lost/timeout, invalid response,
  expired preparation (prepares again) and a wallet that never answers
  (times out; the sheet can be closed while the wallet prompt is open).

Review fixes:
- The demo gateway is labelled: a banner says nothing is registered on
  Stellar, and the result reads "Demo deploy only" with no explorer
  link, no agent page and no made-up hash. Demo agents are not listed.
- WalletPort.signTransaction only signs and returns the signed XDR; the
  server submits (API contract #77, Decision A). MockWallet returns a
  marked signed placeholder instead of a hash.
- Real agents start at a 0.0 rating; explorer links use
  stellarExpertTxUrl; the unused SuccessPanel, DeployProgressView and
  AppScope.ids are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #112. The deploy flow connected to the wallet inside the
preparing step with no limit, and the sheet could not be closed then,
so a stuck connect popup trapped the user.

- connect() gets the same wallet timeout as signTransaction(), and
  "No answer from your wallet" offers a retry.
- The flow can be closed while any wallet prompt (connect or sign) is
  open; server steps stay bounded by the step timeout.
- The timeout detail no longer prints "null s" without a step timeout,
  and user-facing text no longer cites issue numbers.

Test: a connect that never completes times out, nothing is prepared,
and the user can close the flow while waiting. 122 passing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dart 3.12 (Serverpod 4 toolchain on main) flags prefer_initializing_formals
for private fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Pericena
Pericena force-pushed the feat/27-deploy-flow branch from 5862f38 to bd73747 Compare October 8, 2026 15:10
@cloudflare-workers-and-pages

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

Copy link
Copy Markdown

Deploying puls3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4e9f325
Status: ✅  Deploy successful!
Preview URL: https://c77db044.puls3-4lw.pages.dev
Branch Preview URL: https://feat-27-deploy-flow.puls3-4lw.pages.dev

View logs

@Pericena

Pericena commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks! Done:

  • Rebased onto main and dropped 2dcfc9a (feat(flutter): mobile-first theme and app shell #111 was squash-merged). The pubspec conflict is resolved on Serverpod 4.0.4.
  • connect() now has the same wallet timeout as signTransaction(), and the flow can be closed while any wallet prompt (connect or sign) is open. New test: a connect() that never completes times out, nothing is prepared, and the user can close.
  • Also fixed: "null s" in the step-timeout message, and user-facing text no longer cites issue numbers.

The other non-blocking notes (retry by step, superseded signature futures, permanent submit rejections, set_agent_wallet signature) will go with #18/#96.

@Pericena
Pericena requested a review from TOMOKI977 October 8, 2026 15:17
@Pericena Pericena added area: frontend Flutter implementation type: feat New functionality labels Oct 8, 2026

@TOMOKI977 TOMOKI977 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The blocker is resolved:

  • _waitForWallet() puts the same timeout on connect() and signTransaction(), and isWaitingForWallet lets the user close the flow during either prompt.
  • The finally notify is guarded by _disposed, so closing mid-prompt is safe.
  • The new test (a connect that never answers, close while waiting, walletTimeout shown, nothing prepared) covers exactly the case.
  • I checked the rebase with range-diff: only the Serverpod 4.0.4 bump, the fix and the style commit. Nothing else changed.

Thanks for the "null s" fix and for removing the issue numbers from the copy. The deferred notes for #18/#96 sound good.

I'll update the branch with main and squash-merge once CI is green.

@TOMOKI977
TOMOKI977 merged commit 71b577c into main Oct 8, 2026
10 checks passed
@TOMOKI977
TOMOKI977 deleted the feat/27-deploy-flow branch October 8, 2026 15:34
Pericena added a commit that referenced this pull request Oct 8, 2026
Refs #25. Built on the deploy flow (#112). The real-wallet acceptance
on Testnet (screenshot) and the server relay (#96/#18) are pending.

- WalletPort keeps one signing boundary: connect, disconnect, address,
  network, signTransaction (returns the signed XDR, the server submits)
  and signAuthEntry. Typed errors: rejected, not installed, locked/
  unavailable, wrong network, account changed, invalid payload.
- FreighterWallet over a small JS bridge (freighter-api 6.0.1). Before
  any prompt it checks the payload (#69): Testnet passphrase, no
  fee-bump envelopes, no trailing bytes, auth entries with a non-zero
  expiration and address credentials only. After signing it checks the
  account did not change.
- MockWallet for tests and the demo console (--dart-define=WALLET=mock);
  Freighter is the default on the web.
- Wallet sheet (S09): disconnected, connecting, connected (Testnet
  badge, copyable address, disconnect), cancelled, wrong network, not
  installed (install link), locked, with a retry in every error.
- Demo flows (deploy, hire) sign a harmless, never-submitted Testnet
  envelope so a real wallet shows its prompt; they stay labelled as
  demos. Wallet errors show human messages.

Tests: 19 for the Freighter adapter checks, 11 for the connection
states, chip and phone layout. 151 passing, analyze clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pericena added a commit that referenced this pull request Oct 8, 2026
Review of #113.

- WalletController.connect() times out (2 minutes) with WalletTimedOut,
  so an ignored, blocked or stuck Freighter popup never traps the deploy
  flow, the hire sheet or the wallet sheet. The deploy flow also times
  out its reconnect before re-signing and can be closed while any wallet
  prompt is open (from #112).
- Disconnecting during a pending connect stays disconnected instead of
  reconnecting when the prompt settles.
- The Freighter bridge maps only "rejected:" to a rejection; any other
  error is WalletUnavailable with a log line (no payload data), not
  "you rejected".
- An unknown --dart-define=WALLET value fails at startup instead of
  silently using the mock wallet.
- Wallet sheet uses Puls3Breakpoints.narrowGutter; one shared waiting
  bar; README lists signAuthEntry and the timeout.

Tests: two Freighter tests now await the future before checking state,
so they can fail; new tests for a connect that never answers, disconnect
during connect, an account switch while signing, and the timed-out
panel. 165 passing, analyze clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: frontend Flutter implementation type: feat New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants