Implements step 8 from the architecture gap-analysis — the first portal page backed by the CurrenciCombo orchestrator (PR A–F). Stacks on the live Solace portal branch (PR #2), not on the orchestrator branches, because this is a frontend-only change.
What lands
Routes
GET /transactions — list of recent plans. Columns: Plan ID, State (chip), Instrument hint, Owner, Updated, ›.
GET /transactions/:planId — detail view with three cards:
12-state machine grid from architecture §8. Current state glows green; visited states in indigo; non-reached states dimmed. Terminal states (COMMITTED / ABORTED / CLOSED) visually distinct.
Audit trail — from_state → to_state, actor, actor_role (SoD chip), reason, timestamp. Straight from the state_transitions table introduced by PR A.
Signed event stream — id, type, signature, prev_hash, timestamp. Straight from the events table introduced by PR D. Demonstrates the hash chain visually.
Backend wiring — src/services/orchestrator.ts
listPlans() → GET /api/plans
getPlanState(id) → GET /api/plans/:id/state
getPlanEvents(id) → GET /api/plans/:id/events
probeOrchestrator() → GET /health
When VITE_ORCHESTRATOR_URL is unset (orchestrator not yet deployed), every call returns deterministic demo data so the 12-state view and audit trail still render. Matches how proxmox / dbisCore already degrade. Each card gets a LIVE / DEGRADED / DEMO badge so it is obvious which is which.
UI chrome
New Transactions nav entry (GitBranch icon) between Transaction Builder and Accounts.
Orchestrator registered in backendCatalog so the existing Dashboard BackendStatusBar picks it up automatically (LIVE when VITE_ORCHESTRATOR_URL is set, MOCKED otherwise).
CSS
All new styles appended to src/index.css under a clearly-labelled /transactions page (PR G) block — no existing rules changed. Covers state-pill grid, source badges, state chips, role chips, portal table rows.
Out of scope
SSE live-stream UI. The endpoint exists on the orchestrator (PR D) but the detail page polls /events once on mount. Adding an EventSource subscription is a follow-up.
Pagination.listPlans() returns whatever the orchestrator returns — no page-size negotiation yet.
Write actions. No approve / release / validate buttons in this PR. Those need the full SoD identity layer (architecture §4.6) wired, which is a separate effort.
Verification
$ npx tsc --noEmit # clean
$ npm run build # built in 605ms
UI verification of the three cards + demo-data fallback will be performed in test mode after merge (or by a reviewer locally).
Series order
A → B → C → D → E → F → G → H.
Base: devin/1776532671-solace-bank-portal (the live-portal branch, PR #2). Diff here is G-only.
Implements **step 8** from the architecture gap-analysis — the first portal page backed by the CurrenciCombo orchestrator (PR A–F). Stacks on the live Solace portal branch (PR #2), not on the orchestrator branches, because this is a frontend-only change.
## What lands
### Routes
- `GET /transactions` — list of recent plans. Columns: Plan ID, State (chip), Instrument hint, Owner, Updated, `›`.
- `GET /transactions/:planId` — detail view with three cards:
1. **12-state machine grid** from architecture §8. Current state glows green; visited states in indigo; non-reached states dimmed. Terminal states (COMMITTED / ABORTED / CLOSED) visually distinct.
2. **Audit trail** — `from_state → to_state`, actor, actor_role (SoD chip), reason, timestamp. Straight from the `state_transitions` table introduced by PR A.
3. **Signed event stream** — id, type, signature, prev_hash, timestamp. Straight from the `events` table introduced by PR D. Demonstrates the hash chain visually.
### Backend wiring — `src/services/orchestrator.ts`
- `listPlans()` → `GET /api/plans`
- `getPlanState(id)` → `GET /api/plans/:id/state`
- `getPlanEvents(id)` → `GET /api/plans/:id/events`
- `probeOrchestrator()` → `GET /health`
When `VITE_ORCHESTRATOR_URL` is unset (orchestrator not yet deployed), every call returns **deterministic demo data** so the 12-state view and audit trail still render. Matches how `proxmox` / `dbisCore` already degrade. Each card gets a `LIVE` / `DEGRADED` / `DEMO` badge so it is obvious which is which.
### UI chrome
- New **Transactions** nav entry (GitBranch icon) between Transaction Builder and Accounts.
- Orchestrator registered in `backendCatalog` so the existing Dashboard `BackendStatusBar` picks it up automatically (`LIVE` when `VITE_ORCHESTRATOR_URL` is set, `MOCKED` otherwise).
### CSS
All new styles appended to `src/index.css` under a clearly-labelled `/transactions page (PR G)` block — no existing rules changed. Covers state-pill grid, source badges, state chips, role chips, portal table rows.
## Out of scope
- **SSE live-stream UI.** The endpoint exists on the orchestrator (PR D) but the detail page polls `/events` once on mount. Adding an `EventSource` subscription is a follow-up.
- **Pagination.** `listPlans()` returns whatever the orchestrator returns — no page-size negotiation yet.
- **Write actions.** No approve / release / validate buttons in this PR. Those need the full SoD identity layer (architecture §4.6) wired, which is a separate effort.
## Verification
```
$ npx tsc --noEmit # clean
$ npm run build # built in 605ms
```
UI verification of the three cards + demo-data fallback will be performed in test mode after merge (or by a reviewer locally).
## Series order
A → B → C → D → E → F → **G** → H.
Base: `devin/1776532671-solace-bank-portal` (the live-portal branch, PR #2). Diff here is G-only.
Ran live against a local Vite build at http://127.0.0.1:5173 with orchestrator intentionally unset (the DEMO fallback — only exercisable path until orchestrator ships). 2 tests, 14 sub-assertions, all passed.
Test 1 — /transactions list view — passed
Test 2 — /transactions/demo-pay-014 detail (12-state grid + audit trail + signed event stream) — passed
Test 1 — list (DEMO badge, 4 rows)
Test 2 — detail (COMMITTED, 8 audit rows, 8 events)
Adversarial DOM-assertion check (click to expand)
Headline adversarial check: the PARTIALLY_EXECUTED pill must be pending and notvisited (demo transitions never entered that state). A broken visited set would fail here. It passed, along with all other 11 pill-class checks.
=== PILL CLASSES ===
OK DRAFT {current:false,visited:true, pending:false,terminal:false}
OK INITIATED {current:false,visited:true, pending:false,terminal:false}
OK PRECONDITIONS_PENDING {current:false,visited:true, pending:false,terminal:false}
OK READY_FOR_PREPARE {current:false,visited:true, pending:false,terminal:false}
OK PREPARED {current:false,visited:true, pending:false,terminal:false}
OK EXECUTING {current:false,visited:true, pending:false,terminal:false}
OK PARTIALLY_EXECUTED {current:false,visited:false,pending:true, terminal:false}
OK VALIDATING {current:false,visited:true, pending:false,terminal:false}
OK COMMITTED {current:true, visited:false,pending:false,terminal:true }
OK ABORTED {current:false,visited:false,pending:true, terminal:true }
OK UNWIND_PENDING {current:false,visited:false,pending:true, terminal:false}
OK CLOSED {current:false,visited:false,pending:true, terminal:true }
=== COUNTS === {pillCount:12, auditRows:8, eventRows:8}
=== ROLE CHIPS (distinct) === submitter, coordinator, approver, releaser, validator
Audit row spot-checks (all OK):
Row 1: ∅ → DRAFT / ops.bob / submitter / plan created
Only the DEMO path is exercisable today (orchestrator not deployed). LIVE / DEGRADED branches are wired identically in code but not verified end-to-end.
MetaMask-connect flow was not re-verified here — covered by PR #2.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements step 8 from the architecture gap-analysis — the first portal page backed by the CurrenciCombo orchestrator (PR A–F). Stacks on the live Solace portal branch (PR #2), not on the orchestrator branches, because this is a frontend-only change.
What lands
Routes
GET /transactions— list of recent plans. Columns: Plan ID, State (chip), Instrument hint, Owner, Updated,›.GET /transactions/:planId— detail view with three cards:from_state → to_state, actor, actor_role (SoD chip), reason, timestamp. Straight from thestate_transitionstable introduced by PR A.eventstable introduced by PR D. Demonstrates the hash chain visually.Backend wiring —
src/services/orchestrator.tslistPlans()→GET /api/plansgetPlanState(id)→GET /api/plans/:id/stategetPlanEvents(id)→GET /api/plans/:id/eventsprobeOrchestrator()→GET /healthWhen
VITE_ORCHESTRATOR_URLis unset (orchestrator not yet deployed), every call returns deterministic demo data so the 12-state view and audit trail still render. Matches howproxmox/dbisCorealready degrade. Each card gets aLIVE/DEGRADED/DEMObadge so it is obvious which is which.UI chrome
backendCatalogso the existing DashboardBackendStatusBarpicks it up automatically (LIVEwhenVITE_ORCHESTRATOR_URLis set,MOCKEDotherwise).CSS
All new styles appended to
src/index.cssunder a clearly-labelled/transactions page (PR G)block — no existing rules changed. Covers state-pill grid, source badges, state chips, role chips, portal table rows.Out of scope
/eventsonce on mount. Adding anEventSourcesubscription is a follow-up.listPlans()returns whatever the orchestrator returns — no page-size negotiation yet.Verification
UI verification of the three cards + demo-data fallback will be performed in test mode after merge (or by a reviewer locally).
Series order
A → B → C → D → E → F → G → H.
Base:
devin/1776532671-solace-bank-portal(the live-portal branch, PR #2). Diff here is G-only.Test report —
/transactionspageRan live against a local Vite build at
http://127.0.0.1:5173with orchestrator intentionally unset (theDEMOfallback — only exercisable path until orchestrator ships). 2 tests, 14 sub-assertions, all passed./transactionslist view — passed/transactions/demo-pay-014detail (12-state grid + audit trail + signed event stream) — passedAdversarial DOM-assertion check (click to expand)
Headline adversarial check: the
PARTIALLY_EXECUTEDpill must bependingand notvisited(demo transitions never entered that state). A brokenvisitedset would fail here. It passed, along with all other 11 pill-class checks.Audit row spot-checks (all OK):
∅ → DRAFT/ops.bob/submitter/plan createdREADY_FOR_PREPARE → PREPARED/ops.chen/approver/approve — prepare (SoD)VALIDATING → COMMITTED/ops.eve/validator/reconciled + committed (SoD)Event stream spot-checks (all OK):
demo-sig-0000, prev_hash∅state.committedCaveats:
DEMOpath is exercisable today (orchestrator not deployed).LIVE/DEGRADEDbranches are wired identically in code but not verified end-to-end.Session: https://app.devin.ai/sessions/a33f9ebe303d413aab86e04252a1b4a1
07d47e726eto0bda0b76f1