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>
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
m2as system of record:- registry repo:
git.machinemachine.ai/m2/m2-marketunless split later intom2/market-registry; - releases carry immutable Solution bundles;
- listing PRs ride the fedlearn scored PR + human veto pipeline and add the
commercializedisposition.
- registry repo:
- Define
solution.schema.jsonas a commercial superset of fedlearnm2-core-manifest.schema.jsonand the fedlearn candidate fields already assumed by m2-market. - Define
listing.schema.jsonas the catalog-facing projection used by cargstore/M2 Store, Scout, Hermes, and CLI. - Define
market:catalogin 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 onlysolutionis installable in MVP.servicemay 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 runm2-market publish <bundle-dir>, then the CLI validates schemas, computes the bundle hash, creates a Forgejo branchlisting/<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_hashmatches 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
approvedlabel, when it merges, thenlisting.statusbecomespublishedand the indexer upserts it tomarket:catalog. - Given a PR with label
vetoor/veto <reason>, when the watcher processes it, then the PR is closed without indexing and the listing remainsdraftorrejected.
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 tenantnasrsearches, then the listing must not be returned by CLI, Store, Scout, or Hermes. - Given
market:catalogis deleted or stale, whenm2-market-indexer reindexruns, then the catalog is rebuilt from Forgejomainwithout 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>, verifiescontent_hash, and hands the commercial charge request to S1 before calling the S5 apply adapter. - Given a job-priced listing, when
installis attempted, then the CLI must refuse direct install with exit code1and 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 asm2-market publish. - Given a fedlearn candidate is tenant-derived, when
commercializeis selected withoutowner_initiated: trueandscrub_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, notcommercialize.
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_versionapplicability.image_classes[]applicability.roles[]applicability.tool_requirements[]tenant_scopeevidence[]content_hashscrub_statuswhen 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:
fixedis directly installable.jobis discoverable and showable, but install starts a proposal/job route rather than a direct apply.currencyis alwaysm2crin MVP.amountis 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_refidentifiers 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:listingdisposition:commercializerisk:low|medium|highinventory:solution|capability|resource|serviceveto-until:<iso8601>tenant:<tenant-id|public>
Merge gates:
- schema validation green;
- content hash check green;
- secret scan green;
- tenant firewall check green;
- no
vetolabel; approvedlabel 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.namelisting.summarylisting.categorylisting.keywordssolution.intentsolution.descriptionsolution.behaviorsummaries onlylisting.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:
publishedrecords are upserted.delistedrecords are deleted from broad catalog index but retained in registry.draft,in_review, andrejectedare 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
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:
- Resolve listing and verify
status=published. - Verify tenant visibility and applicability.
- Display price, license, and permission diff unless
--yes. - Ask S1 ledger to debit buyer and create grant.
- Fetch release asset named by
install_ref. - Verify
solution.content_hash. - Call S5 apply adapter (
m2-core-syncwhen landed; local adapter only for pre-rails wedge). - 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-op1: usage or unsupported flow such as direct install of job listing2: listing not found3: insufficient funds from ledger4: apply failed after charge; refund/remediation delegated to S1/S5 flow5: validation, tenant firewall, or permission rejection6: 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
primusandagent-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:catalogsearch contract;listing.jsonpresentation fields compatible with cargstore basics:id/name/summary/category/icon/featured-ish keywords/install_ref;m2-market show/install --jsonas 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.deploymentfields;- 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]
- [NEEDS CLARIFICATION] Registry remote: should MVP use the existing
m2/m2-marketrepo as the registry of record, or create/splitm2/market-registrybefore the first paid install? S2 recommends existingm2/m2-marketfor MVP. - [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? - [NEEDS CLARIFICATION] Standard license text: what exact
m2-market-standard-v1terms should be stored behindlicense.termsbefore real paid installs?