Refine specs with open questions
This commit is contained in:
@@ -42,20 +42,26 @@ The mock lives in `src/context/mocks/families.tsx` with fixtures in a co-located
|
||||
### D4: Member-status derivation
|
||||
The four UI statuses map to contract reads: **Pending** = `GetPendingInvitationsForFamilyPaged` (carry the `expired` flag), **Joined** = `GetFamilyMembersPaged`, **Rejected** = `GetPastInvitationsForFamilyPaged` filtered to `Rejected` status, **Removed** = `GetPastMembersForFamilyPaged` (left/kicked) plus `Revoked` past invitations where relevant. The derivation lives in a selector hook so both UI and tests share one definition.
|
||||
|
||||
### D5: Storybook three-level structure
|
||||
### D5: Family tab is always visible; family key is standalone
|
||||
The Family tab renders for **every** wallet account (not gated on owning a family or controlling a node), so any account can start a family: it shows the create entry point when the account owns no family and the management surface when it does. The family key produced on creation is a **standalone** key, modelled as an opaque value isolated behind the `createFamily`/`acceptFamilyInvitation` request boundary.
|
||||
|
||||
### D6: Large lists are paginated via the contract's exclusive `start_after` cursor
|
||||
Member lists and invitation archives use the contract's cursor pagination: each page passes `start_after` (exclusive) and reads `start_next_after` from the response to fetch the next page, with the contract's default limit of 50 (max 100). The TanStack Query read hooks expose this as incremental/infinite pagination; the mock honours the same cursor semantics so paging is exercised without a chain.
|
||||
|
||||
### D7: Storybook three-level structure
|
||||
- **Components** (`src/components/families/*.stories.tsx`): each component with explicit state args (empty, loading, error, expired, over-limit, success).
|
||||
- **Pages** (`src/pages/families/*.stories.tsx`): composed surfaces (owner management page, operator invites page) backed by the mock provider.
|
||||
- **Flows** (`*.flow.stories.tsx`): play functions (`@storybook/test`) that perform the user actions end to end (create → invite → accept → kick → disband; operator: receive → accept/reject → leave).
|
||||
|
||||
### D6: Playwright runs against the static Storybook build
|
||||
### D8: Playwright runs against the static Storybook build
|
||||
Because the production app is Tauri (not a plain web target), Playwright e2e specs run against `build-storybook` served statically, exercising the flow stories as real browser sessions. This gives deterministic, chain-free e2e without packaging Tauri. *Alternative considered:* `tauri-driver`/WebDriver against the native app — heavier, flaky in CI, and unnecessary since the contract layer is mocked anyway.
|
||||
|
||||
### D7: Creation fee and limits are read from chain config
|
||||
### D9: Creation fee and limits are read from chain config
|
||||
The UI reads `create_family_fee`, `family_name_length_limit`, and `family_description_length_limit` from contract `Config` (mocked in fixtures), never hardcoding 100 NYM or character counts. Validation is byte-length based to match the contract.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Family key / delegation not in contract spec]** → Model the family key as an opaque value in types/mocks and isolate it behind the `createFamily`/`acceptFamilyInvitation` boundary so a later contract decision (multisig vs standalone) changes only the request layer, not the UI.
|
||||
- **[Family key / delegation not in contract spec]** → Model the standalone family key as an opaque value in types/mocks and isolate it behind the `createFamily`/`acceptFamilyInvitation` boundary, so a later contract decision changes only the request layer, not the UI.
|
||||
- **[No `UpdateFamily` edit handler in contract spec]** → Build the edit UI + mock path now; gate real submission behind a feature check so it is dark until the contract adds the handler.
|
||||
- **[Status derivation from archives is subtle]** (Rejected vs Revoked vs Removed) → Centralize in one selector hook with unit tests covering each archive→status mapping.
|
||||
- **[Playwright-vs-Tauri divergence]** → Storybook flows test UI logic against mocks, not the real IPC bridge; a thin set of manual/native smoke checks should still cover Tauri wiring before release.
|
||||
@@ -63,12 +69,11 @@ The UI reads `create_family_fee`, `family_name_length_limit`, and `family_descri
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Additive only — new tab, context, requests, types, stories, tests. No existing wallet behavior changes. Rollout can be gated by the Family-tab eligibility check (and a feature flag for the contract-dependent edit/key paths) so the surface ships dark until the contract dependencies land. Rollback is removal of the tab entry point.
|
||||
Additive only — new tab, context, requests, types, stories, tests. No existing wallet behavior changes. The Family tab is always visible, so rollout is gated only by a feature flag for the contract-dependent edit/key paths (which ship dark until the contract dependencies land). Rollback is removal of the tab entry point.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Family key: multisig or standalone? (NYM-1210 "per Discovery decision".) Determines the create/accept request shape.
|
||||
- Will the contract add an `UpdateFamily` handler for NYM-1211 edits, and what is its message shape / auth?
|
||||
- Exact eligibility rule for showing the Family tab (owns a family OR controls a bonded node — confirm).
|
||||
- Figma file/frame URLs for each component and page (to be supplied at apply time via Figma MCP).
|
||||
- Pagination/refresh strategy for large member lists and invitation archives (cursor-based per the contract's `start_after`).
|
||||
|
||||
_Resolved:_ family key is **standalone**; the Family tab is **always visible**; large lists are **paginated via the contract's `start_after` cursor** (default 50, max 100).
|
||||
|
||||
@@ -5,7 +5,7 @@ Node Families is a new on-chain capability (see the `node-families-contract` spe
|
||||
## What Changes
|
||||
|
||||
**Family owner flows (NYM-1210–1215)**
|
||||
- New **Family Tab** visible to eligible users, with create-family entry point.
|
||||
- New **Family Tab**, always visible to any wallet account: shows a create-family entry point when the account owns no family, and the family management surface when it does.
|
||||
- Create a family: attach the configured creation fee (`Config::create_family_fee`), set name + description, surface insufficient-balance and fee errors.
|
||||
- Add/edit family **name** and **description**, with byte-length limits and input sanitisation, inline over-limit errors.
|
||||
- Invite a node by **node ID**: triggers the contract invite (nonce/TTL via `validity_secs`), with confirmation; warns and does not send if the node is already in a family, does not exist, or already has a pending invite from this family.
|
||||
@@ -27,7 +27,7 @@ Node Families is a new on-chain capability (see the `node-families-contract` spe
|
||||
- UI implemented from **Figma** (designs supplied via Figma MCP during apply).
|
||||
|
||||
**Contract dependencies — NOT covered by the current `node-families-contract` spec** (see Impact; flagged for resolution):
|
||||
- **Family key / multisig / delegation of node control** (NYM-1210, NYM-1217): the contract spec models ownership as `info.sender` and acceptance as a membership record only — there is no key-generation or control-delegation mechanism. NYM-1210 marks this "per Discovery decision."
|
||||
- **Family key (standalone) / delegation of node control** (NYM-1210, NYM-1217): the contract spec models ownership as `info.sender` and acceptance as a membership record only — there is no key-generation or control-delegation mechanism. The family key is a **standalone** key.
|
||||
- **Edit name/description after creation** (NYM-1211): the contract spec has `CreateFamily` (carries name/description) and `UpdateConfig` (admin) but **no `UpdateFamily` edit handler**.
|
||||
|
||||
## Capabilities
|
||||
|
||||
+14
-10
@@ -1,12 +1,12 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Family Tab is visible to eligible users and exposes a create entry point
|
||||
### Requirement: Family Tab is always visible and exposes create or management based on ownership
|
||||
|
||||
The wallet SHALL display a **Family** tab to eligible users (an account whose connected address either owns a family or controls a bonded node). The tab SHALL present a create-family entry point when the connected address does not already own a family. When the address already owns a family, the tab SHALL show the family management surface instead of the create entry point.
|
||||
The wallet SHALL display the **Family** tab for every connected wallet account, regardless of whether the account owns a family or controls a bonded node, so that any account can start a new family. When the connected address does not own a family, the tab SHALL present a create-family entry point. When the address already owns a family, the tab SHALL show the family management surface instead of the create entry point.
|
||||
|
||||
#### Scenario: Eligible user without a family sees the create entry point
|
||||
- **WHEN** an eligible user opens the Family tab and their address owns no family
|
||||
- **THEN** the tab renders a "Create family" entry point
|
||||
#### Scenario: Account without a family sees the create entry point
|
||||
- **WHEN** any connected account opens the Family tab and its address owns no family
|
||||
- **THEN** the tab is shown and renders a "Create family" entry point
|
||||
|
||||
#### Scenario: Owner sees management surface instead of create
|
||||
- **WHEN** the connected address already owns a family
|
||||
@@ -28,13 +28,13 @@ The wallet SHALL allow an eligible user to create a family by submitting a name
|
||||
- **WHEN** creation fails with `InvalidFamilyCreationFee` or `InvalidDeposit`
|
||||
- **THEN** the wallet shows a clear fee error and the family is not created
|
||||
|
||||
### Requirement: Family key is generated on creation
|
||||
### Requirement: A standalone family key is generated on creation
|
||||
|
||||
On successful family creation the wallet SHALL generate a family key (multisig or standalone, per the Discovery decision) and present it to the owner. The exact key mechanism depends on the `node-families-contract` adding key/delegation support; until then the wallet SHALL treat the family key as an opaque value backed by mocked behavior.
|
||||
On successful family creation the wallet SHALL generate a **standalone** family key and present it to the owner. The exact key mechanism depends on the `node-families-contract` adding key/delegation support; until then the wallet SHALL treat the family key as an opaque value backed by mocked behavior.
|
||||
|
||||
#### Scenario: Family key presented on creation
|
||||
#### Scenario: Standalone family key presented on creation
|
||||
- **WHEN** a family is created successfully
|
||||
- **THEN** the wallet generates and displays the associated family key to the owner
|
||||
- **THEN** the wallet generates and displays the associated standalone family key to the owner
|
||||
|
||||
### Requirement: Family owner can add and edit the family name and description
|
||||
|
||||
@@ -94,7 +94,11 @@ The wallet SHALL list the family's pending invitations with their expiry state (
|
||||
|
||||
### Requirement: Family owner can view the member list grouped by status
|
||||
|
||||
The wallet SHALL display all nodes associated with the family grouped into four statuses: **Pending** (active pending invitations), **Joined** (current members), **Rejected** (invitations the node declined), and **Removed** (members that left or were kicked). The list SHALL refresh to reflect current contract state and SHALL render a per-status empty state when a group has no entries. Statuses are derived from the contract queries: pending invitations, current members, and the past-invitation / past-member archives.
|
||||
The wallet SHALL display all nodes associated with the family grouped into four statuses: **Pending** (active pending invitations), **Joined** (current members), **Rejected** (invitations the node declined), and **Removed** (members that left or were kicked). The list SHALL refresh to reflect current contract state and SHALL render a per-status empty state when a group has no entries. Statuses are derived from the contract queries: pending invitations, current members, and the past-invitation / past-member archives. Large lists SHALL be paginated using the contract's exclusive `start_after` cursor (default page size 50, max 100), fetching subsequent pages via the returned `start_next_after`.
|
||||
|
||||
#### Scenario: Large member list is paginated by cursor
|
||||
- **WHEN** a status group has more entries than one page
|
||||
- **THEN** the wallet fetches additional pages using `start_after`/`start_next_after` rather than loading the whole list at once
|
||||
|
||||
#### Scenario: Members are grouped by status
|
||||
- **WHEN** the owner opens the member list
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## 1. Types & request bindings
|
||||
|
||||
- [ ] 1.1 Add TS types in `src/types` for `NodeFamily`, `FamilyMembership`, `FamilyInvitation`, `PendingFamilyInvitationDetails`, `PastFamilyInvitation` (with status), `PastFamilyMember`, and contract `Config` (fee + limits)
|
||||
- [ ] 1.2 Add an opaque `FamilyKey` type and create/accept request args that carry it (isolated so the multisig-vs-standalone decision changes only this layer)
|
||||
- [ ] 1.2 Add a standalone `FamilyKey` type (opaque value) and create/accept request args that carry it, isolated behind the request layer
|
||||
- [ ] 1.3 Add `src/requests/families.ts` Tauri IPC bindings for execute msgs: createFamily, updateFamily (edit, dark/gated), disbandFamily, inviteToFamily, revokeFamilyInvitation, kickFromFamily, acceptFamilyInvitation, rejectFamilyInvitation, leaveFamily
|
||||
- [ ] 1.4 Add query bindings: getFamilyByOwner, getFamilyMembership, family members paged, pending invitations for family/node paged, past invitations for family paged, past members for family paged, config
|
||||
- [ ] 1.5 Export new requests from `src/requests/index.ts`
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
## 6. Family Tab & pages
|
||||
|
||||
- [ ] 6.1 Add the Family tab entry point with eligibility gating (owns a family OR controls a bonded node)
|
||||
- [ ] 6.1 Add the Family tab, always visible for every wallet account (no eligibility gating)
|
||||
- [ ] 6.2 Owner management page composing components from section 4
|
||||
- [ ] 6.3 Operator invites page composing components from section 5
|
||||
- [ ] 6.4 Route between create entry point and management surface based on ownership
|
||||
|
||||
Reference in New Issue
Block a user