T013 worker found record_install violating CHECK(amount>0) when platform_pct rounds cut or seller_amount to zero (unhandled IntegrityError -> HTTP 500). Fixed: skip zero rows, grant references the paying tx; regression test added. 9 ledger tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
176 lines
13 KiB
Markdown
176 lines
13 KiB
Markdown
# Tasks: M2 Marketplace — First Wedge
|
||
|
||
**Input**: Design documents from `/specs/001-market-first-wedge/`
|
||
|
||
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md
|
||
|
||
**Tests**: included where the design demands them (research.md R9: ledger property tests
|
||
are how SC-003/SC-004 are held; schema validation guards the frozen contract).
|
||
|
||
**Organization**: grouped by user story. The wedge gate (SC-001, plan row 7) is reachable
|
||
at the end of Phase 5 — Store (US4) and Scout (US5) are NOT on that path.
|
||
|
||
## Format: `[ID] [P?] [Story] Description`
|
||
|
||
## Phase 1: Setup (Shared Infrastructure)
|
||
|
||
**Purpose**: monorepo scaffolding per plan.md Project Structure
|
||
|
||
- [x] T001 Create monorepo skeleton: `schemas/`, `ledger/`, `cli/`, `indexer/`, `solutions/`, `scout/`, `store/README.md` (pointer file) per plan.md structure
|
||
- [x] T002 Initialize uv workspaces: `ledger/pyproject.toml` (fastapi, uvicorn, pytest), `cli/pyproject.toml` (click, httpx, pytest), `indexer/pyproject.toml` (httpx) — Python 3.12
|
||
- [x] T003 [P] Configure ruff + pytest defaults at repo root (`pyproject.toml` tool sections shared via workspace)
|
||
|
||
---
|
||
|
||
## Phase 2: Foundational (Blocking Prerequisites)
|
||
|
||
**Purpose**: the frozen contracts everything else builds against
|
||
|
||
**⚠️ CRITICAL**: no user story work until this phase completes
|
||
|
||
- [x] T004 [P] Author frozen `schemas/solution.schema.json` v1 per data-model.md Solution table (manifest-core + commercial fields; validation rules incl. evidence non-empty, tenant_scope firewall attestations)
|
||
- [x] T005 [P] Author frozen `schemas/listing.schema.json` v1 per data-model.md Listing table (incl. `stats` conversion-signal fields, `status` enum, `install_ref`)
|
||
- [x] T006 Write schema validation test fixtures in `schemas/tests/` (valid + invalid examples for both schemas; invalid must include: missing evidence, tenant_scope violation, bad price) and a `just validate` / pytest runner
|
||
- [x] T007 Create Forgejo repo `m2/market-registry` per contracts/registry-layout.md: layout dirs, PR template (evidence/price/permissions/rollback), CI validation job (schema-validate listings, content_hash check), veto/approved label protocol — use the forgejo skill/API
|
||
- [x] T008 [P] Write fedlearn coordination note `docs/fedlearn-coordination.md`: frozen solution-schema core fields that the fedlearn manifest must stay superset-compatible with (research.md R3), the commercialize→registry-PR seam (R2), and the m2core-sync adapter swap acceptance (contracts/apply-adapter.md); deliver a copy to /home/m2/m2o/.planning/federated-learning/proposals/
|
||
- [x] T009 [P] Define shared config conventions doc `docs/config.md`: `~/.m2-market/config.toml` keys, env vars for services, key classes (service/admin) — from contracts/cli.md + ledger-api.md
|
||
|
||
**Checkpoint**: schemas frozen, registry live, coordination recorded
|
||
|
||
---
|
||
|
||
## Phase 3: User Story 1 — Paid install via CLI (Priority: P1) 🎯 MVP
|
||
|
||
**Goal**: `search → show → install` closes debit→grant→apply on a real machine
|
||
|
||
**Independent test**: quickstart.md Scenarios B + C against a deployed ledger with one
|
||
published listing
|
||
|
||
### Ledger core (contracts/ledger-api.md)
|
||
|
||
- [x] T010 [P] [US1] Implement tx + grant models and derived-balance queries in `ledger/src/m2_ledger/models.py` (SQLite WAL, append-only invariants, data-model.md Transaction/LicenseGrant)
|
||
- [x] T011 [P] [US1] Implement X-API-Key auth (service/admin key classes) in `ledger/src/m2_ledger/auth.py`
|
||
- [x] T012 [US1] Implement API routes `/health`, `/balance/{op}`, `/tx/install`, `/tx/refund`, `/tx`, `/license/{op}/{listing}` in `ledger/src/m2_ledger/api.py` (402/409 semantics, all-or-nothing install tx, idempotent by ref)
|
||
- [x] T013 [US1] Ledger unit + property tests in `ledger/tests/test_properties.py`: balance==fold(log) under generated sequences; no grant without tx; install atomicity; duplicate-ref 409
|
||
- [ ] T014 [US1] Dockerfile + Coolify deployment for m2-ledger on the `coolify` network in `ledger/Dockerfile` + `ledger/README.md` deploy notes; verify `GET /health` from the network
|
||
- [x] T015 [US1] Create `market:catalog` partition and implement indexer `indexer/src/m2_market_indexer/reindex.py` + `watch.py` per contracts/catalog-index.md (registry → memory-api upsert, tenant_visibility payload, delist removal)
|
||
|
||
### CLI (contracts/cli.md)
|
||
|
||
- [x] T016 [P] [US1] CLI package scaffold + config loading (`~/.m2-market/config.toml`, operator mapping) in `cli/src/m2_market/cli.py` (click group, `--json`, exit-code map)
|
||
- [x] T017 [P] [US1] Ledger client in `cli/src/m2_market/ledger.py` (install/refund/balance/license calls, error mapping to exit codes 3/4)
|
||
- [x] T018 [P] [US1] Registry client in `cli/src/m2_market/registry.py` (fetch listing dirs, download release bundle, verify content_hash)
|
||
- [x] T019 [P] [US1] Catalog client in `cli/src/m2_market/catalog.py` (memory-api semantic query, server-side tenant filter param)
|
||
- [x] T020 [P] [US1] ApplyAdapter protocol + InstallState in `cli/src/m2_market/apply/base.py` per contracts/apply-adapter.md (Changeset, plan/apply/verify/rollback, state.json read/write)
|
||
- [x] T021 [US1] Local adapter in `cli/src/m2_market/apply/local.py`: recipe.yaml-driven file placement into agent-home, post-install checks, idempotent re-apply, rollback from backups
|
||
- [x] T022 [US1] `m2core_sync` stub adapter in `cli/src/m2_market/apply/m2core_sync.py` raising RailsNotLanded (exit 6)
|
||
- [x] T023 [US1] `search` + `show` commands in `cli/src/m2_market/cli.py` (catalog query → table/JSON; show renders evidence/price/permissions diff)
|
||
- [x] T024 [US1] `install` command in `cli/src/m2_market/cli.py`: the FR-005 sequence (confirm → debit → fetch → apply → verify → refund-on-failure → state → entrypoint hint) per contracts/cli.md
|
||
- [x] T025 [US1] `wallet` command in `cli/src/m2_market/cli.py` (balance + recent tx)
|
||
- [ ] T026 [US1] CLI integration tests in `cli/tests/test_install_flow.py` against a local ledger instance + fixture registry/catalog: funded install, insufficient funds (no side effects), idempotent re-run, apply-failure refund
|
||
|
||
**Checkpoint**: Scenario B/C pass end-to-end with a fixture listing (SC-003/005 held locally)
|
||
|
||
---
|
||
|
||
## Phase 4: User Story 2 — Publish a Solution with evidence (Priority: P1)
|
||
|
||
**Goal**: builder → validated listing PR → veto window → published → discoverable
|
||
|
||
**Independent test**: quickstart.md Scenario A
|
||
|
||
- [ ] T027 [US2] `publish` command in `cli/src/m2_market/cli.py` + `cli/src/m2_market/publish.py`: validate bundle against schemas (exit 5 on violation incl. tenant firewall), push `listing/<id>` branch, open PR via Forgejo API with template
|
||
- [ ] T028 [US2] Registry CI hardening in `m2/market-registry` CI config: evidence-required, owner-initiated + double-scrub attestations for tenant-scoped submissions, content_hash-vs-release check (Story 2 AC-2/AC-4 mechanical rejection)
|
||
- [ ] T029 [P] [US2] Package seed Solution `solutions/mm-pdf-report/` (from the proven mm-pdf outcome): solution.json, payload/, recipe.yaml, real evidence refs
|
||
- [ ] T030 [P] [US2] Package seed Solution `solutions/agent-scaffold/` (from the agent-scaffold skill outcome): solution.json, payload/, recipe.yaml, real evidence
|
||
- [ ] T031 [P] [US2] Package seed Solution `solutions/competitor-scan/` (from the competitor-scan report outcome): solution.json, payload/, recipe.yaml, real evidence
|
||
- [ ] T032 [US2] Publish all three seeds through the real protocol (PRs, review, merge), verify each discoverable via `m2-market search` (SC-002)
|
||
|
||
**Checkpoint**: ≥3 published evidence-backed listings live
|
||
|
||
---
|
||
|
||
## Phase 5: User Story 3 — Wallets and starter grants (Priority: P2)
|
||
|
||
**Goal**: bootstrapable economy with auditable balances
|
||
|
||
**Independent test**: quickstart.md Scenario D + grant flow
|
||
|
||
- [ ] T033 [US3] `/grant/starter` admin endpoint in `ledger/src/m2_ledger/api.py` + config-driven default amount (100 cr flagged-unconfirmed) with tests in `ledger/tests/test_grants.py`
|
||
- [ ] T034 [US3] Snapshot module `ledger/src/m2_ledger/snapshot.py`: balances dump → commit `audit/YYYY-MM-DD.json` to m2/market-registry via Forgejo API; `/snapshot` admin endpoint + daily cron (Coolify scheduled task or in-app scheduler)
|
||
- [ ] T035 [US3] Issue starter grants to the two pilot operators (m2bd, sdjs-operator) and run Scenario D verification (snapshot committed, properties green)
|
||
|
||
**🎯 WEDGE GATE (plan row 7, SC-001)**:
|
||
|
||
- [ ] T036 [US3] Execute the real paid install between two operators: m2bd buys one seeded listing published by sdjs-operator via `m2-market install` on a real desktop; capture ledger rows, grant, applied bundle, and invoke the entrypoint; record evidence in `.m2herd/context/` and the run report
|
||
|
||
---
|
||
|
||
## Phase 6: User Story 4 — M2 Store on canaries (Priority: P3)
|
||
|
||
**Goal**: one-click install UI over the same rails (work lands in the cargstore repo,
|
||
branch `m2-store`; contracts referenced from this repo)
|
||
|
||
**Independent test**: quickstart.md Scenario F on chris-m2o
|
||
|
||
- [ ] T037 [US4] Fork/branch `m2-store` on github.com/machine-machine/cargstore; swap catalog source from Flatpak JSON to `market:catalog` via memory-api HTTP (desktop-provisioned creds per research.md R7) in cargstore `src/` catalog service
|
||
- [ ] T038 [US4] Add Solution install backend to cargstore that shells `m2-market install --json` and renders progress/result truthfully (incl. refund-on-failure path) in cargstore `src/` install manager
|
||
- [ ] T039 [US4] Solution detail page: evidence, price, permissions diff (from `m2-market show --json`) in cargstore `src/` UI
|
||
- [ ] T040 [US4] Rebrand Clawdbot→M2 Store and deploy to canaries chris-m2o + gunnar-m2o only; run Scenario F
|
||
|
||
---
|
||
|
||
## Phase 7: User Story 5 — Solution Scout v0 (Priority: P3)
|
||
|
||
**Goal**: in-the-moment proposals on ONE canary, privacy-guarded
|
||
|
||
**Independent test**: quickstart.md Scenario G
|
||
|
||
- [ ] T041 [US5] Scout host exploration spike → `scout/SPIKE.md`: compare standalone supervised watcher vs Hermes plugin vs herdr plugin/hook on: summary access, opt-in enforcement point, on-box summarization cost, notification surface, rollout story per image class (research.md R6; operator fork §14.4 "explore") — time-boxed, ends with a decision
|
||
- [ ] T042 [US5] Implement Scout v0 per SPIKE.md winner in `scout/` (or plugin location the spike picks): summary ingestion → embed → `market:catalog` match (tenant-scoped) → threshold → toast + deep link; opt-in via pull-policy config; rate cap (default 5/day)
|
||
- [ ] T043 [US5] Proposal outcome recording (shown/dismissed/accepted/suppressed) locally + summarized to `market:evidence` per data-model.md Proposal; conversion counts folded to listing stats via indexer telemetry pass
|
||
- [ ] T044 [US5] Deploy Scout v0 to ONE canary opt-in desktop; run Scenario G (incl. opt-out and rate-cap negative checks)
|
||
|
||
---
|
||
|
||
## Phase 8: Polish & Cross-Cutting
|
||
|
||
- [ ] T045 [P] Install telemetry write-back (FR-014): CLI posts install outcome records to `market:evidence`; indexer folds into listing `stats.installs` — `cli/src/m2_market/telemetry.py` + indexer pass
|
||
- [ ] T046 [P] `reindex` operational check task: run Scenario E, document recovery runbook in `docs/runbook.md` (incl. memory-api split-brain caveat from research.md R4)
|
||
- [ ] T047 Run full quickstart.md scenario sweep A–E on canaries; fix gaps; tick spec.md acceptance boxes in a verification note `specs/001-market-first-wedge/VERIFICATION.md`
|
||
|
||
---
|
||
|
||
## Dependencies
|
||
|
||
```
|
||
Phase 1 → Phase 2 → { US1 (P3..) , US2 (needs T007 registry; T027 needs T016 CLI scaffold) }
|
||
US1 T015 indexer ← T007 registry
|
||
US2 T032 discoverability ← US1 T015+T023 (search path)
|
||
US3 ← US1 ledger core (T010-T014)
|
||
T036 WEDGE GATE ← T024 (install), T032 (real listing), T033/T035 (funded wallets)
|
||
US4 ← US1 CLI (T023/T024) + T015 catalog
|
||
US5 ← T015 catalog; T042 ← T041 spike
|
||
Phase 8 T047 ← everything on its scenario list
|
||
```
|
||
|
||
Story completion order: US1 → US2 → US3 (gate) → US4 ∥ US5 → Polish.
|
||
US4 and US5 are independent of each other and of the gate.
|
||
|
||
## Parallel execution examples
|
||
|
||
- Phase 2: T004, T005, T008, T009 in parallel (disjoint files); T006/T007 after T004+T005.
|
||
- US1 wave 1: T010, T011 ∥ T016, T017, T018, T019, T020 (7 workers, disjoint files).
|
||
- US1 wave 2 (serial per file): T012→T013→T014; T021→T022 (same dir but disjoint files —
|
||
T021 and T022 CAN pair if split carefully; keep serial to be safe); T023→T024→T025 (same
|
||
cli.py — one worker).
|
||
- US2 seeds: T029, T030, T031 in parallel (disjoint solution dirs).
|
||
- Post-gate: US4 (T037–T040) and US5 (T041–T044) as two parallel workstreams.
|
||
|
||
## Implementation strategy
|
||
|
||
MVP = Phase 1 + 2 + US1 with a fixture listing (Scenario B/C locally), then US2 seeds make
|
||
it real, US3 funds it, T036 closes the wedge commercially (SC-001). US4/US5 extend surfaces
|
||
after the gate — they improve discovery, they don't block the win. fedlearn integration
|
||
(plan row 10) is intentionally NOT a task here: it becomes its own feature when the rails
|
||
land, guided by docs/fedlearn-coordination.md and the frozen apply-adapter contract.
|