m2-market/specs/S2-catalog-registry.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

28 KiB

S2 — Catalog & Registry SPEC

Grounded in live inspection on 2026-07-02: /home/m2/m2-market already contains v1 schemas/solution.schema.json and schemas/listing.schema.json, first-wedge contracts for registry/catalog/CLI, and a local /home/m2/m2-core clone with fedlearn schemas. Live services are reachable: https://gpt.machinemachine.ai/health, https://memory.machinemachine.ai/health, and https://git.machinemachine.ai/api/healthz; Docker shows Forgejo, memory-api, Qdrant, m2o desktops, and Coolify on the coolify network.

1. Scope & non-goals

MVP scope

  • Freeze the marketplace catalog/registry contracts for installable Solutions.
  • Treat Forgejo org m2 as system of record:
    • registry repo: git.machinemachine.ai/m2/m2-market unless split later into m2/market-registry;
    • releases carry immutable Solution bundles;
    • listing PRs ride the fedlearn scored PR + human veto pipeline and add the commercialize disposition.
  • Define solution.schema.json as a commercial superset of fedlearn m2-core-manifest.schema.json and the fedlearn candidate fields already assumed by m2-market.
  • Define listing.schema.json as the catalog-facing projection used by cargstore/M2 Store, Scout, Hermes, and CLI.
  • Define market:catalog in memory-api as a rebuildable semantic index, not source of truth.
  • Define tenant-scoped search/show/install/publish CLI verbs for m2-market.
  • Support v1 inventory types solution|capability|resource|service, but only solution is installable in MVP. service may be published and discovered for job-priced work.

Non-goals

  • Ledger internals, wallet accounting, starter grants, refunds, payout mechanics: S1 owns them. S2 defines only catalog-side fields and ledger touchpoints.
  • Store UI design and Electron packaging: S3 owns cargstore/M2 Store.
  • Scout matching host, thresholds, and notification UX: S4 owns it. S2 defines the catalog query contract Scout consumes.
  • Bundle build format, runtime-sync implementation, signing, and packaging details beyond registry paths and manifest fields: S5 owns packaging.
  • Hermes build-vs-install proposal policy and scoring: S6 owns proposal engine behavior.

2. User stories + acceptance criteria

Story S2.1 — Publish a reviewed listing

As a builder, I package a repeatable outcome with evidence, price, seller identity, license, permissions, and deployment metadata, then submit it for review.

  • Given a valid bundle directory containing solution.json, listing.json, recipe.yaml, payload files, and evidence files, when I run m2-market publish <bundle-dir>, then the CLI validates schemas, computes the bundle hash, creates a Forgejo branch listing/<listing_id>-v<semver>, uploads a release asset, and opens a PR.
  • Given a listing PR, when CI runs, then it must validate both schemas, verify evidence is non-empty, verify solution.content_hash matches the release asset, verify tenant firewall rules, and render price, permission diff, evidence summary, and rollback note in the PR.
  • Given a PR with no veto after the configured review window and an approved label, when it merges, then listing.status becomes published and the indexer upserts it to market:catalog.
  • Given a PR with label veto or /veto <reason>, when the watcher processes it, then the PR is closed without indexing and the listing remains draft or rejected.

Story S2.2 — Search tenant-allowed listings

As an operator, I search by outcome rather than package id and see only listings my tenant may use.

  • Given a published listing with tenant_visibility: ["*"], when any tenant searches a semantically related query, then the listing may appear.
  • Given a published listing with tenant_visibility: ["gst"], when a caller from tenant nasr searches, then the listing must not be returned by CLI, Store, Scout, or Hermes.
  • Given market:catalog is deleted or stale, when m2-market-indexer reindex runs, then the catalog is rebuilt from Forgejo main without needing any memory-api-only data.

Story S2.3 — Show installable evidence and permissions

As a buyer, I inspect a listing before spending credits.

  • Given a listing id, when I run m2-market show <listing_id>, then output includes name, version, seller, price model, license, evidence summary, applicability, permissions, install ref, and status.
  • Given the listing is delisted, when I run show, then it is visible only by exact id or license/install context and is not returned by broad search.

Story S2.4 — Install through catalog contract

As a buyer, I install a published solution from the registry release that the listing points at.

  • Given a published fixed-price listing, when m2-market install <listing_id> runs, then S2 resolves the listing from registry/catalog, verifies status and tenant visibility, fetches release <listing_id>-v<semver>, verifies content_hash, and hands the commercial charge request to S1 before calling the S5 apply adapter.
  • Given a job-priced listing, when install is attempted, then the CLI must refuse direct install with exit code 1 and print the required proposal/job flow handoff; job settlement is S1/S6 territory.

Story S2.5 — Commercialize a fedlearn candidate

As a curator, I mark a scored fedlearn candidate as commercially listable rather than free core.

  • Given a fedlearn cluster passed scoring, when the reviewer chooses commercialize, then the pipeline opens the same listing PR shape as m2-market publish.
  • Given a fedlearn candidate is tenant-derived, when commercialize is selected without owner_initiated: true and scrub_status.double_scrubbed: true, then CI rejects it before human review.
  • Given the item is fleet-infra learning, when reviewed, then it must use promote-to-core, not commercialize.

3. Interfaces & data contracts

3.1 Forgejo registry of record

Canonical URL:

  • MVP repo: https://git.machinemachine.ai/m2/m2-market
  • Registry paths in that repo:
schemas/
  solution.schema.json
  listing.schema.json
listings/<listing_id>/
  listing.json
  solution.json
  evidence/
    <evidence_id>.json
releases:
  <listing_id>-v<semver>
    assets:
      solution-bundle.tar.zst
audit/
  catalog-index/YYYY-MM-DDTHHMMSSZ.json

If operations later split a clean m2/market-registry repo, paths and release tags stay identical. The split is a remote/location change, not a contract change.

Release bundle shape:

solution-bundle.tar.zst
  solution.json
  listing.json
  recipe.yaml
  payload/
  evidence/
  manifest.json

manifest.json inside the bundle:

{
  "schema_version": "m2.solution_bundle.v1",
  "listing_id": "lst_competitor-scan",
  "solution_id": "sol_competitor-scan",
  "solution_version": "1.0.0",
  "created_at": "2026-07-02T00:00:00Z",
  "files": [
    {"path": "solution.json", "sha256": "hex"},
    {"path": "recipe.yaml", "sha256": "hex"}
  ]
}

Release tag format: <listing_id>-v<semver>, matching listing.install_ref.

3.2 solution.schema.json v1

Normative schema lives at schemas/solution.schema.json. S2 requires the existing schema be tightened before implementation to cover the locked decisions below.

Required top-level fields:

{
  "schema_version": "m2.solution.v1",
  "solution_id": "sol_mm-pdf-report",
  "name": "MM PDF Report",
  "summary": "Generate branded PDF reports from structured input.",
  "intent": "Turn approved analysis into a styled client-ready PDF report.",
  "behavior": {},
  "tools": [],
  "runtime": {},
  "memory_schema": {},
  "permissions": [],
  "deployment": {
    "recipe_ref": "recipe.yaml",
    "entrypoint": "m2-solution run mm-pdf-report",
    "verify_command": "m2-solution verify mm-pdf-report"
  },
  "applicability": {
    "image_classes": ["primus", "agent-latest"],
    "roles": ["operator"],
    "tool_requirements": []
  },
  "tenant_scope": "m2-core",
  "evidence": [],
  "content_hash": "sha256:<64-hex>",
  "price": {},
  "seller": "op_sdjs",
  "license": {},
  "revenue_split": {}
}

Fedlearn-compatible core subset:

  • schema_version
  • applicability.image_classes[]
  • applicability.roles[]
  • applicability.tool_requirements[]
  • tenant_scope
  • evidence[]
  • content_hash
  • scrub_status when tenant-derived

Commercial superset fields:

Price

The current local schema only permits fixed price. MVP must support the locked v1 models:

{
  "model": "fixed",
  "amount": 40,
  "currency": "m2cr"
}
{
  "model": "job",
  "currency": "m2cr",
  "quote": {
    "min_amount": 120,
    "max_amount": 300,
    "expires_after_hours": 72,
    "requires_acceptance": true
  },
  "settlement": {
    "ledger_reason": "route",
    "escrow_required": true
  }
}

Rules:

  • fixed is directly installable.
  • job is discoverable and showable, but install starts a proposal/job route rather than a direct apply.
  • currency is always m2cr in MVP.
  • amount is an integer number of credits; no fractional credits.

Seller

{
  "operator_id": "op_sdjs",
  "display_name": "sdjs operator",
  "contact_ref": "forgejo:m2/sdjs",
  "tenant_id": "machine-machine"
}

The existing schema allows a bare string. For implementation, accept both during migration, but normalize registry records to the object form above.

License

{
  "license_id": "lic_m2_market_standard_v1",
  "terms": "m2-market-standard-v1",
  "scope": "operator",
  "major_version_coverage": true,
  "allows_internal_modification": true,
  "allows_redistribution": false,
  "support": "best_effort"
}

Rules:

  • License grants are keyed by (operator_id, listing_id, major_version).
  • Delisting does not revoke existing grants.
  • Redistribution is false unless explicitly allowed.

Revenue split

{
  "platform_pct": 10,
  "seller_pct": 90,
  "contributors": [
    {"operator_id": "op_researcher", "pct": 10}
  ]
}

Rules:

  • Percentages must total 100.
  • Default platform cut is 10 percent per locked brief.
  • Ledger execution and payout are S1, but registry stores the split used to explain pricing and reproduce the charge request.

Permissions

[
  {
    "permission_id": "perm_memory_read_project",
    "kind": "memory",
    "access": "read",
    "scope": "tenant",
    "target": "project_context",
    "reason": "Read project brief and prior approved notes.",
    "required": true
  },
  {
    "permission_id": "perm_browser_control",
    "kind": "desktop",
    "access": "take-control",
    "scope": "operator-approved",
    "target": "browser",
    "reason": "Operate browser only after user approval.",
    "required": false
  }
]

Allowed kind: memory|filesystem|network|desktop|mcp|secret_ref|ledger|browser.

Allowed access: read|write|execute|observe|suggest|take-control|hand-back.

Rules:

  • No raw secret values. Use secret_ref identifiers only.
  • Permission diff is required in listing PR body and m2-market show.
  • Tenant-scoped permissions cannot target another tenant.

Evidence

[
  {
    "evidence_id": "ev_mm_pdf_2026w27_001",
    "source": "herdr",
    "ref": "evidence/ev_mm_pdf_2026w27_001.json",
    "machine": "chris-m2o",
    "session": "sess_2026w27_pdf",
    "summary": "Generated a branded PDF report from structured findings.",
    "tokens": 32000,
    "wall_time_seconds": 840,
    "cost_estimate_m2cr": 12,
    "outcome": "success",
    "scrub_status": {
      "secrets_redacted": true,
      "pii_redacted": true,
      "double_scrubbed": true
    }
  }
]

Rules:

  • At least one evidence item is required for publish.
  • Evidence files in evidence/ must not contain raw tenant data or secrets.
  • Tenant-derived work requires owner initiation and double scrub.

3.3 listing.schema.json v1

Normative schema lives at schemas/listing.schema.json. Required implementation shape:

{
  "schema_version": "m2.listing.v1",
  "listing_id": "lst_mm-pdf-report",
  "solution_id": "sol_mm-pdf-report",
  "solution_version": "1.0.0",
  "inventory_type": "solution",
  "name": "MM PDF Report",
  "summary": "Generate branded PDF reports from structured input.",
  "category": "reporting",
  "keywords": ["pdf", "report", "brand"],
  "icon": "icons/mm-pdf.png",
  "price": {"model": "fixed", "amount": 40, "currency": "m2cr"},
  "seller": {"operator_id": "op_sdjs", "display_name": "sdjs operator"},
  "license_ref": "lic_m2_market_standard_v1",
  "evidence_summary": "Validated on chris-m2o with one successful report run.",
  "tenant_visibility": ["*"],
  "applicability": {
    "image_classes": ["primus", "agent-latest"],
    "roles": ["operator"],
    "tool_requirements": []
  },
  "permissions_summary": [
    {"kind": "filesystem", "access": "write", "target": "~/.m2-market/solutions/mm-pdf"}
  ],
  "stats": {
    "installs": 0,
    "rating": 0,
    "proposals_shown": 0,
    "proposals_accepted": 0
  },
  "status": "published",
  "install_ref": "lst_mm-pdf-report-v1.0.0",
  "registry": {
    "repo": "m2/m2-market",
    "commit": "<git-sha>",
    "release_url": "https://git.machinemachine.ai/m2/m2-market/releases/tag/lst_mm-pdf-report-v1.0.0"
  }
}

State machine:

draft -> in_review -> published -> delisted
             |             |
             v             v
          rejected      delisted

State rules:

  • draft: local or branch-only; not indexed.
  • in_review: PR exists; not indexed.
  • published: merged to main; indexed.
  • delisted: exact-id show remains possible for license/install history; broad search hides it.
  • rejected: optional audit state; not indexed.

3.4 Listing PR template

Every publish/commercialize PR body must include:

## Commercialize Review

Listing: <listing_id>
Solution: <solution_id>@<version>
Inventory type: solution|service|capability|resource
Price: <fixed amount m2cr | job quote range>
Seller: <operator_id>
Tenant visibility: <["*"] or tenant ids>

## Evidence
<summary with evidence refs>

## Permission Diff
<new permissions vs current installed state or "new install">

## Tenant Firewall
Owner initiated: true|false|not-applicable
Double scrubbed: true|false|not-applicable

## Rollback
<how to uninstall/rollback release asset>

## Veto
Veto until: <timestamp>
Use label `veto` or comment `/veto <reason>`.

Required labels:

  • market:listing
  • disposition:commercialize
  • risk:low|medium|high
  • inventory:solution|capability|resource|service
  • veto-until:<iso8601>
  • tenant:<tenant-id|public>

Merge gates:

  • schema validation green;
  • content hash check green;
  • secret scan green;
  • tenant firewall check green;
  • no veto label;
  • approved label present;
  • high-risk listings require manual merge, never timeout auto-merge.

3.5 market:catalog partition in memory-api

Partition name: market:catalog.

Source: published listings/<listing_id>/listing.json plus selected fields from solution.json.

Writer: only m2-market-indexer.

Readers: m2-market CLI, M2 Store, Scout, Hermes market-propose skill.

Record shape stored in memory-api:

{
  "agent_id": "market:catalog",
  "text": "MM PDF Report\nGenerate branded PDF reports...\nintent...\nkeywords...\nevidence summary...",
  "metadata": {
    "schema_version": "m2.catalog_record.v1",
    "listing_id": "lst_mm-pdf-report",
    "solution_id": "sol_mm-pdf-report",
    "solution_version": "1.0.0",
    "inventory_type": "solution",
    "name": "MM PDF Report",
    "summary": "Generate branded PDF reports from structured input.",
    "category": "reporting",
    "keywords": ["pdf", "report", "brand"],
    "price": {"model": "fixed", "amount": 40, "currency": "m2cr"},
    "seller_operator_id": "op_sdjs",
    "tenant_visibility": ["*"],
    "status": "published",
    "install_ref": "lst_mm-pdf-report-v1.0.0",
    "registry_commit": "<git-sha>",
    "content_hash": "sha256:<64-hex>",
    "applicability": {
      "image_classes": ["primus", "agent-latest"],
      "roles": ["operator"],
      "tool_requirements": []
    },
    "stats": {
      "installs": 0,
      "rating": 0,
      "proposals_shown": 0,
      "proposals_accepted": 0
    }
  }
}

Index text fields:

  • listing.name
  • listing.summary
  • listing.category
  • listing.keywords
  • solution.intent
  • solution.description
  • solution.behavior summaries only
  • listing.evidence_summary
  • evidence summaries, never raw evidence payloads

Tenant-scoped query contract:

POST /memory/search
X-API-Key: <runtime key>
Content-Type: application/json

{
  "agent_id": "market:catalog",
  "query": "generate branded PDF reports",
  "limit": 10,
  "filters": {
    "status": "published",
    "tenant_visibility_any": ["*", "machine-machine"],
    "inventory_type": ["solution"],
    "price_model": ["fixed", "job"],
    "image_class": "primus"
  }
}

Response normalized by m2-market-indexer/CLI:

{
  "items": [
    {
      "listing_id": "lst_mm-pdf-report",
      "score": 0.83,
      "listing": {},
      "registry_commit": "<git-sha>"
    }
  ]
}

Indexing rules:

  • published records are upserted.
  • delisted records are deleted from broad catalog index but retained in registry.
  • draft, in_review, and rejected are never indexed.
  • All writes require memory-api X-API-Key.
  • If memory-api split-brain recurs, registry remains truth and reindex is the recovery path.

Indexer commands:

m2-market-indexer watch --repo /home/m2/m2-market --poll-seconds 300
m2-market-indexer reindex --repo /home/m2/m2-market --partition market:catalog
m2-market-indexer validate --repo /home/m2/m2-market

Telemetry partition: market:evidence.

Install/proposal telemetry record:

{
  "agent_id": "market:evidence",
  "text": "Install succeeded for lst_mm-pdf-report on chris-m2o in 52 seconds.",
  "metadata": {
    "schema_version": "m2.market_evidence.v1",
    "event_id": "evt_01h...",
    "event_type": "install_succeeded",
    "listing_id": "lst_mm-pdf-report",
    "operator_id": "op_m2bd",
    "tenant_id": "machine-machine",
    "desktop": "chris-m2o",
    "ts": "2026-07-02T00:00:00Z",
    "duration_seconds": 52,
    "ledger_ref": "install:lst_mm-pdf-report:<uuid>",
    "scrubbed": true
  }
}

Telemetry folding:

  • A scheduled indexer job folds aggregate counts into listing.stats.
  • Stats changes may be committed directly by an indexer bot or batched into a PR; registry remains truth.
  • Self-purchases are excluded from quality metrics.

3.6 CLI contract

Config file: ~/.m2-market/config.toml, mode 0600.

operator_id = "op_m2bd"
tenant = "machine-machine"
image_class = "primus"

ledger_url = "http://m2-ledger:8000"
ledger_api_key = "..."

memory_api_url = "https://memory.machinemachine.ai"
memory_api_key = "..."

forgejo_url = "https://git.machinemachine.ai"
forgejo_token = "..."

registry_repo = "m2/m2-market"
apply_adapter = "m2core-sync"

All commands support --json.

m2-market search "branded pdf report" --type solution --limit 10

Behavior:

  • queries market:catalog;
  • applies tenant filter server-side when memory-api supports it and client-side as a backstop;
  • hides delisted records;
  • returns id, name, summary, inventory type, price, seller, installs, rating, score.

m2-market show

m2-market show lst_mm-pdf-report

Behavior:

  • resolves exact listing from registry first, catalog second;
  • shows evidence, price, seller, license, permissions, applicability, status, install ref;
  • for delisted listing, shows delisted warning and existing license status if available.

m2-market install

m2-market install lst_mm-pdf-report --yes

Behavior for price.model=fixed:

  1. Resolve listing and verify status=published.
  2. Verify tenant visibility and applicability.
  3. Display price, license, and permission diff unless --yes.
  4. Ask S1 ledger to debit buyer and create grant.
  5. Fetch release asset named by install_ref.
  6. Verify solution.content_hash.
  7. Call S5 apply adapter (m2-core-sync when landed; local adapter only for pre-rails wedge).
  8. Write install telemetry to market:evidence.

Behavior for price.model=job:

  • no direct apply;
  • prints proposal handoff instructions and exits non-zero until S6 job flow exists.

Exit codes:

  • 0: success or idempotent no-op
  • 1: usage or unsupported flow such as direct install of job listing
  • 2: listing not found
  • 3: insufficient funds from ledger
  • 4: apply failed after charge; refund/remediation delegated to S1/S5 flow
  • 5: validation, tenant firewall, or permission rejection
  • 6: sync rails not landed

m2-market publish

m2-market publish ./bundle-dir

Behavior:

  • validates solution.json, listing.json, evidence, release manifest, content hash;
  • enforces tenant/firewall checks before pushing;
  • creates or updates listings/<listing_id>/;
  • creates release <listing_id>-v<semver>;
  • opens a PR using the listing template.

Publish options:

m2-market publish ./bundle-dir --draft
m2-market publish ./bundle-dir --tenant-visibility '*'
m2-market publish ./bundle-dir --price fixed:40
m2-market publish ./bundle-dir --price-job 120:300

m2-market wallet

Catalog CLI may expose wallet for operator convenience, but S1 owns its API semantics.

m2-market reindex

Admin-only command:

m2-market reindex --full

Invokes m2-market-indexer reindex and emits audit record under audit/catalog-index/<timestamp>.json.

3.7 APIs S2 consumes or exposes

S2 does not add a public long-lived service in MVP. It uses these APIs:

Forgejo:

GET /api/v1/repos/m2/m2-market
GET /api/v1/repos/m2/m2-market/contents/listings/<listing_id>/listing.json?ref=main
POST /api/v1/repos/m2/m2-market/pulls
POST /api/v1/repos/m2/m2-market/releases
GET /api/v1/repos/m2/m2-market/releases/tags/<listing_id>-v<semver>

memory-api:

GET /health
POST /memory/store
POST /memory/search

ledger S1 touchpoints:

POST /tx/install
GET /license/{operator_id}/{listing_id}

The install charge request S2 sends to S1:

{
  "buyer_operator_id": "op_m2bd",
  "listing_id": "lst_mm-pdf-report",
  "solution_version": "1.0.0",
  "price": {"model": "fixed", "amount": 40, "currency": "m2cr"},
  "seller": {"operator_id": "op_sdjs"},
  "revenue_split": {"platform_pct": 10, "seller_pct": 90},
  "ref": "install:lst_mm-pdf-report:<uuid>"
}

3.8 File paths

Registry:

  • /home/m2/m2-market/schemas/solution.schema.json
  • /home/m2/m2-market/schemas/listing.schema.json
  • /home/m2/m2-market/listings/<listing_id>/listing.json
  • /home/m2/m2-market/listings/<listing_id>/solution.json
  • /home/m2/m2-market/listings/<listing_id>/evidence/*.json

CLI local state:

  • ~/.m2-market/config.toml
  • ~/.m2-market/state.json
  • ~/.cache/m2-market/releases/<install_ref>/solution-bundle.tar.zst

Applied solution target:

  • S5 decides exact targets, but S2 requires runtime-sync compatibility across primus and agent-latest; no image rebuild and no local registry push/pull.

4. Integration contract with other subsystems

S1 ledger

S2 provides:

  • listing id, seller, price, revenue split, license terms, and install ref.
  • install charge request for fixed-price installs.

S1 provides:

  • debit/credit/grant transaction;
  • license lookup;
  • refund/remediation path on apply failure;
  • ledger audit snapshot.

Boundary: S2 never computes authoritative balances and never writes ledger rows.

S3 store

S2 provides:

  • market:catalog search contract;
  • listing.json presentation fields compatible with cargstore basics: id/name/summary/category/icon/featured-ish keywords/install_ref;
  • m2-market show/install --json as stable Store backend.

S3 provides:

  • UI rendering, deep links, and install button behavior.

S4 Scout

S2 provides:

  • tenant-filtered semantic search;
  • catalog fields needed in proposal toast: listing name, summary, price, rating, installs, evidence summary, install deep link;
  • telemetry sink market:evidence.

S4 provides:

  • on-box summarization, opt-in/rate limit enforcement, confidence threshold, notification UX.

S5 packaging/runtime-sync

S2 provides:

  • registry release tag and bundle hash contract;
  • solution.deployment fields;
  • permission manifest and applicability constraints.

S5 provides:

  • bundle builder;
  • signing if added;
  • recipe.yaml;
  • m2-core-sync/runtime-sync apply, verify, rollback.

S6 proposal engine

S2 provides:

  • catalog query and listing detail API/CLI;
  • job-priced listing discovery;
  • evidence and stats fields for build-vs-install proposals.

S6 provides:

  • proposal composition, cost comparison, job routing, and when to recommend install vs build.

Fedlearn

S2 integrates with fedlearn by:

  • preserving fedlearn-compatible manifest fields;
  • adding curation disposition commercialize;
  • reusing scored PR + veto semantics;
  • using runtime-sync as universal install path.

Fedlearn dispositions:

promote-to-core -> m2/m2-core, free commons
commercialize   -> m2/m2-market listings PR
reject/park     -> no catalog listing

5. Options compared

Registry repo: m2/m2-market vs separate m2/market-registry

  • m2/m2-market: already present locally, contains schemas, docs, and seed solution dirs; simplest MVP.
  • m2/market-registry: cleaner source of truth, smaller clone for desktops, cleaner audit and release history.

Recommendation: use m2/m2-market for MVP because it exists and the brief names repo layout m2/m2-market; keep paths compatible with a later split to m2/market-registry.

Catalog truth: Forgejo vs memory-api

  • Forgejo: versioned, reviewable, release assets, PR/veto, audit.
  • memory-api: semantic search and tenant-scoped retrieval, but rebuildable and exposed to split-brain risk.

Recommendation: Forgejo is truth; market:catalog is derived.

Publish flow: direct push vs PR/veto

  • Direct push is faster but bypasses tenant and evidence review.
  • PR/veto reuses fedlearn gates and gives commercial listings an audit trail.

Recommendation: all published listings enter through PR/veto; no direct publish to main.

Price v1: fixed only vs fixed + job

  • Fixed only is easiest to install and ledger-settle.
  • Fixed + job is locked by the brief and needed for operator-delivered services.

Recommendation: support both in schemas and catalog; only fixed is direct-installable in MVP.

6. Risks/edge cases

  • Tenant data leakage: enforce tenant_visibility, tenant_scope, owner initiation, double scrub, evidence summary-only indexing, and CI secret scans.
  • Registry/index divergence: registry wins; reindex from main is mandatory operational recovery.
  • Broken local registry route: release assets are fetched from Forgejo, not Docker push/pull.
  • Mixed desktop images: install path must go through runtime-sync/apply adapter, not baked images.
  • Paid but not applied: S2 records enough install ref and telemetry for S1/S5 refund and remediation, but S1/S5 own transaction compensation and rollback.
  • Self-purchase metric gaming: allow install but exclude from quality stats.
  • Job listing confusion: direct install must refuse job-priced listings until proposal/job flow is implemented.
  • Evidence bloat or raw data indexing: catalog text embeds summaries only; evidence files remain scrubbed in Forgejo.
  • Permission creep: every permission must include kind/access/scope/target/reason and render in PR plus show.
  • Stats churn in registry: batch stats updates to avoid noisy PRs; retain ability to rebuild stats from market:evidence.

7. [NEEDS CLARIFICATION]

  1. [NEEDS CLARIFICATION] Registry remote: should MVP use the existing m2/m2-market repo as the registry of record, or create/split m2/market-registry before the first paid install? S2 recommends existing m2/m2-market for MVP.
  2. [NEEDS CLARIFICATION] Veto authority and timeout: which operator/team receives listing PR notifications, and is low-risk auto-merge after 72h allowed for commercial listings or should every commercial listing require explicit approved?
  3. [NEEDS CLARIFICATION] Standard license text: what exact m2-market-standard-v1 terms should be stored behind license.terms before real paid installs?