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>
23 KiB
S6 — Proposal Engine & Evidence Loop SPEC
1. Scope & non-goals
MVP scope
S6 owns the pull-side proposal engine and pricing evidence loop:
- Hermes
market-proposeskill: turns a user intent into build-vs-install-vs-job proposals. - Cost-intelligence layer: combines similar past work from memory, real token/time spend from m2-gpt metering/cognition traces, herdr run durations, and
market:catalogsearch. - Evidence records: every serious job leaves a scrubbed, tenant-scoped record with wanted/tools/contributors/tokens/time/resources/failed/succeeded/reusable-verdict.
- Proposal citations: proposals must cite listing evidence, similar job evidence, and cost estimates separately.
- Harness-agnostic contract: Hermes is the primary surface; OpenAI-wire clients through m2-gpt can call the same tool/API later.
MVP deploys as:
- A Hermes skill installed by runtime-sync in desktop home volumes.
- A small
m2-market-proposerservice on Coolify network for deterministic retrieval, estimate aggregation, evidence writes, and proposal JSON. - A gateway-side optional middleware hook that records real usage and exposes it by correlation id; the proposal decision logic stays outside the gateway.
Later
- Automatic quote negotiation with human operators.
- Dynamic repricing of listings.
- Public web marketplace proposal widgets.
- Full resource-market routing optimizer.
- Crypto-capable ledger integration beyond internal credits.
Non-goals
- Ledger schema or payment settlement: S1.
- Catalog/listing truth, indexing, install flow, and M2 Store UX: S2/S3.
- Scout push notifications: S4.
- Packaging/build artifacts: S5.
- Raw client transcript ingestion. S6 consumes scrubbed summaries only.
2. User stories + acceptance criteria
US-1: Operator asks Hermes what path to take
Given an operator asks Hermes for an outcome such as "automate my eBay listings"
When Hermes invokes market-propose with the current intent and tenant identity
Then the response contains at least one of each applicable path:
install_adapt: existing Solution covers enough of the intent.operator_job: a Service/operator can deliver or adapt it.custom_build: no adequate inventory exists or client constraints require custom work.
Acceptance:
- Every price/time/token estimate has at least one citation or is marked
low_confidence. - Tenant visibility is enforced before any listing or evidence is returned.
- Client-derived evidence is never cited unless
owner_initiated=trueanddouble_scrubbed=true.
US-2: Proposal cites evidence without leaking data
Given similar past work exists in memory under tenant-private and shared partitions
When a proposal is generated for tenant gst
Then only tenant_id in ["gst", "__fleet__", "m2-core"] and allowed shared records are searched.
Acceptance:
- Cross-tenant records are impossible to retrieve through the API filter.
- Citations include stable IDs and short sanitized summaries, not raw transcript text.
- The API returns
evidence_redactions[]explaining omitted records by class, not identity.
US-3: Serious job leaves an evidence record
Given an install, operator-assisted job, or custom build runs longer than 10 minutes, spends more than 100k tokens, or creates a reusable artifact
When the job ends or is abandoned
Then m2-market-proposer writes a m2.market.evidence.v1 record to memory and attaches its summary to any relevant listing.
Acceptance:
- Records validate against the schema below.
- Failed jobs are recorded; they are usable for estimates but do not raise listing success stats.
- Reusable verdict is one of
create_solution,update_solution,not_reusable,unclear.
US-4: Evidence improves future estimates
Given at least three evidence records exist for a capability or listing
When a later proposal asks for similar work
Then estimates use p50/p80/p95 ranges instead of a single naive value.
Acceptance:
- Proposal JSON includes
estimate_basis.sample_count. - Outliers remain in evidence but are downweighted only by explicit rule.
- Install path estimates cite listing-level install telemetry before generic similar jobs.
3. Interfaces & data contracts
Existing contracts S6 depends on
memory-api: livehttps://memory.machinemachine.ai/healthreturns ok; search endpoint shape isPOST /memory/searchwithquery,agent_id,routing_strategy,limit,tenant_id, and optionalfilters.m2-gpt: livehttps://gpt.machinemachine.ai/healthreturns{"status":"healthy","gateway":"bifrost"}. It already has tenants/agents,tenant_budgets,route_budgets,spend_log, cognition traces, and usage passthrough from Bifrost.market:catalog: existing catalog contract stores published listing payloads under memory partitionagent_id="market:catalog".m2.solution.v1andm2.listing.v1: existing schemas already carry evidence, price, seller, tenant visibility, and stats.m2-ledger: S6 quotes credits but does not debit; install/job settlement remains the ledger contract.
Memory partitions
Use memory-api agent_id as partition:
market:catalog: published listing semantic index, owned by S2/S3.market:evidence: job and install evidence records, owned by S6.market:proposals: optional proposal audit summaries, owned by S6.
Tenant scope:
- Shared evidence uses
tenant_id="m2-core"ortenant_id="__fleet__". - Tenant-private evidence uses canonical tenant id such as
gst,nasr,parlobyg,peter. - Queries must pass
tenant_id=[caller_tenant, "m2-core", "__fleet__"]unless caller is an operator-admin.
Evidence record schema
Schema id: m2.market.evidence.v1.
{
"schema_version": "m2.market.evidence.v1",
"evidence_id": "ev_2026w27_01j2abc",
"job_id": "job_01j2abc",
"tenant_id": "m2-core",
"operator_id": "sdjs-operator",
"agent_ids": ["chris-m2o"],
"created_at": "2026-07-02T00:00:00Z",
"wanted": {
"intent": "Generate a branded competitor scan report",
"normalized_capabilities": ["research", "report-generation"],
"constraints": ["no raw client data in shared catalog"]
},
"tools": [
{"name": "Hermes", "kind": "agent"},
{"name": "m2-gpt", "kind": "llm_gateway"},
{"name": "memory-api", "kind": "memory"},
{"name": "herdr", "kind": "run_capture"}
],
"contributors": [
{"kind": "operator", "id": "sdjs-operator", "role": "builder"},
{"kind": "agent", "id": "chris-m2o", "role": "executor"}
],
"metering": {
"m2_gpt_correlation_ids": ["turn_01j2abc"],
"prompt_tokens": 120000,
"completion_tokens": 18000,
"total_tokens": 138000,
"cost_usd": 0.42,
"wall_time_seconds": 2400,
"active_time_seconds": 1500,
"herdr_run_ids": ["run_2026-07-02T00-00-00Z"]
},
"resources": [
{"kind": "desktop", "id": "chris-m2o", "seconds": 2400},
{"kind": "model", "id": "GLM-5.1", "tokens": 138000}
],
"outcome": {
"status": "succeeded",
"failed": [
{"step": "site scraping", "reason": "blocked by login", "recovered": true}
],
"succeeded": [
{"step": "report generation", "artifact_ref": "forgejo:m2/market-registry/..."}
]
},
"reusable_verdict": {
"decision": "create_solution",
"confidence": 0.82,
"reason": "Repeatable report workflow with scrubbed inputs"
},
"listing_refs": [
{"listing_id": "lst_competitor-scan", "relationship": "supports_estimate"}
],
"scrub_status": {
"secrets_redacted": true,
"pii_redacted": true,
"double_scrubbed": true,
"raw_sources_retained": false
},
"content_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
Required fields:
schema_version, evidence_id, job_id, tenant_id, created_at, wanted, tools, contributors, metering, resources, outcome, reusable_verdict, scrub_status, content_hash.
Memory store payload:
{
"content": "Wanted: branded competitor scan report. Outcome: succeeded. Reusable: create_solution. Time: 40m. Tokens: 138k.",
"agent_id": "market:evidence",
"tenant_id": "m2-core",
"metadata": {
"schema_version": "m2.market.evidence.v1",
"evidence_id": "ev_2026w27_01j2abc",
"job_id": "job_01j2abc",
"listing_ids": ["lst_competitor-scan"],
"capabilities": ["research", "report-generation"],
"status": "succeeded",
"reusable_decision": "create_solution",
"tokens_total": 138000,
"wall_time_seconds": 2400,
"content_hash": "sha256:..."
}
}
Proposal request schema
Schema id: m2.market.proposal-request.v1.
{
"schema_version": "m2.market.proposal-request.v1",
"request_id": "prq_01j2abc",
"tenant_id": "m2-core",
"operator_id": "m2bd",
"agent_id": "chris-m2o",
"harness": "hermes",
"intent": "I want to automate eBay listings",
"context_summary": "User has product photos and wants listing drafts.",
"constraints": {
"max_credits": 150,
"deadline": "2026-07-03",
"data_sensitivity": "tenant_private",
"allowed_inventory_types": ["solution", "service", "resource", "capability"]
},
"current_tools": ["browser", "filesystem", "m2-market"],
"correlation_id": "turn_01j2abc"
}
Proposal response schema
Schema id: m2.market.proposal.v1.
{
"schema_version": "m2.market.proposal.v1",
"proposal_id": "prop_01j2abc",
"request_id": "prq_01j2abc",
"tenant_id": "m2-core",
"generated_at": "2026-07-02T00:00:00Z",
"intent_summary": "Automate eBay listing creation from product inputs.",
"paths": [
{
"path_id": "path_install_adapt_1",
"kind": "install_adapt",
"title": "Install eBay Listing Workflow and adapt prompts",
"recommendation": "recommended",
"coverage": 0.72,
"credits": {"min": 40, "expected": 60, "max": 90, "currency": "m2cr"},
"time": {"p50_seconds": 7200, "p80_seconds": 14400},
"tokens": {"p50": 600000, "p80": 1200000},
"resources": [{"kind": "desktop", "quantity": 1}],
"next_action": {
"type": "install",
"listing_id": "lst_ebay-listing-workflow",
"deeplink": "m2store://listing/lst_ebay-listing-workflow"
},
"citations": [
{"kind": "listing", "id": "lst_ebay-listing-workflow", "claim": "catalog match"},
{"kind": "evidence", "id": "ev_2026w26_01x", "claim": "similar workflow completed in 2h"}
],
"risks": ["Needs credentialed browser session", "May require image style adaptation"],
"estimate_basis": {"sample_count": 4, "method": "listing_install_p80"}
}
],
"evidence_redactions": [
{"reason": "tenant_not_visible", "count": 3},
{"reason": "not_double_scrubbed", "count": 1}
],
"audit": {
"catalog_query_id": "memq_01j2abc",
"evidence_query_id": "memq_01j2abd",
"m2_gpt_correlation_id": "turn_01j2abc"
}
}
Path kinds:
install_adapt: buy/install Solution and adapt locally.operator_job: engage operator/service through job-based pricing.custom_build: build from scratch.resource_route: rent internal resource/capacity when it is the dominant cost.defer: no acceptable path under constraints.
HTTP API: m2-market-proposer
Service: m2-market-proposer, Coolify app on coolify network. Base internal URL: http://m2-market-proposer:8000.
Auth:
X-API-Key: service key injected at runtime.- Optional
Authorization: Bearer <m2-gpt tenant key>for direct harness calls after gateway identity forwarding lands.
GET /health
Response:
{"status":"healthy","memory":"ok","m2_gpt":"ok"}
POST /v1/proposals
Body: m2.market.proposal-request.v1.
Response: m2.market.proposal.v1.
Required behavior:
- Normalize intent into capability tags.
- Query
market:catalogwith tenant visibility. - Query
market:evidencewith tenant scope[caller, "m2-core", "__fleet__"]. - Fetch m2-gpt spend/time by
correlation_idwhere available. - Build ranked paths with citations.
- Store proposal audit summary in
market:proposalsunlessdry_run=true.
POST /v1/evidence
Body: m2.market.evidence.v1.
Response:
{"evidence_id":"ev_2026w27_01j2abc","stored":true,"attached_listing_ids":["lst_competitor-scan"]}
Behavior:
- Validate schema.
- Enforce scrub gate before shared write.
- Store semantic summary in memory partition
market:evidence. - Emit listing-attachment event for S2/S3 indexer.
GET /v1/evidence/{evidence_id}
Returns sanitized evidence record if caller tenant can see it; 404 for not found or forbidden to avoid existence leaks.
POST /v1/estimates
Body:
{
"tenant_id": "m2-core",
"intent": "build competitor scan report",
"listing_ids": ["lst_competitor-scan"],
"capabilities": ["research", "report-generation"],
"confidence_floor": 0.6
}
Response:
{
"sample_count": 5,
"tokens": {"p50": 300000, "p80": 800000, "p95": 2000000},
"time_seconds": {"p50": 1800, "p80": 7200, "p95": 172800},
"credits": {"p50": 30, "p80": 90, "p95": 300},
"method": "similar_evidence_weighted",
"citations": [{"kind":"evidence","id":"ev_..."}]
}
CLI verbs
Add to existing m2-market CLI surface:
m2-market propose "<intent>" --tenant <tenant> --operator <operator_id> --json
m2-market evidence capture --job-id <job_id> --from-herdr <run_id> --correlation-id <turn_id>
m2-market evidence submit evidence.json
m2-market evidence show <evidence_id> --json
Exit codes follow existing CLI contract: 0 ok, 1 usage, 2 not found, 5 validation/firewall rejection.
Hermes skill: market-propose
Install path:
- Primus:
/home/developer/.hermes/skills/market-propose/ - Agent-latest/OpenClaw-compatible fallback assets:
/home/developer/.m2-core/skills/market-propose/ - Distributed by runtime-sync/m2-core-sync only; no baked secrets.
Skill files:
market-propose/
SKILL.md
skill.json
scripts/propose.py
scripts/evidence_capture.py
skill.json:
{
"name": "market-propose",
"version": "0.1.0",
"runtime": "python3",
"commands": {
"propose": "python3 scripts/propose.py",
"evidence_capture": "python3 scripts/evidence_capture.py"
},
"env": [
"M2_MARKET_PROPOSER_URL",
"M2_MARKET_API_KEY",
"M2_GPT_CORRELATION_ID",
"M2_TENANT_ID",
"M2_OPERATOR_ID"
]
}
Hermes behavior:
- Invoke
proposewhen the user asks for cost, build/install choice, "is there a package", "can someone do this", or "estimate this job". - Render concise human prose, but preserve machine-readable proposal JSON in an attached block or local state file.
- Do not install or spend credits without explicit user approval.
m2-gpt metering contract
S6 consumes existing gateway data instead of duplicating metering:
spend_log: tenant, route/model, recorded time, prompt/completion/total tokens where present, cost.- cognition trace/audit v2:
tenant_id,agent_id,turn_id/correlation id,model,tokens,tool,status, latency/time. - Bifrost OpenAI response
usage: passthrough source for token counts.
Add read-only internal endpoint in m2-gpt:
GET /internal/v1/metering/turn/{correlation_id}
Auth: service key, internal network only.
Response:
{
"correlation_id": "turn_01j2abc",
"tenant_id": "m2-core",
"agent_id": "chris-m2o",
"started_at": "2026-07-02T00:00:00Z",
"ended_at": "2026-07-02T00:12:00Z",
"model_calls": [
{
"route_id": "spark-glm",
"model": "GLM-5.1",
"prompt_tokens": 120000,
"completion_tokens": 18000,
"total_tokens": 138000,
"cost_usd": 0.42,
"status": "ok"
}
],
"totals": {
"prompt_tokens": 120000,
"completion_tokens": 18000,
"total_tokens": 138000,
"cost_usd": 0.42,
"wall_time_seconds": 720
}
}
This endpoint is a gateway concern but S6 defines it as the minimum metering read needed for evidence capture. If not available in MVP, evidence_capture records m2_gpt_correlation_ids and tokens_unknown=true.
Events
S6 emits JSONL-compatible events to stdout and optional webhook for S2/S3:
{
"event_type": "market.evidence.created",
"event_version": "v1",
"ts": "2026-07-02T00:00:00Z",
"evidence_id": "ev_2026w27_01j2abc",
"tenant_id": "m2-core",
"listing_ids": ["lst_competitor-scan"],
"reusable_decision": "create_solution"
}
Other events:
market.proposal.generatedmarket.proposal.acceptedmarket.proposal.dismissedmarket.evidence.attached_to_listing
Storage and attachment
Authoritative evidence storage:
- Memory semantic copy:
agent_id="market:evidence"in memory-api. - Audit copy: JSONL at proposer service volume
/var/lib/m2-market-proposer/evidence/YYYY-Www.jsonl. - Listing attachment: S6 does not mutate listing truth directly. It emits
market.evidence.attached_to_listing; S2/S3 indexer updateslisting.evidence_summary, listing stats, and optional registry audit PR/commit.
Proposal audits:
- Stored as summaries under
agent_id="market:proposals"withtenant_idscoped to caller. - TTL/default retention: 90 days for tenant-private, indefinite for shared scrubbed proposal statistics.
4. Integration contract with other subsystems
S1 ledger
Inputs from S1:
- Current price for install/job/resource.
- Job escrow/quote status when path kind is
operator_job. - Platform cut defaults only for display; S6 does not settle.
Outputs to S1:
- Approved path metadata may become ledger
reason=route|install|jobref. - Proposal id should be included in ledger refs where possible:
prop_...:path_....
S2 catalog
Inputs from S2:
market:catalogsearchable listing payloads.- Listing stats: installs, rating, proposals_shown, proposals_accepted.
- Tenant visibility policy.
Outputs to S2:
market.proposal.generated/accepted/dismissedevents.- Evidence attachment events by listing id.
- Suggested new capability tags from proposal normalization.
S3 store/install
Inputs from S3:
- M2 Store deep links:
m2store://listing/<listing_id>. - Install state and permission diff when available.
Outputs to S3:
- Proposal path next actions with listing ids.
- Evidence of install success/failure for listing stats.
S4 Scout
Shared contracts:
- S4 can call
POST /v1/proposalswithharness="scout"and a session summary. - S4 owns push timing/UI; S6 owns proposal construction and estimate basis.
- Proposal/dismissal telemetry uses the same events.
S5 packaging
Inputs from S5:
- Build/package evidence created during solution packaging.
- Manifest ids, release refs, tool requirements.
Outputs to S5:
- Reusable verdicts:
create_solutionandupdate_solution. - Evidence citations that should be included in a future
solution.evidence[].
m2-gpt
Touchpoints:
- Tenant/agent identity from bearer key.
- m2-gpt
spend_logand cognition traces bycorrelation_id. - Optional gateway middleware/tool-dispatch registration so all OpenAI-wire harnesses can call proposal tool.
No S6 secrets are baked into images. M2_MARKET_API_KEY and M2_GPT_API_KEY are runtime-injected.
memory-api
Touchpoints:
POST /memory/searchwithagent_id="market:catalog"andagent_id="market:evidence".- Store via existing memory write path once auth is enforced.
- Tenant/principal filters must be passed on every query.
m2o/herdr
Touchpoints:
herdrrun summaries supply active time, failed/succeeded steps, tools invoked.- Desktop identity from fleet/herdr config supplies
agent_id. - Runtime-sync installs the Hermes skill across primus and agent-latest without image rebuild.
5. Options compared
Option A: Hermes skill only
Pros:
- Fastest MVP.
- Fits primary agent surface.
- Can render proposals conversationally.
Cons:
- Duplicates retrieval/estimate logic if OpenClaw or Store needs it.
- Harder to centralize evidence writes and audit.
Option B: m2-gpt middleware/tool only
Pros:
- Harness-agnostic by default for anything OpenAI-wire.
- Already sees identity, tokens, model usage, and budgets.
Cons:
- Bloats latency-sensitive gateway.
- Proposal logic depends on catalog and ledger services, which should not be in the critical chat-completion path.
- Harder to evolve independently.
Option C: Proposer service + Hermes skill adapter (recommended)
Pros:
- Keeps deterministic business logic and evidence writes centralized.
- Hermes gets a thin local skill; OpenClaw/Store/Scout can call the same API.
- m2-gpt only provides identity/metering and optional tool exposure.
- Can be canary-deployed as a Coolify app and rolled back independently.
Cons:
- One new small service to operate.
- Requires service keys and network ACLs.
Recommendation: Option C for MVP. Add gateway middleware only for two narrow functions: expose market.propose as an OpenAI tool later, and provide read-only metering by correlation id.
Cost estimate method
Compared:
- Static price table: simple but ignores real work history.
- LLM-estimated effort: flexible but unverifiable.
- Evidence-weighted estimates: uses real prior tokens/time/outcomes and listing telemetry.
Recommendation: evidence-weighted estimates. Fall back to static seed values only when sample_count < 2, and mark as low_confidence.
6. Risks/edge cases
- Tenant leakage: all memory searches must include tenant filters; forbidden evidence returns
404, not403. - Client-derived commercialization: shared evidence requires
owner_initiated=trueanddouble_scrubbed=true; otherwise tenant-private only. - Secrets in evidence: evidence capture uses the fedlearn scrubber policy and fails closed on high-entropy strings.
- Bad estimates from tiny samples: responses must expose
sample_count,method, and confidence. - Failed jobs hidden from pricing: failed evidence is required because it improves estimates; proposals must distinguish success evidence from failure evidence.
- Gateway fragility: do not put catalog search inside the hot chat-completion path; proposer service is called explicitly by skill/tool.
- Abuse/spam proposals: proposal audits are rate-limited per operator and do not auto-spend credits.
- Split-brain memory-api: indexer/proposer must use the live endpoint resolver until fedlearn T13 is complete; evidence JSONL audit copy allows replay.
- Clock/correlation mismatch: if m2-gpt turn id is missing, evidence can still record herdr run ids and
tokens_unknown=true; later backfill may attach metering. - Cross-harness UX drift: Hermes prose is non-authoritative; proposal JSON is the contract.
7. [NEEDS CLARIFICATION]
- What exact m2-gpt correlation id is guaranteed to be available inside Hermes skill execution:
turn_id,correlation_id, or a newM2_GPT_CORRELATION_IDenv var? - Who is allowed to mark tenant-private evidence as
owner_initiated=trueanddouble_scrubbed=true: tenant admin, platform operator, or both? - What is the first static fallback conversion from USD/token cost to M2 credits for estimates before ledger/resource pricing data is available?