plans: names terms review fixes in tasks

This commit is contained in:
shum committed 2026-10-23 13:03:14 +00:00
1 parent 32419107a9
commit 16d2ff0c99
1 file changed
+42 -29
@@ -384,30 +384,31 @@ The owner's decisions of 2026-10-22 remove the code for a taken order and keep a
### Terms and community names (owner, 2026-10-23)
The owner's decisions of 2026-10-23: community names come from a list the operator keeps in the service's configuration and reach the page with every search answer; every order signs the version of the terms the buyer agreed to (contract section 1, Terms and community names; Appendix A, items 39 and 40). T28 builds the configuration and the answers; T29 the signed `terms`. T29 needs T28's `termsFor`, so they run in waves 12 and 13.
The owner's decisions of 2026-10-23: community names come from a list the operator keeps in the service's configuration and reach the page with every search answer; every order signs the version of the terms the buyer agreed to (contract section 1, Terms and community names; Appendix A, items 39 and 40). T28 builds the configuration and the answers; T29 the signed `terms`. T29 needs T28's `termsFor` and the webapp's vectors (W1j-0), so they run in waves 12 and 13.
#### T28. The community list and the terms setting
- **Goal:** the operator's list of community names and the terms version and URL, read at startup, in the search answers and the registration view.
- **Files:** `StoreService/Config.hs` (`[community]`, and `[names] terms_version`, `terms_url`); `apps/simplex-store-service/store_service.ini.example`; new `StoreService/Names/Community.hs`; `StoreService/Names/Env.hs` (the list in `NamesEnv`); `StoreService/Web/Names/Search.hs`; `StoreService/Names/View.hs`; `apps/simplex-store-service/README.md` (editing the list and bumping the terms version, each taking effect on restart); `simplex-chat.cabal`; `tests/Test.hs`; `tests/Bots/StoreService/ConfigTests.hs`, `NamesSearchTests.hs`, `NamesOrdersTests.hs` (the view); new `tests/Bots/StoreService/NamesCommunityTests.hs`.
- **Goal:** the operator's list of community names and the terms version and URL, read at startup into `NamesConfig`, in the search answers and the registration view.
- **Files:** `StoreService/Config.hs` (`[community]`, `[names] terms_version` and `terms_url`, the list built into `NamesConfig` as `nCommunity :: Map LabelHash Text` and `nTerms`, the repeated-section check, the count logged at startup); new `StoreService/Names/Community.hs` (`communityFor`, `termsFor`, over `NamesConfig`); `StoreService/Web/Names/Search.hs`; `StoreService/Names/View.hs`; `apps/simplex-store-service/store_service.ini.example`; `scripts/store-service/store_service.ini.example`; `scripts/store-service/names-contract-run.sh` (writes `terms_version`, `terms_url` and one `[community]` line for the e2e spec's fixed label `bakers`); `apps/simplex-store-service/README.md`; `simplex-chat.cabal`; `tests/Test.hs`; `tests/Bots/StoreService/ConfigTests.hs` (and `namesSection`, which other specs reuse), `NamesSearchTests.hs` (its `NamesConfig` at line 117), `NamesOrdersTests.hs` (the view, its `NamesConfig` at line 295); new `tests/Bots/StoreService/NamesCommunityTests.hs`. In this repository, on `sh/names-design`: `plans/names-codes/e2e/deployLocal.ts` (its `[names]` block gains the two settings), committed by the manager with T28's merge.
- **Do:**
- `[names] terms_version` (required with `[names]`; 1–32 characters of `A-Za-z0-9._-`) and `terms_url` (required; an `https://` URL).
- `[community]`: each key a label, each value its text. Its keys are names, so `unknownKeys` skips the section instead of warning. Startup refuses, naming the line: a label that fails `validLabel` (T2) for the configured TLD, a label listed twice (the task confirms how the `ini` parser treats a repeated key and reads the section's raw lines if it keeps only one), a text empty, over 200 characters, or with control characters.
- `Names/Community.hs`: the list as a map from label hash (keccak256 of the label, as the page sends it) to text, built once at startup; `communityFor :: NamesEnv -> LabelHash -> Maybe Text`; `termsFor :: NamesEnv -> LabelHash -> Text` (the version, with `/community` for a listed name), which T29 uses.
- `[names] terms_version` (required; 1–32 characters of `A-Za-z0-9._-`) and `terms_url` (required; an `https://` URL). No default: a default version would record agreement to a document nobody named.
- `[community]`: each key a label, each value its text, built by `Config.hs` into `nCommunity` keyed by the label's keccak256 (as the page sends it). `unknownKeys` skips the section's keys. Startup refuses, naming the line or key: `[community]` without `[names]`; any section header that appears twice in the file (`ini` 0.4.1 builds its section map last-wins, so the check reads the file's header lines before parsing); a key listed twice in `[community]` (`keys "community"` returns every occurrence, in order); a label that fails `validLabel` (T2) for the configured TLD; a text empty, over 200 characters, or with control characters. Startup logs how many community names it loaded.
- `Names/Community.hs`: `communityFor :: NamesConfig -> LabelHash -> Maybe Text`; `termsFor :: NamesConfig -> LabelHash -> Text` (the version, with `/community` for a listed name), which T29 uses. Every consumer already holds `NamesConfig`, so no handler signature changes.
- Search: `community` in every answer, `terms: {version, url}` in the available one. View: `community` from the registration's label hash.
- The README says the list is read only at startup, that editing it changes which wording new orders must sign, and that a terms version bump makes every unsigned page re-ask (`terms_changed`).
- **Acceptance:** the answers' JSON as the contract's examples, field for field; badge specs green.
- **Tests:** the config parsed with and without `[community]`; each refusal (a bad label, an upper-case one, a repeated one, an empty, an over-long and a multi-line or control-character text, a missing or malformed `terms_version` or `terms_url`), naming its line; no warning for community keys; `community` in available, registered, being-registered and reserved answers, and null for an unlisted name; `terms` in the available answer; the view's `community`; `termsFor` for a listed and an unlisted name.
- README: the list and the version are read only at startup; an edit applies to new orders and moves only, and open orders keep what they signed; after a restart, search each listed name and check its marker shows; a text should not end with a full stop, since the popup adds one; an upgrade note that an existing ini needs `terms_version` and `terms_url` before the service starts.
- **Acceptance:** the answers' JSON as the contract's examples, field for field; T21's script starts the service with the new settings; badge specs green.
- **Tests:** the config parsed with and without `[community]`; each refusal, naming its line or key: `[community]` without `[names]`, a repeated `[community]` header and a repeated `[names]` header, a repeated key, a bad label, an upper-case one, an empty, an over-long and a control-character text, a missing or malformed `terms_version` or `terms_url`; no warning for community keys; the loaded count logged; `community` in available, registered, being-registered and reserved answers, and null for an unlisted name; `terms` in the available answer; the view's `community`; `termsFor` for a listed and an unlisted name.
- **Depends on:** T10, T11 (merged). Wave 12.
#### T29. The terms version in the signed order
- **Goal:** every order and move carries and signs `terms`, and the service refuses terms that are not the current ones for the name.
- **Files:** `StoreService/Names/Signatures.hs` (`orderMessage`, `retargetMessage`, `spendMessage` gain `<terms>` before `<timestamp>`; `OrderFields` gains `ofTerms`); `StoreService/Names/Orders.hs` (`parseOrderFields`, `checkTerms`, `ORTermsChanged`); `StoreService/Web/Names/Orders.hs` (`/api/invoice`, `/api/name/register`, `/spend`, and `terms_changed`'s response); `StoreService/Web/Names/Owner.hs` (`/name`); `StoreService/Names/Balances.hs` (the spend's checks); `StoreService/Names/Store.hs` (the `terms` column, `orderHash` over `terms`, `retargetRegistration` storing the new `terms`); the two migration modules (migration `20261023_name_terms`: `name_registrations.terms`, text, not null, default ''); `tests/Bots/StoreService/NamesSignatureTests.hs`, `NamesOrdersTests.hs`, `NamesSpendTests.hs`, `NamesOwnerTests.hs`, `NamesStoreTests.hs`.
- **Do:** the contract's checks in their order: `terms` format with the other formats (`bad_request`); existing orders before it, so an exact resend, `terms` included in its hash, is answered whatever the version is now; then, after the duration, `terms` equal to `termsFor` for the (new) label hash, else `409 {"error": "terms_changed", "terms": {"version", "url"}, "community": …}`. The registration stores the signed `terms`; a move replaces it; the original `order_hash` stays. Existing rows keep '' (no database holds names rows yet).
- **The shared vector:** one order, one spend and one move signed with `terms` `2026-10-23/community` by the webapp's fixture owner code, their messages, EIP-191 digests and signatures computed once by the webapp (viem) and pasted, with the same inputs, into `NamesSignatureTests.hs` and the webapp's test (W1j); both must reproduce them byte for byte.
- **Tests:** the vectors; each endpoint refusing an old version and the wrong variant for a listed and an unlisted name with `terms_changed` and the current terms and `community`; an exact resend after the version changed answered with its order; two orders differing only in `terms` with different `order_hash`; a malformed `terms` `bad_request`; a move to a community name with the plain version refused, with the community variant accepted, its `terms` stored; the order of checks (a stale timestamp with old terms is `bad_request`; old terms on a taken name is `terms_changed`, before the chain).
- **Depends on:** T28. Wave 13. T21 is run again after it against the webapp with W1j.
- **Goal:** every order and move carries and signs `terms`; the service refuses terms that are not the current ones for the name, stores what was signed, and returns it in the view.
- **Naming:** the code already uses `OrderTerms` (`OTInvoice`, `OTCode`, `OTBalance`) for the payment side of `order_hash`. The new field is `ofAgreedTerms` / `nrAgreedTerms`, column `agreed_terms`; the JSON name stays `terms`.
- **Files:** `StoreService/Names/Signatures.hs` (`orderMessage`, `retargetMessage`, `spendMessage` gain `<terms>` before `<timestamp>`; `OrderFields` gains `ofAgreedTerms`); `StoreService/Names/Orders.hs` (`parseOrderFields`, `checkTerms`, `ORTermsChanged`; `orderFieldsOf` compares it, so a body differing only in `terms` is a move); `StoreService/Web/Names/Orders.hs` (`/api/invoice`, `/api/name/register`, `/spend`, `terms_changed`'s response); `StoreService/Web/Names/Owner.hs` (`/name`); `StoreService/Names/Balances.hs` (the spend's checks); `StoreService/Names/Store.hs` (the column, `orderHash` over it, `retargetRegistration` storing the new value); `StoreService/Names/View.hs` (`terms`: the stored value, null for ''); the two migration modules (migration `20261023_name_terms`: up `ALTER TABLE @name_registrations ADD COLUMN agreed_terms TEXT NOT NULL DEFAULT ''`, SQLite needing the default for `NOT NULL`; down `ALTER TABLE @name_registrations DROP COLUMN agreed_terms`); `tests/Bots/StoreService/NamesSignatureTests.hs` (the order, name and spend vectors at lines 38–62 regenerated from Appendix C, `find`'s unchanged), `NamesOrdersTests.hs`, `NamesSpendTests.hs`, `NamesOwnerTests.hs`, `NamesStoreTests.hs`.
- **Do:** the contract's checks in their order: `terms` format with the other formats (`bad_request`); existing orders before it, so an exact resend, `terms` included in its hash, is answered whatever the version is now; then, after the duration, `terms` equal to `termsFor` for the (new) label hash, else `409 {"error": "terms_changed", "terms": {"version", "url"}, "community": …}`, before `/spend`'s balance, the code's state and the chain. The registration stores the signed value; a move replaces it; the original `order_hash` stays.
- **Tests:** Appendix C's vectors, plain and `/community`; each endpoint refusing an old version and the wrong variant, for a listed and an unlisted name, with `terms_changed` and the current terms and `community`; the stored value on each insert path (invoice, code, covered spend, difference spend) and in the view, null for a '' row; an exact resend after a version bump for each kind (the invoice view, the code order's `{registrationId}`, the spend); two orders differing only in `terms` with different `order_hash`; a `/name` body differing only in `terms` counted as a move and checked; a malformed `terms` `bad_request`; a move to a community name with the plain version refused and with the community variant accepted, its value stored; the order of checks: a stale timestamp with old terms `bad_request`, old terms on a taken name `terms_changed` before the chain, `/spend` `terms_changed` before `balance_changed`, `/api/name/register` `terms_changed` before the code's state, and `/name` from `failed` running `/retry`'s check 1 before the terms; the migration up and down on SQLite and Postgres.
- **Rollout:** an older page that sends no `terms` gets `bad_request` once T29 lands; W1j ships with it (nothing is live yet).
- **Depends on:** T28, and W1j-0 (Appendix C's values written). Wave 13. T21 is run again after it, against the webapp with W1j, and must pass.
### Stages 6 and 7: the service side
@@ -444,8 +445,8 @@ Each wave starts from `sh/store-service` as merged at the end of the last one. "
| 9 | T23, T27 | `Names/Store.hs`, the migrations, `Orders.hs`, `Names/Turns.hs`, `Names/Balances.hs`, `Names/View.hs`, `Names/Orders.hs`, and, for `RPBalance`'s case, `balance_changed`'s response and `createPaidRegistration`'s call only, `Names/Worker.hs`, `Web/Names/Orders.hs`, `Web/Names/Owner.hs`. T27: `README.md` |
| 10 | T24, T25, T26 | T24: `Names/Worker.hs`, `Orders.hs`, `Names/View.hs`, the migrations, `Poller.hs`, `Providers.hs`, `Providers/BTCPay.hs`, `Providers/Stripe.hs`, `Store/Invoices.hs`, `Names/Store.hs`, `Names/Orders.hs`, `Web/Names/Owner.hs`. T25: `Web/Names/Orders.hs`, `Names/Balances.hs`, `Names/Signatures.hs`. T26: `Service.hs`, `README.md` |
| 11 | T21 | `README.md` |
| 12 | T28 | `Config.hs`, `Names/Env.hs`, `Web/Names/Search.hs`, `Names/View.hs`, `README.md` |
| 13 | T29 | `Names/Signatures.hs`, `Names/Orders.hs`, `Web/Names/Orders.hs`, `Web/Names/Owner.hs`, `Names/Balances.hs`, `Names/Store.hs`, the migrations |
| 12 | T28 | `Config.hs`, `Names/Community.hs`, `Web/Names/Search.hs`, `Names/View.hs`, both ini examples, `names-contract-run.sh`, `README.md`, `simplex-chat.cabal` and `tests/Test.hs` lines, `ConfigTests.hs`, `NamesSearchTests.hs`, `NamesOrdersTests.hs`, `NamesCommunityTests.hs` |
| 13 | T29 | `Names/Signatures.hs`, `Names/Orders.hs`, `Web/Names/Orders.hs`, `Web/Names/Owner.hs`, `Names/Balances.hs`, `Names/Store.hs`, `Names/View.hs`, the migrations, `NamesSignatureTests.hs` (after W1j-0) |
The wave-10 integrator, after merging T24, T25 and T26, adds one end-to-end spec to `NamesSpendTests.hs`: a difference spend paid at `FakeBTCPay` and settled (the order `queued`, the hold spent); the same cancelled (the hold released); a covered spend's order ending `taken` (a balance of its own, its whole `price`); a `//refund` of the rest; and a difference invoice paid after it closed, credited to its order's balance. Wave 10 has no other dependency between its tasks.
@@ -492,29 +493,41 @@ In simplex.domains, branch `sh/names-webapp`, in the webapp's workstream beside
## Webapp tasks for the terms, community names and the approved mockup
In simplex.domains, `sh/names-webapp`, beside waves 12 and 13, following the contract (Appendix A, items 39 and 40) and ep's approved mockup (2026-10-23; the queue in `.superpowers/program/webapp-queue.md`, entries of 2026-10-22 and 2026-10-23).
In simplex.domains, `sh/names-webapp`, beside waves 12 and 13, following the contract (Appendix A, items 39 and 40; Appendix C), ep's approved mockup f72e14fc (v6), and `plans/names-codes/2026-10-23-ux-principles.md`.
#### W1j-0. The signature vectors
- **Goal:** Appendix C's values, before wave 13 starts.
- **Files:** a script or test under `test/` that computes them with viem from the stated inputs; `test/flow.test.ts` and the other tests whose order, move and spend vectors change (SPEND among them), regenerated, `find`'s unchanged; in this repository, `plans/names-codes/2026-10-21-store-api.md` Appendix C, filled by the manager from the task's output.
- **Do:** for `order`, `name` and `spend`, each with `2026-10-23` and `2026-10-23/community`: the message text, its EIP-191 digest and the signature, by the fixture owner code's key.
- **Depends on:** the contract. T29 depends on it.
#### W1j. Terms and community names
- **Goal:** the page signs the terms the buyer ticked, and shows community names with their wording.
- **Files:** `dev/mock/service.ts`, `dev/mock.ts` (a community list and `POST /control/community`, the terms version and URL, `terms` in bodies, messages, the order hash and the check, `terms_changed`); `src/api.ts` (`community` and `terms` in the answers and the view, `terms_changed`); `src/flow.ts` (`<terms>` in the order, spend and move messages; the record's `terms`); `src/store.ts`; `src/views/checkout.ts` and `src/screens/checkout.ts` (the checkbox); `src/views/search.ts`, `src/views/names.ts`, `src/views/order.ts` (the marker); the stylesheet; their tests; `test/crypto.test.ts` or `test/flow.test.ts` (the shared vector); `e2e/` (a community spec).
- **Goal:** the page signs only terms the buyer agreed to, on every path that signs an order, and shows community names with their wording.
- **Files:** `dev/mock/service.ts`, `dev/mock.ts` (a community list and `POST /control/community`, the terms version and URL, `terms` in bodies, messages, the order hash and the check, the view's `terms`, `terms_changed`); `src/api.ts` (`community` and `terms` in the answers, `community` and `terms` in `RegistrationView`, `terms_changed`); `src/flow.ts` (`<terms>` in the order, spend and move messages; the record's agreed `terms`; `reopen`, `openOrder` and `syncRecord` taking it from the view); `src/store.ts`; `src/views/checkout.ts`, `src/screens/checkout.ts`, `src/views/search.ts`, `src/views/order.ts`, `src/views/names.ts`; the stylesheet; their tests; `e2e/` (a community spec).
- **Do:**
- The checkbox above the main button on Check your order (and before "Register with your code"), required: "I agree to <SimpleX name terms> and do not knowingly violate any trademarks.", or for a community name "I agree to <SimpleX name terms> and will use this name for its community. ⓘ"; the link is the answer's `terms.url`; ⓘ opens "This name is for <for>. Its owner agrees to use it for this community, not as the name of one person or business."
- The signed `terms`: the answer's `terms.version`, with `/community` for a community name; kept on the order record, so a resend sends the same; on `terms_changed`, the page takes the answer's terms and `community`, unticks the box, says the terms changed, and signs anew on the next press.
- "Community ⓘ" inline after the name in search rows, Check your order and Your names: one button opening the popup, on the name's baseline with the icon centred on the word, 16 px after the name on desktop, right-aligned on the name's last line on a phone, no hover underline, pointer cursor; community names at the size of other names (22 px, 20 px on a phone); a taken community name's popup caption as the mockup.
- **Acceptance:** the mockup's community states at 1280 and 390 px in Chromium, Firefox (158 too) and WebKit, motion and no flashes; the shared vector reproduced byte for byte.
- **Tests:** the vector (T29's); each order kind and the move sending `terms`; `terms_changed` re-asking; the checkbox's two wordings and the popup; the marker's placement measured in e2e.
- **Depends on:** the contract (items 39 and 40). The service side lands with T28 and T29.
- **Every signing path** (manager's ruling, 2026-10-23):
- Check your order (a new order, a code order, a spend): the checkbox before the main button.
- "Choose another name" from search (`views/search.ts` `retarget`), a move to a different name: the checkbox before signing, with the community wording when the target is listed.
- The same name again: a new invoice after one expired (`views/order.ts` ~865), spending a balance again (`spendAgain`, ~913), a move without links (`moveWithoutLinks`, ~1050): reuse the record's (or the view's) agreed `terms` without asking only when they equal the current terms for the name; otherwise show the checkbox first.
- `terms_changed` on any path: take the answer's terms and `community`, untick, say the terms changed, ask again.
- **The checkbox**, required: "I agree to <SimpleX name terms> and do not knowingly violate any trademarks.", or for a community name "I agree to <SimpleX name terms> and will use this name for its community. ⓘ"; the link is `terms.url`; ⓘ opens "This name is for <for>. Its owner agrees to use it for this community, not as the name of one person or business."
- **The marker:** "Community ⓘ" inline after the name in search rows, Check your order and Your names, as v6: one button opening the popup, on the name's baseline with the icon centred on the word, 16 px after the name on desktop, right-aligned on the name's last line on a phone, no hover underline, pointer cursor; community names at the size of other names (22 px, 20 px on a phone); a taken community name's popup caption.
- The community e2e spec uses the label `bakers`, which T28's `names-contract-run.sh` lists, so it passes against the mock and in T21's rerun.
- **Acceptance:** v6's community states at 1280 and 390 px in Chromium, Firefox (158 too) and WebKit, with motion and no flashes (the UX principles); Appendix C reproduced byte for byte.
- **Tests:** Appendix C; each signing path sending `terms`, asking or reusing as ruled, and handling `terms_changed`; a restored order (the view's `terms`) paying again without asking at an unchanged version and asking after a bump; the checkbox's two wordings and the popup; the marker's placement measured in e2e.
- **Depends on:** W1j-0. Ships with T29.
#### W1k. The phone card page and the remaining mockup items
- **Goal:** the approved mockup's remaining layout items.
- **Goal:** v6's remaining layout items (mockup f72e14fc, `2026-10-23-ux-principles.md`).
- **Files:** `src/views/order.ts` (the card page), `src/views/search.ts` (rows), the stylesheet, their e2e.
- **Do:**
- Pay by card on a phone keeps its fixed button; Stripe's note moves under it; the name note is one line: "Available at HH:MM. If someone registers it first, you keep the payment as balance."
- Search answer rows keep equal top and bottom padding for long names (16 px `padding-block`, 72 px minimum height).
- Any other 2026-10-22/23 queue entry the mockup shows and the page does not, listed in the task's report with what it measured.
- **Acceptance:** positions measured in e2e at 390 and 1280 px against the mockup; no layout shift when the card form loads.
- Any other 2026-10-22/23 queue entry v6 shows and the page does not, listed in the task's report with what it measured.
- **Acceptance:** positions measured in e2e at 390 and 1280 px against v6; no layout shift when the card form loads.
- **Depends on:** nothing new in the service.
## Owner answers (2026-10-21, binding)