Repository navigation
feat(settlement-ui): B05 authorization and settlement details slice - #36
Merged
Merged
Conversation
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.
Deploying with
|
| 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
@oneshot/settlement-ui, the B05 Lane B frontend slice for Privyauthorization 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
UNKNOWNnon-terminal with no action; no confirmation countsHandoff artifact:
packages/settlement-ui/README.mdrecords the componententry point, route slot, fixtures, mock-server version
1.0.0, and thecomposition note.
Product and security invariants
Invariant notes:
transition. This is a read-only presentational slice; the
SettlementClientinterface has a single
readIntentmethod, and no component renders abutton, form, or input.
UNKNOWNreconciles without blind retry:UNKNOWNrenders as "Notfinal" 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.
through
bigintstring arithmetic. A malformed amount renders as malformedrather than as a rounded number.
COMMITTEDdurablestate, a well-formed Arc identity, and an authoritative Arc observation.
Settlement.explorer_urlis an unconstrainedbounded 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.
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:
Independent review evidence
Gate A — exact candidate tree before push
Two rounds. Round two is the tree that was pushed.
Round 1
710614af76ae5c28e2c1f69b2c00480f47b623b7fe84cc22695d62f19be8ff83b6da9afd03fb39272fbc8bd9380eb18f1aeff192b84867fbfbc906c5free-pi-clideepseek-v4-flash(provider: free-pi)VERDICT: PASSexplorer host not pinned;
attempts.at(-1)assumed array recency; recordedinsertion count was wrong.
Round 2 — the reviewed and pushed tree
710614af76ae5c28e2c1f69b2c00480f47b623b750778da60c693f17bd3e61025ce0ad4903ce68c21b3664eef73e5e82d69a204858414b589cc0faf7free-pi-clideepseek-v4-flashVERDICT: PASSresidual risk rather than changed here:
assertNoSensitiveFieldsmatches field names, not values. Upstream OpenAPIsanitation plus known-field-only projection are the compensating controls.
recipient,business_intent_id,network,asset, andstaterenderwithout a control-character sweep; the seam validates them as bounded,
enum, or address fields upstream.
axe-corescan disablescolor-contrast; a deploy-time contrast auditis still owed.
latestAttempttreats an emptycreated_atas undated rather thanrejecting it. Safe and tested.
Round-two changes: explorer links must sit on a host allowlist defaulting to
testnet.arcscan.app(the host documented indocs/settlement/PROVIDER_SETUP.md), withallowedExplorerHostsfor otherdeployments and
FIXTURE_EXPLORER_HOSTSso the synthetic fixtures opt intotheir own host instead of widening the default; attempt selection is by latest
created_at; the insertion count is corrected. Package tests 173 to 186, rootsuite 778 to 791.
Gate B — exact remote PR head
1b3664eef73e5e82d69a204858414b589cc0faf750778da60c693f17bd3e61025ce0ad4903ce68c2free-pi-clideepseek-v4-flash(provider: free-pi)VERDICT: PASSrather than changed, so the reviewed tree stays the pushed tree:
assertNoSensitiveFieldsmatches field names, not values; upstream OpenAPIsanitation and known-field-only projection are the compensating controls.
axe-corescan disablescolor-contrast; a deploy-time contrast auditis still owed.
latestAttemptkeeps an undated attempt as fallback rather than rejectingit; safe and tested, but the seam is the intended validator.
pnpm format:checkfails on untrackedsubgraph/generated/*codegenoutput. Pre-existing and unrelated; prettier over the touched paths passes.
origin/develophas advanced past the recorded base(
7f4ad079fd4b3d45b2a6d36c9c00003751f51e7d); merge-time conflictresolution is a human step.
Reviewer evidence: tree identity confirmed equal across
REVIEWED_HEAD_TREE,GATE_A_TREE, and the localHEAD^{tree}; merge-base withdevelopequals therecorded base; every consumed field verified against the frozen generated types;
OPENAPI_MOCK_SERVER_VERSION1.0.0and all 7 frozen UI fixtures present;testnet.arcscan.appconfirmed againstdocs/settlement/PROVIDER_SETUP.md; nosecrets, 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.
Agent policy / repository-policyand all applicable CI checks pass.Risk and rollback
Residual risks:
testnet.arcscan.appmust pass itsown
allowedExplorerHosts; an empty list disables explorer links.origin/develophas advanced to7f4ad079fd4b3d45b2a6d36c9c00003751f51e7dsince the recorded base. Merge-time conflict resolution is a human step.
LIVE_NOT_RUNand human-gated. The slice closeson frozen and Lane B local fixtures, matching the A05 and C05 precedent.
READY,SUBMITTING, and revert states exist only as Lane B local fixturesbuilt from the frozen schema, because published fixture digests are immutable.
Rollback: the change is additive. Reverting the commit removes the package, the
tsconfig.jsonreference slot, and the lockfile importer; no other packageimports it yet.
Human merge