m2-market/specs/001-market-first-wedge/tasks.md
m2 (AI Agent) 7381f1cdee wedge: tick T004/T005/T008/T009 (wave-2 herd converged)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:26:15 +02:00

176 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`)
- [ ] 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
- [ ] 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)
- [ ] 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)
- [ ] T011 [P] [US1] Implement X-API-Key auth (service/admin key classes) in `ledger/src/m2_ledger/auth.py`
- [ ] 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)
- [ ] 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
- [ ] 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)
- [ ] 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)
- [ ] T017 [P] [US1] Ledger client in `cli/src/m2_market/ledger.py` (install/refund/balance/license calls, error mapping to exit codes 3/4)
- [ ] T018 [P] [US1] Registry client in `cli/src/m2_market/registry.py` (fetch listing dirs, download release bundle, verify content_hash)
- [ ] T019 [P] [US1] Catalog client in `cli/src/m2_market/catalog.py` (memory-api semantic query, server-side tenant filter param)
- [ ] 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)
- [ ] 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
- [ ] T022 [US1] `m2core_sync` stub adapter in `cli/src/m2_market/apply/m2core_sync.py` raising RailsNotLanded (exit 6)
- [ ] T023 [US1] `search` + `show` commands in `cli/src/m2_market/cli.py` (catalog query → table/JSON; show renders evidence/price/permissions diff)
- [ ] 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
- [ ] 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 AE 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 (T037T040) and US5 (T041T044) 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.