# S5 Spec — Packaging & Seed Solutions Grounded reads: `/tmp/mktspec/BRIEF.md`, `CONCEPT.md`, `context/SYSTEM-MAP.md`, `/home/m2/m2o/.planning/federated-learning/PLAN.md`, frozen marketplace schemas/contracts, live Docker fleet, live `m2-gpt` and `memory-api` health checks, `mm-pdf` and `agent-scaffold` skills. ## 1. Scope & non-goals ### MVP scope S5 owns the contract for turning a proven outcome into a sellable installable Solution: - builder workflow from either fedlearn `commercialize` or direct `m2-market publish` - canonical bundle layout: `solution.yaml`, `solution.json`, `listing.json`, `payload/`, `recipe.yaml`, `permissions.yaml`, `invoke.md`, `evidence/` - packaging validation and PR review requirements for `m2/market-registry` - install/uninstall/upgrade contract through `m2-market` and the `m2core-sync` apply adapter - pre-apply license check hook against `m2-ledger` - evidence attachment for provenance, cost telemetry, permissions, rollback, and installs - concrete seed bundle specs for `mm-pdf`, `agent-scaffold`, and `competitor-scan` The MVP must work with the existing frozen `schemas/solution.schema.json` and `schemas/listing.schema.json`. The human-authored source manifest is `solution.yaml`; the publish command renders canonical `solution.json` and `listing.json` that pass frozen JSON Schema validation. ### Later - metered usage pricing and revenue sharing by invocation - external public marketplace packaging - cryptographic bundle signatures beyond content hash verification - automated LLM-based tenant scrub beyond deterministic scanner + human attestations - public coin/exchange or external payment settlement - service escrow/dispute workflow for job-priced Solutions ### Non-goals - Ledger internals, transaction schema, and platform cut mechanics: S1. - Catalog indexing/search/ranking and storefront fields beyond package/listing emission: S2/S3. - Scout matching/proposal logic: S4. - Hermes build-vs-install proposal composition: S6. ## 2. User stories + acceptance criteria ### Story A — package a direct proven outcome Given a builder has a repeatable skill/playbook and at least one provenance record, When they run `m2-market package init --from-skill /home/m2/.claude/skills/mm-pdf --id sol_mm-pdf-report`, Then a bundle directory is created with `solution.yaml`, payload files, deploy recipe, permissions, invoke docs, and an evidence stub. Given that bundle directory, When the builder runs `m2-market publish ./bundle --price 25 --seller `, Then the CLI validates schema, permissions, hash, evidence, and tenant firewall rules, uploads a release asset, and opens a `m2/market-registry` PR in `in_review`. ### Story B — package from fedlearn commercialize Given fedlearn curator marks a cluster disposition `commercialize`, When the handoff calls the S5 package importer with the staged cluster/core artifact, Then the importer creates a Solution bundle draft preserving fedlearn provenance, cost telemetry, applicability, risk class, rollback note, and scrub status. Given the generated draft includes tenant-derived evidence, When `tenant_scope != "m2-core"`, Then publish fails unless `owner_initiated: true` and `scrub_status.double_scrubbed: true` are present and reviewed in the PR. ### Story C — install, verify, uninstall, and re-install Given a published fixed-price listing and a buyer with enough credits, When `m2-market install ` runs, Then the CLI checks or purchases a license, verifies bundle hash, calls the apply adapter, runs the recipe verify command, records install state, and prints the invocation hint. Given the same listing is already licensed and applied, When `m2-market install ` runs again, Then no duplicate debit occurs and the adapter returns no-op success. Given an installed Solution, When `m2-market uninstall ` runs, Then the adapter executes the recipe uninstall steps, leaves the license grant intact, updates install state to `rolled_back`, and emits uninstall telemetry. ### Story D — upgrade Given `lst_mm-pdf-report-v1.1.0` is published and the buyer has a major-version license for v1, When `m2-market upgrade lst_mm-pdf-report`, Then the CLI checks license coverage, downloads the new bundle, applies only the versioned changeset, verifies, and updates state from `1.0.0` to `1.1.0` without a new debit. Given a new major version `2.0.0`, When a v1 license holder upgrades, Then the CLI requires a new install purchase unless the listing declares explicit grace terms. ## 3. Interfaces & data contracts ### Registry paths Truth lives in Forgejo `git.machinemachine.ai/m2/market-registry`. ```text listings// ├── listing.json ├── solution.json ├── evidence/ │ ├── provenance.jsonl │ ├── cost.json │ ├── scrub-attestation.json │ └── permissions-diff.md └── README.md releases/.tar.gz # Forgejo release asset, not committed as a blob ``` Builder workspaces may live in this repo under `solutions//`, but the registry PR is the system of record. ### Bundle layout Release asset tarball: ```text solution.json # canonical frozen schema payload solution.yaml # source manifest; informational inside release listing.json # catalog-facing record for this version recipe.yaml # declarative apply/verify/uninstall/upgrade contract permissions.yaml # source permission declarations rendered into solution.permissions invoke.md # operator-facing invocation docs; no secrets payload/ skills//... scripts/... prompts/... config/... evidence/ provenance.jsonl cost.json scrub-attestation.json tests.json ``` `content_hash` is `sha256:` over the normalized tarball with `content_hash` blanked before hashing. Publish rejects hashes computed from a dirty or non-normalized bundle. ### `solution.yaml` source manifest ```yaml schema_version: m2.solution.source.v1 solution_id: sol_mm-pdf-report version: 1.0.0 name: MM PDF Report Generator summary: Render Machine.Machine branded PDF and HTML reports from Markdown. intent: Generate branded PDF/HTML deliverables from operator-authored Markdown. seller: op_sdjs tenant_scope: m2-core price: model: fixed amount: 25 currency: m2cr license: terms: "Per-operator install; commercial use inside owned/client workspaces permitted." major_version_coverage: true revenue_split: platform_pct: 10 runtime: harnesses: ["hermes", "openclaw"] surfaces: ["desktop"] adapter: m2core-sync applicability: image_classes: ["primus", "agent-latest"] roles: ["operator", "research", "proposal"] tool_requirements: ["bash", "docker", "google-chrome-in-target"] deployment: recipe_ref: recipe.yaml entrypoint: "mm-pdf generate [--out output.pdf] [--style dark|purple|light]" verify_command: "mm-pdf --version || test -x ~/.local/bin/mm-pdf" behavior: skills: - payload/skills/mm-pdf/SKILL.md tools: - name: docker purpose: render inside target m2o container permissions_ref: permissions.yaml evidence_ref: evidence/provenance.jsonl ``` Publish renders this into frozen `m2.solution.v1` by copying known schema fields and placing extra source-only fields such as `version` only in `listing.solution_version` and release tag. ### `recipe.yaml` ```yaml schema_version: m2.recipe.v1 adapter: m2core-sync install_ref: lst_mm-pdf-report-v1.0.0 targets: - id: skill type: directory source: payload/skills/mm-pdf dest: "${AGENT_HOME}/.claude/skills/mm-pdf" mode: "0755" - id: command type: file source: payload/scripts/mm-pdf dest: "${AGENT_HOME}/.local/bin/mm-pdf" mode: "0755" config_merge: [] post_install: - "mkdir -p ${AGENT_HOME}/.local/bin" verify: - "test -f ${AGENT_HOME}/.claude/skills/mm-pdf/SKILL.md" - "test -x ${AGENT_HOME}/.local/bin/mm-pdf" uninstall: - "rm -rf ${AGENT_HOME}/.claude/skills/mm-pdf" - "rm -f ${AGENT_HOME}/.local/bin/mm-pdf" rollback: strategy: restore_backups note: m2core-sync records file backups before replacement and restores by changeset_hash. ``` Rules: - destinations must be under `${AGENT_HOME}`, `${M2_HOME}`, or a declared per-solution data directory; host-level writes are rejected for marketplace MVP - no recipe command may contain inline secrets, tokens, or absolute tenant paths - install and uninstall must be idempotent - verify commands must be offline-safe and deterministic - adapter must run canary-first when installing fleet-wide ### `permissions.yaml` ```yaml schema_version: m2.permissions.v1 permissions: - id: fs-write-agent-home kind: filesystem scope: "${AGENT_HOME}/.claude/skills/mm-pdf" access: write reason: install skill files - id: docker-exec-local kind: command scope: "docker exec " access: execute reason: render PDF through Chrome inside a desktop container - id: memory-read kind: memory scope: "market:evidence" access: write reason: emit install/invoke telemetry ``` Rendered `solution.permissions[]` objects are displayed as the install permissions diff in CLI and M2 Store. ### Evidence contracts `evidence/provenance.jsonl`: ```json {"source":"fedlearn:submissions/sub_2026w27_chris_01hxyz","machine":"chris-m2o","session":"herdr-run-2026-07-01T12:00:00Z","excerpt":"Generated branded PDF from Markdown using Chrome headless.","tokens":12000,"wall_time":480} ``` `evidence/cost.json`: ```json { "schema_version": "m2.solution.cost.v1", "solution_id": "sol_mm-pdf-report", "samples": 3, "build_cost": {"tokens": 180000, "wall_time_seconds": 7200, "credits_equivalent": 18}, "invoke_cost_estimate": {"tokens": 1000, "wall_time_seconds": 90, "credits_equivalent": 1}, "maintenance_cost_estimate": {"credits_per_minor": 5}, "source_refs": ["market:evidence/mm-pdf-2026w27"] } ``` `evidence/scrub-attestation.json`: ```json { "schema_version": "m2.scrub.v1", "tenant_scope": "m2-core", "owner_initiated": true, "double_scrubbed": true, "secret_scan": "passed", "pii_scan": "passed", "reviewers": ["op_builder", "op_reviewer"], "reviewed_at": "2026-07-02T00:00:00Z" } ``` Install/invoke telemetry is appended locally, then summarized to `market:evidence`: ```json { "schema_version": "m2.solution.telemetry.v1", "event_id": "evt_", "event": "install|uninstall|upgrade|invoke", "operator_id": "op_buyer", "tenant": "m2-core", "listing_id": "lst_mm-pdf-report", "solution_version": "1.0.0", "machine": "chris-m2o", "result": "success|failed|rolled_back", "wall_time": 12.4, "tokens": 0, "credits": 25, "error_class": null } ``` ### CLI verbs Builder: - `m2-market package init --from-skill --id --name ` - `m2-market package import-fedlearn --cluster --out solutions/` - `m2-market package validate [--strict-tenant]` - `m2-market package build --out dist/.tar.gz` - `m2-market publish --seller --price ` Buyer: - `m2-market install [--yes]` - `m2-market uninstall [--yes]` - `m2-market upgrade [--to ]` - `m2-market invoke -- ` optional wrapper that records invoke telemetry Admin/review: - `m2-market package diff-permissions ` - `m2-market package evidence-summary ` - `m2-market package verify-release ` ### Install/uninstall/upgrade sequence Install: 1. Resolve listing from `market:catalog`; fetch registry `solution.json`. 2. License hook: `GET /licenses/check?operator_id=&listing_id=&major=`. 3. If no valid grant: `POST /tx/install` with price, seller, platform cut, and ref. 4. Download release tarball by `listing.install_ref`. 5. Verify `content_hash`. 6. Adapter preflight: `m2-core-sync plan --bundle --license-grant `. 7. Apply: `m2-core-sync apply --bundle --state ~/.m2-market/state.json`. 8. Verify recipe. 9. Write install state and telemetry. Uninstall: 1. Confirm installed state exists. 2. Run `m2-core-sync uninstall --install-ref ` or adapter rollback by `changeset_hash`. 3. Verify declared uninstall checks. 4. Mark `rolled_back`; do not revoke license by default. 5. Emit telemetry. Upgrade: 1. Resolve latest published listing version with same listing id. 2. Check major license coverage through ledger. 3. Compute permission and recipe diff. 4. Require confirmation if permissions broaden. 5. Apply upgrade changeset; rollback to prior version on verify failure. 6. Emit telemetry. ### License check hook Required m2-ledger endpoints consumed by S5 install path: ```http GET /licenses/check?operator_id=op_buyer&listing_id=lst_mm-pdf-report&major=1 200 {"valid":true,"grant_id":"gr_...","covers_major":1} 404 {"valid":false} POST /tx/install {"buyer":"op_buyer","listing_id":"lst_mm-pdf-report","seller":"op_sdjs","amount":25,"currency":"m2cr","ref":"lst_mm-pdf-report:chris-m2o:"} 200 {"tx_ids":[1,2],"grant":{"grant_id":"gr_...","covers_major":1}} POST /tx/refund {"ref":"lst_mm-pdf-report:chris-m2o:","reason":"apply_failed"} 200 {"refunded":true} ``` The adapter must receive `grant_id` during plan/apply. A bundle apply without a valid grant must fail before any file writes. ## 4. Integration contract with other subsystems - S1 ledger: S5 calls `/licenses/check`, `/tx/install`, `/tx/refund`; ledger grants remain authoritative for reinstall/upgrade. - S2 catalog: S5 emits valid `listing.json` and `solution.json`; S2 indexes merged registry records into `market:catalog` and folds telemetry into stats. - S3 store: S3 shows `invoke.md`, evidence, price, and permission diff from S5 bundle metadata; install button shells `m2-market install --json`. - S4 Scout: S4 proposals deep-link to listing ids; S5 guarantees install refs and invocation hints exist after install. - S6 proposal engine: S6 consumes `evidence/cost.json`, provenance, price model, and invoke docs for build-vs-install comparisons. - fedlearn: fedlearn `commercialize` calls `m2-market package import-fedlearn`; staged provenance and applicability map directly into `solution.evidence[]` and `applicability`. - m2-core-sync: S5 recipe is the marketplace-specific payload fed to the universal runtime sync/apply path. Until fedlearn rails land, the existing `ApplyAdapter` local path can execute the same recipe shape. - memory-api: S5 writes only summarized package evidence to `market:evidence`; raw tenant data stays out of shared memory. - Forgejo: PR labels are `market:listing`, `status:in_review`, `risk:`, `veto-until:`, optional `approved`, optional `veto`. ## 5. Options compared ### Source manifest format - JSON only: aligns with schemas, poor for human packaging. - YAML source rendered to JSON: best authoring ergonomics while preserving frozen schema. - Python package metadata: too language-specific. Recommendation: YAML source plus canonical JSON output. ### Install backend - Direct local file copy: good interim adapter, weak as long-term universal fleet path. - m2-core-sync: matches locked runtime-sync decision and mixed image constraint. - Container image install: rejected for MVP; local registry route is broken and secrets must not be baked. Recommendation: recipe contract targets `m2core-sync`; local adapter may execute same recipe while fedlearn rails are incomplete. ### Pricing model per package - Fixed install: deterministic and supported by frozen schema. - Job price: required by locked decision, but current JSON Schema only enum-validates `fixed`. Recommendation: MVP bundles with direct install use `fixed`. Job-priced packages are listed as `inventory_type: service` or use `price_ext.model: job` in `solution.yaml` until `solution.schema.json` v2 adds `job`. ### Evidence storage - Evidence embedded only in `solution.json`: too shallow for audit. - Evidence files beside registry listing: reviewable, indexed, and durable. - Memory-only evidence: not authoritative. Recommendation: registry evidence files are truth; memory `market:evidence` is derived and summarized. ## 6. Seed Solution package specs ### Seed 1 — `mm-pdf` Source asset: `/home/m2/.claude/skills/mm-pdf`. ```yaml solution_id: sol_mm-pdf-report listing_id: lst_mm-pdf-report version: 1.0.0 name: MM PDF Report Generator price: {model: fixed, amount: 25, currency: m2cr} category: documents tenant_scope: m2-core install_targets: - "${AGENT_HOME}/.claude/skills/mm-pdf" - "${AGENT_HOME}/.local/bin/mm-pdf" entrypoint: "mm-pdf generate [--out output.pdf] [--style dark|purple|light]" ``` Bundle contents: - `payload/skills/mm-pdf/SKILL.md` - `payload/skills/mm-pdf/templates/dark.css` - `payload/skills/mm-pdf/templates/purple.css` - `payload/scripts/mm-pdf` wrapper that calls skill script `generate.sh` - `recipe.yaml`, `permissions.yaml`, `invoke.md`, `evidence/*` Permissions: - write skill directory under `${AGENT_HOME}` - write executable under `${AGENT_HOME}/.local/bin` - execute `docker ps`, `docker exec`, `docker cp` against local m2o container - write output PDFs/HTML only to user-selected working directory - no network secrets; no tenant memory reads required Runtime deps: - bash, docker CLI on host/container - target m2o container with Node/npm and Google Chrome; observed `nasr-m2o` has this - optional Marp path for slides later, not required for MVP Install targets: - primus and agent-latest desktops - canaries: `chris-m2o` first, `gunnar-m2o` second Evidence: - skill file path and script provenance from `/home/m2/.claude/skills/mm-pdf` - successful local render transcript - cost sample for Markdown-to-PDF runs ### Seed 2 — `agent-scaffold` Source asset: `/home/m2/.claude/skills/agent-scaffold`. ```yaml solution_id: sol_agent-scaffold listing_id: lst_agent-scaffold version: 1.0.0 name: Agent Workspace Scaffold price: {model: fixed, amount: 35, currency: m2cr} category: agent-ops tenant_scope: m2-core install_targets: - "${AGENT_HOME}/.claude/skills/agent-scaffold" - "${AGENT_HOME}/.local/bin/agent-scaffold" entrypoint: "agent-scaffold \"\" [--out ]" ``` Bundle contents: - `payload/skills/agent-scaffold/SKILL.md` - `payload/skills/agent-scaffold/scripts/scaffold.py` - `payload/scripts/agent-scaffold` wrapper around `python3 scaffold.py` - `payload/templates/PRD.md.j2`, `SOUL.md.j2`, `MEMORY.md.j2` only if extracted from script in a future cleanup; MVP may keep script-generated templates inline - `invoke.md` documenting env vars `M2_MEMORY_API_URL`, `M2_MEMORY_AGENT_ID`, `M2_MEMORY_API_KEY` Permissions: - write `~/agents//PRD.md`, `SOUL.md`, `MEMORY.md` - read memory-api through configured endpoint and `X-API-Key` - no raw client data export; memory query runs under buyer tenant/agent id - optional write to git is manual, not automated in MVP Runtime deps: - Python 3 standard library - memory-api reachable from desktop or host; live endpoint verified healthy - `M2_MEMORY_API_KEY` injected at runtime, never packaged Install targets: - primus and agent-latest desktops - Hermes/OpenClaw compatible as a CLI/skill, not harness-specific Evidence: - source skill and script from `/home/m2/.claude/skills/agent-scaffold` - sample scaffold output with no tenant-specific content - wall-time/token estimate from memory query and local file generation ### Seed 3 — `competitor-scan` Source asset: no canonical local skill found; package as a new outcome bundle from proven research-report workflow, with first implementation as prompts + scripts rather than a tenant-derived artifact. ```yaml solution_id: sol_competitor-scan listing_id: lst_competitor-scan version: 1.0.0 name: Competitor Scan Report price: {model: fixed, amount: 60, currency: m2cr} category: research tenant_scope: m2-core install_targets: - "${AGENT_HOME}/.claude/skills/competitor-scan" - "${AGENT_HOME}/.local/bin/competitor-scan" entrypoint: "competitor-scan \"\" --out report.md [--pdf]" ``` Bundle contents: - `payload/skills/competitor-scan/SKILL.md` with research workflow: scope, source collection, competitor matrix, positioning, pricing, risks, citations, recommendations - `payload/prompts/competitor-scan.md` - `payload/scripts/competitor-scan` orchestrator: accepts subject, writes `report.md`, optionally calls `mm-pdf` if installed - `payload/templates/report.md` - `recipe.yaml`, `permissions.yaml`, `invoke.md`, `evidence/*` Permissions: - network egress for web research through approved tools/connectors only - read/write local report output directory - optional memory read for prior public market research under buyer tenant - no raw tenant CRM/doc access by default - if using browser automation, permission must be explicit and shown in install diff Runtime deps: - Hermes or OpenAI-wire harness through m2-gpt - approved search/browser connector where available - optional `mm-pdf` installed for PDF output Install targets: - primus and agent-latest desktops - initial canary should be non-client tenant or `m2-core` only until evidence is scrubbed Evidence: - generic public-market sample only - citations list in output fixture - cost telemetry from a dry-run scan: LLM tokens, wall time, number of sources Pricing note: - fixed install price `60 m2cr` for the reusable workflow - a human/operator-delivered bespoke competitor scan should be a separate service listing with job pricing once schema v2 supports `price.model: job` ## 7. Risks/edge cases - Tenant leakage: reject tenant-scoped bundles without owner initiation and double scrub; competitor-scan must not package client-specific examples as seed evidence. - Secrets in payload: deterministic scanner blocks API keys, JWTs, SSH keys, high-entropy strings, `.env`, browser profiles, and absolute secret paths. - License/apply split brain: if debit succeeds and apply fails, CLI must call refund and record failed install telemetry; grant without apply remains visible for audit. - Upgrade permission creep: any broadened permission requires explicit confirmation and PR review note. - Uninstall data loss: recipes may remove installed files but must not delete user-created output directories unless declared as disposable cache. - Host fragility: install recipes are canary-first, idempotent, reversible, and confined to agent home paths. - Local registry route is broken: packages are Forgejo release assets, not Docker images or registry pushes. - Job pricing mismatch: locked business decision allows job pricing, but frozen schema only validates `fixed`; represent jobs as service inventory until schema v2. - Fake evidence: registry PR requires provenance files and at least one reproducible verify or sample output; catalog/memory evidence is not authoritative. ## 8. [NEEDS CLARIFICATION] 1. Which operator id is the seller of record for the three seed Solutions: `op_sdjs`, `op_m2bd`, or a platform-owned `op_m2-core`? 2. Should `competitor-scan` MVP be install-only fixed price, or should it launch as the first service/job listing despite the frozen `solution.schema.json` only accepting fixed price? 3. What exact `m2-core-sync` command names will fedlearn ship for uninstall and upgrade (`uninstall`, `rollback`, `apply --upgrade`), so S5 can replace adapter placeholders without aliasing?