m2-market/specs/S5-packaging-seeds.md
m2 (AI Agent) 8a8db9f2cc specs: m2-market spec discovery — 6 subsystem specs + synthesis index
herdr workforce output (6 codex angle-specialists -> claude synthesis):
S1 ledger/economy, S2 catalog/registry, S3 storefront (cargstore->M2 Store),
S4 Solution Scout (standalone watcher recommended), S5 packaging + 3 seed
Solutions (mm-pdf 25cr, agent-scaffold 35cr, competitor-scan 60cr),
S6 proposal engine + evidence loop.

SPEC-INDEX is the tie-break: 23-row cross-cutting contract table (16 mismatches
resolved, e.g. /v1 ledger paths win, m2store://listing/<id> deep link, one
m2.market.telemetry.v1 envelope, op_<slug> operator ids, tenant ids from m2-gpt),
8 deduped clarifications with defaults, 4-phase build order where the paid-install
wedge does NOT block on fedlearn (local ApplyAdapter until rails land), risk register.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:43:50 +02:00

647 lines
23 KiB
Markdown

# 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 <operator_id>`,
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 <listing_id>` 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 <listing_id>` runs again,
Then no duplicate debit occurs and the adapter returns no-op success.
Given an installed Solution,
When `m2-market uninstall <listing_id>` 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_id>/
├── listing.json
├── solution.json
├── evidence/
│ ├── provenance.jsonl
│ ├── cost.json
│ ├── scrub-attestation.json
│ └── permissions-diff.md
└── README.md
releases/<install_ref>.tar.gz # Forgejo release asset, not committed as a blob
```
Builder workspaces may live in this repo under `solutions/<slug>/`, 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/<skill-name>/...
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 <input.md> [--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 <selected m2o container>"
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_<uuid>",
"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 <path> --id <solution_id> --name <name>`
- `m2-market package import-fedlearn --cluster <cluster_id> --out solutions/<slug>`
- `m2-market package validate <bundle-dir> [--strict-tenant]`
- `m2-market package build <bundle-dir> --out dist/<install_ref>.tar.gz`
- `m2-market publish <bundle-dir> --seller <operator_id> --price <credits>`
Buyer:
- `m2-market install <listing_id> [--yes]`
- `m2-market uninstall <listing_id> [--yes]`
- `m2-market upgrade <listing_id> [--to <semver>]`
- `m2-market invoke <listing_id> -- <args>` optional wrapper that records invoke telemetry
Admin/review:
- `m2-market package diff-permissions <old-bundle> <new-bundle>`
- `m2-market package evidence-summary <bundle-dir>`
- `m2-market package verify-release <install_ref>`
### Install/uninstall/upgrade sequence
Install:
1. Resolve listing from `market:catalog`; fetch registry `solution.json`.
2. License hook: `GET /licenses/check?operator_id=<op>&listing_id=<listing>&major=<n>`.
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 <tar> --license-grant <grant_id>`.
7. Apply: `m2-core-sync apply --bundle <tar> --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 <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:<uuid>"}
200 {"tx_ids":[1,2],"grant":{"grant_id":"gr_...","covers_major":1}}
POST /tx/refund
{"ref":"lst_mm-pdf-report:chris-m2o:<uuid>","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:<low|medium|high>`,
`veto-until:<timestamp>`, 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 <input.md> [--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 <agent-name> \"<what this agent does>\" [--out <dir>]"
```
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/<agent-name>/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 \"<company or market>\" --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?