Activity Overview
Commits and releases over time
- Commits
- Releases
- Authors
Repository Explorer
No repositories match that filter.
618 commits in all time
May 02, 2026 09:43 – Jul 31, 2026 09:43 UTC
feat: frontend: election-specific views on the period detail page
Implements the election frames of the Period Detail design handoff (claude.ai/design 'Period Detail.dc.html', frames 1/3/6). Each race on an election ballot now renders as its own card — E.0N chip, name, seat count and candidate count — instead of a bare h3 over a flat list of topic cards. What sits inside depends on the state: - scoring (active, eligible, simple) — one CandidateBallotCard per candidate: avatar, statement, and Support/Veto/Abstain chips that tint the card once picked. A legend band prices the chips (+1/-1/0) and a footer tracks 'n of m candidates scored'. - advanced — the existing TopicVoteCard allocation inputs, unchanged. Splitting power across the three options is still a valid ballot, and single-choice chips cannot express it. - read-only (logged out, ineligible, ended) — ElectionStandings: the candidates ranked by net score with 'in seat range' pills and a seat-cutoff note, replacing per-candidate tally bars that ranked nothing. - upcoming — the plain option list, inside the same card shell. Labels come off the on-chain options via classifyOption rather than being hardcoded, so a period seeded before the Veto rename still reads 'Against' in its own legend and standings. Grouping is presentation only: every card stays keyed by on-chain topic index, so ballot state and the vote() payload are untouched.
a5714bec
worktree-frac-design-import
4/456 ++ 23 --
chore: rename the election Against option to Veto
Election candidate ballots are Support / Veto / Abstain instead of Support / Against / Abstain, across the Manage UI that writes the on-chain options, the results/scoring copy, the docs pages, the Storybook fixtures and the sample-data seeders. ElectionCandidate.against is renamed to .veto to match. utils/vote.ts is untouched on purpose: its NO_RE already matched both 'against' and 'veto', so periods already on chain with Against labels keep tallying and colouring correctly alongside new Veto ones.
1d670fad
worktree-frac-design-import
13/47 ++ 49 --
feat: frontend: elections in the voter-facing UI
A period runs elections when its body carries `elect`; each candidate names its
race through its own topic body's `e`. The results page understood that, and so
did the operator UI — the ballot and every period summary did not. A voter
meeting a two-election period saw seven undifferentiated "topics" with no way to
tell which seat anyone was standing for.
"Candidate" is a presentation word for a topic in an election period: the
contract, the ABI and every SDK handle stay `topicIndex`, so this is a copy
change, not a rename. utils/periodTerms.ts holds the noun and the count label in
one place — the two existing `isElection ? … : …` ternaries had already drifted
apart (the add-candidate form said "Add topic" in its disconnected state).
Ballot (VotePeriodDetail):
- candidates group under per-race headings via the SDK's groupCandidates,
matching the results page. Ordering only: the state arrays, buildVotes() and
the vote() payload stay flat and keyed by on-chain topic index.
- candidates whose `e` is missing or names no declared election are dropped by
groupCandidates but still rendered, in a trailing section. The contract
requires every topic row to carry the voter's full power, so leaving one off
the ballot would make the period unvotable.
- the T.0N ordinal chip is suppressed for candidates — it would number straight
through races that are ranked separately.
- the two near-identical "View Ranked Results" blocks (one right-aligned, one
not) become one strip that also names each race and its seats.
Summaries now read "2 elections" / "5 candidates" / "3 topics": the period list
rows and column header, both heroes, the detail and results meta lines, the
sidebar info card, and the operator table. All of them already fetched the
period body except the operator table, which gains one read per row and a
TODO(perf) naming the batched reader that would collapse it.
Manage:
- assignment warnings move onto the page. They existed only inside the
Mark-ready dialog, so an operator who never opened it had no idea a candidate
was running in no race — and once the period is ready and a vote lands, the
topic set is frozen and it can no longer be fixed.
- the fix-ability message keyed off canEdit, which folds in `!!sdk`, so a
disconnected visitor was told the period was "no longer editable" when it was
simply not connected. Split into `editable` vs `canEdit`.
- the remaining unconditional "topic" strings (remove-confirm, body dialog,
placeholders, the disconnected add form) go through the shared terms.
Docs: /docs/periods gains an elections section and the glossary gains Election
and Candidate. Both spell out the rule that surprises people — you cast on every
candidate, including races you don't care about, and Abstain is how you sit one
out.
Also: deploy-sample-data declared `{ t: 'EAC', s: 1 }` while its own prose,
comments and console output all described a 2-seat treasury committee;
detailScenario accepted `elect` but hardcoded its topics, so the one election
story flagged an election over two untagged reward-policy topics. Both fixed,
plus multi-election detail stories and election rows in the list fixture.
Typecheck, lint and prettier are clean and the two ballot play-tests still pass.
Verified against localnet sample data (the active two-election period 2),
including a throwaway draft period carrying an untagged candidate to exercise
the manage callout and the trailing ballot section.
44b8c405
worktree-frac-design-import
21/511 ++ 159 --
feat: frontend: cast pooled ballots from the vote page
Pool members could see their prorated share but had no way to spend it. Make
staking-pool positions selectable rows in the vote page's account selector,
nested under the account that holds them — two levels deep for a pool belonging
to an account that delegated to you, which the frac contract honours the same
way it honours a delegated gGov vote.
Data layer:
- hooks/fracQueries.ts: usePooledBallot, the transpose of the account page's
hooks — many accounts against the one committee the period runs on. Folds in
the contract's canVote (authoritative: it mirrors every check vote() enforces)
for eligibility and AQ weight, plus each account's pooled vote records. Where
the shape matches it reuses the account page's query keys, so the two surfaces
share cache rather than re-reading the same boxes.
- a TODO(perf) on the per-position canVote fan-out names the batched registry
logger that would collapse it to one read per account, alongside the two
already noted for the account page.
- useFracVoteMutation, and a writer-enabled frac SDK memoised per wallet
identity. The reader stays separate on purpose: pooled reads must keep working
with no wallet connected.
Ballot:
- a pooled ballot is denominated in AlgoQuarters, so TopicVoteCard and the
allocation summary take a unit and show the approximate vote equivalent
alongside. The allocation rule is unchanged — every topic totals your weight.
- selecting a pooled position switches the signing account first, same rule as a
delegated vote; an account whose only power is pooled now auto-selects its
first eligible position instead of landing on a dimmed "Yourself" row.
- new "Pool not ready" status for a pool that hasn't snapshotted the period or
is still ingesting its AQ ledger. The member's own standing is fine, so "not
eligible" would misplace the blame.
- PooledSharePanel, a per-pool vote record card, and a teal `pooled` Callout
variant matching the tone used elsewhere for pooled voting.
Also:
- AccountSelector: id-keyed selection (an address or `{instance}:{owner}`),
a shared NestedRow for the two indent depths, and mobile truncation fixes —
long names were running under the status column instead of ellipsing.
- Storybook: pooled ballot mock + fixtures for every row status; MockPooledPosition
extends the account-page fixture so one shape drives both surfaces.
- localnet seeder gives bob a tALGO position on top of his delegation to alice,
making the two-levels-deep delegated case reachable; AppProviders passes the
KMD wallet name, without which VITE_KMD_WALLET was silently ignored and the
per-persona wallets had no effect.
- Home: the "how it works" primer moves above the pooled callout, so the callout
reads as a follow-on rather than interrupting the period list; tabs get
tablist/tab/tabpanel roles.
Typecheck and lint are clean. States are covered by Storybook fixtures; not yet
exercised against a live pool on-chain.
fdcdcc2b
worktree-frac-design-import
19/1,541 ++ 182 --
test(algoquarters): assert no round-boundary double-counting for tALGO
Encode the exact round-boundary scenario as regression tests: when an account holds tALGO and transfers its full balance at round T, the sender is credited [start, T) and the receiver [T, end) — the boundary round T is attributed to exactly one holder, and the two sides sum to the whole window (a boundary off-by-one would shift a whole AQ and fail the assertion). Also covers a chained A->B->C hop for the same one-round-one-holder invariant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Pjx7EdkEWdJbWKWLNBmwC
7993e2b0
claude/talgo-round-boundary-counting-8yfr5g
1/44 ++ 0 --
chore: frontend: TEMPORARY deploy-first localnet seed
Standalone copy of the seeder from chore/frac-sample-data-scripts, restructured
so every app deployment happens before any data seeding:
DEPLOY reset · fund · both registries · committee file · instances + escrows
· all three period apps
SEED AlgoQuarters · delegations · period bodies/topics/windows/ready
· ballots · verify
Every app ID therefore exists, and .env is written, before the slow data work
starts — a deployment failure surfaces in seconds instead of minutes, and vite
can be pointed at the registries while the seed is still running.
The committee-file upload sits in the DEPLOY phase because addPeriod needs a
committeeId; syncPeriod stays in SEED because it needs the period ready and the
committee already synced on the instance. createPeriodHelper is split
accordingly into createPeriodApp and populatePeriod, which also means every
period is fully populated before any ballot is cast rather than per-period —
a better fit for the rule that the first vote locks a period against editing.
Committed on its own, and deliberately not wired into deploy-sample-data.ts, so
this commit can be reverted wholesale once the real seeder lands. Does not
include that branch's AppProviders.tsx change for per-persona KMD wallets;
connect through unencrypted-default-wallet, which holds all four personas.
Verified end to end on localnet: 12/12 steps, tallies self-check, and the pooled
account/landing surfaces read correctly against the seeded chain.
c34e8df6
worktree-frac-design-import
4/937 ++ 0 --
feat: frontend: surface pooled voting power (fractional delegation)
Users of liquid staking (xALGO, tALGO) and staking pools (Reti) hold no gGov voting power directly — the pool's escrow accounts do — so the app gave them no indication they could vote at all. Surface their pro-rata share across the landing page, the account page and the docs, per the Claude Design project. Data layer: - add frac-delegation-sdk as a workspace dep + vite source alias; re-export the generated structs from its index so consumers can type against them - lib/fracReaderSdk.ts: optional VITE_FRAC_REGISTRY_APP_ID, mirroring the escregAppId pattern. Unset (every network today) tree-shakes the frac SDK out of the client bundle entirely; set, it loads as its own dynamic chunk on first pooled query rather than on page load. - hooks/fracQueries.ts: usePooledPositions over three primitives, gated so an account in no pool costs exactly one read. Two TODO(perf) notes name the registry-side loggers that would collapse the account page to a single read. UI: - VotingPowerByCommittee: merged direct + pooled card with a Direct/Pooled legend, an always-visible per-row breakdown, an inset current-power footer and the AlgoQuarters footnote. Degrades to the previous direct-only table when the account is in no pool. Also unions in committees where power is held *only* through a pool, which useCommitteeVotingPowers drops. - landing hero splits weight into direct + "via N pools" pills; new pooled callout; "How Governance works" gains a fourth pooled step - new /docs/pooled-voting article, plus a FAQ entry and two glossary terms. One nav.ts entry drives the sidebar, contents, header and pager chain. Two fixes that made pooled-only accounts unable to delegate at all: - canSelfDelegate hid the delegation card from accounts with no block production, which is every pool member - the delegate/undelegate/redelegate mutations never passed fractionalOnly, so the frac-registry fallback inner call in set_voting_account went unfunded Also: extract the Surface/EmptyPanel duplicated between Account and CommitteeDetail; add a --teal-strong token so pooled figures are legible on the pale teal tint in light mode; fix the Storybook mock exporting useXGovVotingPowers where the real hook is useGovVotingPowers, which had been breaking every landing-page story. Pooled reads are wired to real SDK calls but not yet exercised on-chain — the localnet seeder has no frac data. Verified via Storybook fixtures (light/dark, pooled, direct-only, amounts-pending, pooled-only) and in the browser for docs.
322377e1
worktree-frac-design-import
30/1,129 ++ 99 --
fix: don't conflate a missing topic body with an untagged candidate
Addresses review feedback on #94. The election tag `e` rides inside the topic body box, so a body that is missing or unreadable leaves the tag *unknown*, not absent. Two places reported it as "unassigned", sending the operator after the wrong fix on top of the missing-body warning they already got: - the pre-setReady checklist gated `validateAssignment` on `topicBodies.length` alone, which only catches the still-fetching case; it now gates on every entry being present, and the existing missing-body warning reuses the same predicate rather than keeping a near-duplicate condition in sync - the per-candidate row rendered "Unassigned" in warning colour for a null body; it now renders "unknown (body missing)" in muted Also: correct the `getTopicBody` doc (the period-shaped parser accepts the same JSON and strips nothing — only the returned type differs), and fall back to a trim-based candidate name in the results view so a blank title can't render as an empty label, matching describeAssignmentReport.
3ef6668a
chore/frac-sample-data-scripts
3/22 ++ 11 --