Skip to content

feat(settlement-ui): B05 authorization and settlement details slice - #36

Merged
selezenart merged 2 commits into
developfrom
milestone/b05-frontend-settlement-details
Sep 8, 2026
Merged

selezenart merged 2 commits into
developfrom
milestone/b05-frontend-settlement-details

Conversation

@selezenart

@selezenart selezenart commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds @oneshot/settlement-ui, the B05 Lane B frontend slice for Privy
authorization and Arc settlement details. It renders sanitized API fields from
the Gate P4 frozen OpenAPI seam and exposes no settlement, retry, resend, or
policy-override action.

Entry points: SettlementDetailsRoute (route slot), SettlementDetailsPanel
(presentational composition), and the four panels individually. The application
shell and its route registry are untouched, so Gate P5 composition stays a
single-editor change.

Scope and acceptance criteria

  • The change is limited to the stated milestone or issue.
  • Acceptance criteria are listed and satisfied.
  • No unrelated cleanup is included.
Task Result
B05.1 Policy summary from sanitized fields; no keys, signatures, or raw policy PASS
B05.2 Authorization states distinct; denials explained without a bypass PASS
B05.3 Settlement states; UNKNOWN non-terminal with no action; no confirmation counts PASS
B05.4 Verified transaction details; validated outbound explorer URL PASS
B05.5 Component tests, redaction, hostile input, accessibility, responsive PASS

Handoff artifact: packages/settlement-ui/README.md records the component
entry point, route slot, fixtures, mock-server version 1.0.0, and the
composition note.

Product and security invariants

  • Tenant isolation remains fail-closed.
  • Sponsor authorization, auditability, and daily caps remain enforced where applicable.
  • Recipients cannot modify sponsor controls or access sponsor-only data.
  • No secret, token, production identifier, or personal data is committed or pasted into review prompts.
  • Any non-applicable invariant is explained below.

Invariant notes:

  • One intent, at most one committed settlement: not applicable as a state
    transition. This is a read-only presentational slice; the SettlementClient
    interface has a single readIntent method, and no component renders a
    button, form, or input.
  • UNKNOWN reconciles without blind retry: UNKNOWN renders as "Not
    final" with an explicit note that no new settlement action is available.
    Missing, lagging, or unavailable evidence is framed as not proof that no
    payment happened.
  • Money representation: amounts format from canonical integer atomic units
    through bigint string arithmetic. A malformed amount renders as malformed
    rather than as a rounded number.
  • Verified evidence only: transaction details require COMMITTED durable
    state, a well-formed Arc identity, and an authoritative Arc observation.
  • Outbound link safety: Settlement.explorer_url is an unconstrained
    bounded string in the frozen schema, so the consumer validates it: https
    only, no embedded credentials, and it must reference the exact transaction
    hash being displayed. Anything else is dropped with a stated reason.
  • Redaction: a response carrying a secret-shaped field name is refused
    before projection and the route renders "Response withheld".

Test matrix cases selected (.agent/TEST_MATRIX.md): Privy denial;
crash-after-submission / lost payment response; Graph delay, absence, or
ambiguity. Backend-only cases remain proven by A03/A04 and Gate P4.

Validation

Commands and results:

pnpm --filter @oneshot/settlement-ui verify: PASS (format, lint, typecheck, 186 tests, build)
pnpm lint: PASS
pnpm typecheck: PASS
pnpm test: PASS (53 files, 791 tests; base was 49 files, 605 tests)
pnpm check:generated: PASS ("Generated contracts are current")
pnpm validate:fixtures: PASS (9 contracts-v1, 7 ui-v1)
npx markdownlint-cli2 on added Markdown: PASS
pnpm format:check: FAILS only on untracked subgraph/generated/* codegen output
  (ignored by subgraph/.gitignore, absent from .prettierignore); prettier --check
  over every path this branch touches: PASS

Independent review evidence

Gate A — exact candidate tree before push

Two rounds. Round two is the tree that was pushed.

Round 1

  • Base commit SHA: 710614af76ae5c28e2c1f69b2c00480f47b623b7
  • Candidate tree SHA: fe84cc22695d62f19be8ff83b6da9afd03fb3927
  • Candidate commit SHA: 2fbc8bd9380eb18f1aeff192b84867fbfbc906c5
  • Reviewer tool: free-pi-cli
  • Reviewer model: deepseek-v4-flash (provider: free-pi)
  • Verdict: VERDICT: PASS
  • Findings: no blocking findings. Three non-blocking, all addressed in round two:
    explorer host not pinned; attempts.at(-1) assumed array recency; recorded
    insertion count was wrong.

Round 2 — the reviewed and pushed tree

  • Base commit SHA: 710614af76ae5c28e2c1f69b2c00480f47b623b7
  • Candidate tree SHA: 50778da60c693f17bd3e61025ce0ad4903ce68c2
  • Candidate commit SHA: 1b3664eef73e5e82d69a204858414b589cc0faf7
  • Reviewer tool: free-pi-cli
  • Reviewer model: deepseek-v4-flash
  • Verdict: VERDICT: PASS
  • Findings or residual risks: no blocking findings. Four non-blocking, carried as
    residual risk rather than changed here:
    1. assertNoSensitiveFields matches field names, not values. Upstream OpenAPI
      sanitation plus known-field-only projection are the compensating controls.
    2. recipient, business_intent_id, network, asset, and state render
      without a control-character sweep; the seam validates them as bounded,
      enum, or address fields upstream.
    3. The axe-core scan disables color-contrast; a deploy-time contrast audit
      is still owed.
    4. latestAttempt treats an empty created_at as undated rather than
      rejecting it. Safe and tested.

Round-two changes: explorer links must sit on a host allowlist defaulting to
testnet.arcscan.app (the host documented in
docs/settlement/PROVIDER_SETUP.md), with allowedExplorerHosts for other
deployments and FIXTURE_EXPLORER_HOSTS so the synthetic fixtures opt into
their own host instead of widening the default; attempt selection is by latest
created_at; the insertion count is corrected. Package tests 173 to 186, root
suite 778 to 791.

  • The reviewed tree equals the committed tree.

Gate B — exact remote PR head

  • Pull request URL/number: feat(settlement-ui): B05 authorization and settlement details slice #36 feat(settlement-ui): B05 authorization and settlement details slice #36
  • Remote head commit SHA: 1b3664eef73e5e82d69a204858414b589cc0faf7
  • Remote head tree SHA: 50778da60c693f17bd3e61025ce0ad4903ce68c2
  • Reviewer tool: free-pi-cli
  • Reviewer model: deepseek-v4-flash (provider: free-pi)
  • Verdict: VERDICT: PASS
  • Findings or residual risks: no blocking findings. Non-blocking, all carried
    rather than changed, so the reviewed tree stays the pushed tree:
    1. assertNoSensitiveFields matches field names, not values; upstream OpenAPI
      sanitation and known-field-only projection are the compensating controls.
    2. The axe-core scan disables color-contrast; a deploy-time contrast audit
      is still owed.
    3. latestAttempt keeps an undated attempt as fallback rather than rejecting
      it; safe and tested, but the seam is the intended validator.
    4. pnpm format:check fails on untracked subgraph/generated/* codegen
      output. Pre-existing and unrelated; prettier over the touched paths passes.
    5. origin/develop has advanced past the recorded base
      (7f4ad079fd4b3d45b2a6d36c9c00003751f51e7d); merge-time conflict
      resolution is a human step.

Reviewer evidence: tree identity confirmed equal across REVIEWED_HEAD_TREE,
GATE_A_TREE, and the local HEAD^{tree}; merge-base with develop equals the
recorded base; every consumed field verified against the frozen generated types;
OPENAPI_MOCK_SERVER_VERSION 1.0.0 and all 7 frozen UI fixtures present;
testnet.arcscan.app confirmed against docs/settlement/PROVIDER_SETUP.md; no
secrets, TODOs, stray logging, or ignored-file modifications in the diff.

Milestone readiness: scope and acceptance criteria PASS, tests and required
checks PASS, security/privacy/secrets PASS, documentation and operations PASS,
ready for human review PASS.

  • Gate B reviewed the current remote head and matches Gate A's approved tree, or a fresh Gate A was run for the changed tree.
  • Agent policy / repository-policy and all applicable CI checks pass.

Risk and rollback

Residual risks:

  • Secret detection matches field names, not values.
  • A deployment whose explorer differs from testnet.arcscan.app must pass its
    own allowedExplorerHosts; an empty list disables explorer links.
  • origin/develop has advanced to 7f4ad079fd4b3d45b2a6d36c9c00003751f51e7d
    since the recorded base. Merge-time conflict resolution is a human step.
  • Live Arc testnet evidence is LIVE_NOT_RUN and human-gated. The slice closes
    on frozen and Lane B local fixtures, matching the A05 and C05 precedent.
  • READY, SUBMITTING, and revert states exist only as Lane B local fixtures
    built from the frozen schema, because published fixture digests are immutable.

Rollback: the change is additive. Reverting the commit removes the package, the
tsconfig.json reference slot, and the lockfile importer; no other package
imports it yet.

Human merge

  • A human owner has reviewed the evidence and will perform the merge.

Adds @oneshot/settlement-ui, the Lane B frontend slice for Privy policy,
authorization state, settlement state, and verified Arc transaction
evidence, built against the Gate P4 frozen OpenAPI seam and mock server
1.0.0.

Rendering rules the slice enforces:

- Transaction details require a COMMITTED durable state, a well-formed Arc
  identity, and an authoritative Arc observation. Anything less renders as
  unverified with the hash withheld.
- An explorer URL becomes an href only when it is https, carries no
  embedded credentials, and references the exact transaction hash on
  screen. The OpenAPI field is an unconstrained bounded string, so this
  validation is the consumer's responsibility.
- A response carrying a secret-shaped field name is refused before
  projection, so no part of it reaches a component prop.
- Amounts format from integer atomic units through bigint string
  arithmetic. A malformed amount renders as malformed, never rounded.
- UNKNOWN renders as non-terminal with no settlement action. The slice
  exposes no submit, resend, force-pay, or policy-override control, and
  the client interface is read-only by construction.

Five Lane B local fixtures cover states the frozen pack does not carry
(READY, SUBMITTING, on-chain revert, hostile explorer link, committed
without Arc evidence); published fixture digests stay immutable.
@cloudflare-workers-and-pages

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

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
oneshot 1b3664e Sep 08 2026, 08:03 PM

Addresses the Gate A non-blocking findings on the B05 slice.

An explorer link now has to sit on a host allowlist, defaulting to the
Arc testnet explorer documented in docs/settlement/PROVIDER_SETUP.md.
Hash binding alone could not stop a hostile host quoting the real
transaction hash back, so the allowlist is what keeps a spoofed response
from producing a clickable link. SettlementDetailsRoute accepts
allowedExplorerHosts for a deployment on a different explorer.

The synthetic fixtures publish links through a host that is not the
documented explorer, so FIXTURE_EXPLORER_HOSTS lets the fixture viewer
and component tests opt into it rather than widening the default. Under
the default allowlist those fixture links are refused, and a test
asserts exactly that.

The displayed attempt is now selected by latest created_at instead of
array position, which the contract never promised. An equal timestamp
keeps the later element, and an attempt whose timestamp cannot be parsed
never displaces one that carries a usable date.

Also corrects the insertion count recorded for the first commit.
@selezenart
selezenart marked this pull request as ready for review September 8, 2026 20:52
@selezenart
selezenart merged commit 5f845c8 into develop Sep 8, 2026
4 checks passed
@SuPuHe
SuPuHe deleted the milestone/b05-frontend-settlement-details branch September 8, 2026 22:08
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.

1 participant