m2-market/specs/002-market-web/spec.md

5.7 KiB

Feature Specification: M2 Market Web

Feature Branch: 002-market-web

Created: 2026-07-02

Status: Draft

Input: Operator: "build the web app connecting everything" — one browser UI over the wedge's live rails: catalog, listings+evidence, wallet/transactions, registry governance, install handoff. Motivated by UAT: today money is curl-only and governance is raw Forgejo.

User Scenarios & Testing (mandatory)

User Story 1 - Browse & inspect from any browser (Priority: P1)

An operator opens market.machinemachine.ai, browses/searches the live catalog semantically, opens a listing, and sees everything the Store shows (evidence, price, seller, version, permissions) plus stats.

Independent Test: open the URL, search "pdf report", open the listing, verify the same data the CLI's show returns.

Acceptance Scenarios:

  1. Given the live catalog, When I search "branded pdf", Then published listings render with name/summary/price/installs, ordered by relevance.
  2. Given a listing page, When it loads, Then evidence summary, permissions, seller, version, install_ref and stats are shown — same values as m2-market show.
  3. Given a delisted or tenant-invisible listing, Then it does not appear.

User Story 2 - Wallet & economy view (Priority: P1)

An operator picks their identity and sees their balance and transaction history; a fleet view shows all operator balances and links each day to its committed audit snapshot.

Independent Test: select m2bd → balance matches GET /balance/m2bd; fleet view total matches the latest audit/*.json in the registry.

Acceptance Scenarios:

  1. Given operator m2bd selected, When the wallet view loads, Then balance and last 20 transactions match the ledger API exactly.
  2. Given the fleet economy view, Then every operator balance is shown and the "audit" link opens the registry's snapshot file for that day.
  3. Given the browser, Then NO ledger/memory/forgejo key is ever present in client-side code, network responses, or storage (server-side proxy only).

User Story 3 - Governance view (Priority: P2)

Curation state at a glance: open listing PRs with their veto window, published/delisted listings, links into Forgejo for the actual review actions.

Acceptance Scenarios:

  1. Given an open listing PR, Then it appears with title, age, labels, and a link to the Forgejo PR (review/veto happens ON Forgejo — the web app never merges).
  2. Given the listings index, Then each row shows status (published/delisted) and links to its registry directory.

User Story 4 - Install handoff (Priority: P2)

The web cannot apply bundles to a desktop (by design, v1). A listing page gives the operator a copyable, correct install command and points at their desktops.

Acceptance Scenarios:

  1. Given a listing page, When I click "Install on my desktop", Then I get m2-market install <listing_id> --yes copyable + a short "where to run it" note.
  2. The page never claims an install happened.

Edge Cases

  • Ledger or memory-api down → the affected panel shows a clear error, the rest works.
  • Unknown operator id typed → empty wallet with a "no such wallet yet" note (balance 0).
  • Catalog result whose registry entry vanished → row renders from catalog payload only.

Requirements (mandatory)

  • FR-101: A web app MUST be served at market.machinemachine.ai (Coolify app, coolify network, http-scheme domain per docs/runbook.md redirect-loop lesson).
  • FR-102: All upstream calls (memory-api, ledger, Forgejo) MUST go through a server-side backend holding the keys; the browser gets only proxied, minimal JSON.
  • FR-103: Catalog search MUST use the same semantic path + tenant-visibility filter as the CLI (shared reader semantics; server-side).
  • FR-104: Wallet views MUST be read-only in v1 (no grants, no installs, no refunds from the web).
  • FR-105: Access MUST be gated by a fleet passcode (FLEET_PASSCODE env → session cookie); operator identity within a session is a picker, not authentication — UNCONFIRMED assumption, flagged for the operator (internal-trust model v1).
  • FR-106: Governance data MUST come live from the Forgejo API (open PRs with labels, listings dirs + status) with deep links; no write actions.
  • FR-107: The app MUST degrade panel-by-panel when an upstream is down.
  • FR-108: Deployment MUST ride the existing CI/CD pattern (push to m2/m2-market → Coolify deploy; own app + webhook).

Success Criteria (mandatory)

  • SC-101: An operator completes browse → listing → wallet → governance in one browser session with zero terminal usage.
  • SC-102: Wallet numbers equal ledger API values at load time (spot-check 3 ops).
  • SC-103: A secrets scan of served assets + responses shows zero key material.
  • SC-104: App live at market.machinemachine.ai with health endpoint; panels degrade gracefully when an upstream is stopped.
  • SC-105: Lighthouse-basic usability: loads under 3s on the fleet network, usable on a phone (the operator checks desktops from anywhere).

Assumptions

  • v1 auth = fleet passcode + operator picker (FR-105) — OPERATOR TO CONFIRM; upgrade path is per-operator tokens issued by the ledger admin (post-v1).
  • Install-from-web (remote apply to a desktop) is OUT of v1; handoff only (Story 4).
  • Stack: FastAPI backend proxy + React/Vite frontend served by the same container — matches fleet patterns (memory-api FastAPI; m2-gpt admin React) — monorepo web/.
  • Reuses the wedge's frozen contracts; NO new schema, NO new ledger endpoints.
  • cargstore web/ (a static landing page) is superseded by this app.