m2-market/specs/S6-proposal-engine-evidence.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

668 lines
23 KiB
Markdown

# 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-propose` skill: 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:catalog` search.
- 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:
1. A Hermes skill installed by runtime-sync in desktop home volumes.
2. A small `m2-market-proposer` service on Coolify network for deterministic retrieval, estimate aggregation, evidence writes, and proposal JSON.
3. 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=true` and `double_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`: live `https://memory.machinemachine.ai/health` returns ok; search endpoint shape is `POST /memory/search` with `query`, `agent_id`, `routing_strategy`, `limit`, `tenant_id`, and optional `filters`.
- `m2-gpt`: live `https://gpt.machinemachine.ai/health` returns `{"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 partition `agent_id="market:catalog"`.
- `m2.solution.v1` and `m2.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"` or `tenant_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`.
```json
{
"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:
```json
{
"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`.
```json
{
"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`.
```json
{
"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:
```json
{"status":"healthy","memory":"ok","m2_gpt":"ok"}
```
#### `POST /v1/proposals`
Body: `m2.market.proposal-request.v1`.
Response: `m2.market.proposal.v1`.
Required behavior:
1. Normalize intent into capability tags.
2. Query `market:catalog` with tenant visibility.
3. Query `market:evidence` with tenant scope `[caller, "m2-core", "__fleet__"]`.
4. Fetch m2-gpt spend/time by `correlation_id` where available.
5. Build ranked paths with citations.
6. Store proposal audit summary in `market:proposals` unless `dry_run=true`.
#### `POST /v1/evidence`
Body: `m2.market.evidence.v1`.
Response:
```json
{"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:
```json
{
"tenant_id": "m2-core",
"intent": "build competitor scan report",
"listing_ids": ["lst_competitor-scan"],
"capabilities": ["research", "report-generation"],
"confidence_floor": 0.6
}
```
Response:
```json
{
"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:
```bash
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:
```text
market-propose/
SKILL.md
skill.json
scripts/propose.py
scripts/evidence_capture.py
```
`skill.json`:
```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 `propose` when 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:
```json
{
"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:
```json
{
"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.generated`
- `market.proposal.accepted`
- `market.proposal.dismissed`
- `market.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 updates `listing.evidence_summary`, listing stats, and optional registry audit PR/commit.
Proposal audits:
- Stored as summaries under `agent_id="market:proposals"` with `tenant_id` scoped 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|job` ref.
- Proposal id should be included in ledger refs where possible: `prop_...:path_...`.
### S2 catalog
Inputs from S2:
- `market:catalog` searchable listing payloads.
- Listing stats: installs, rating, proposals_shown, proposals_accepted.
- Tenant visibility policy.
Outputs to S2:
- `market.proposal.generated/accepted/dismissed` events.
- 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/proposals` with `harness="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_solution` and `update_solution`.
- Evidence citations that should be included in a future `solution.evidence[]`.
### m2-gpt
Touchpoints:
- Tenant/agent identity from bearer key.
- m2-gpt `spend_log` and cognition traces by `correlation_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/search` with `agent_id="market:catalog"` and `agent_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:
- `herdr` run 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`, not `403`.
- Client-derived commercialization: shared evidence requires `owner_initiated=true` and `double_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]
1. What exact m2-gpt correlation id is guaranteed to be available inside Hermes skill execution: `turn_id`, `correlation_id`, or a new `M2_GPT_CORRELATION_ID` env var?
2. Who is allowed to mark tenant-private evidence as `owner_initiated=true` and `double_scrubbed=true`: tenant admin, platform operator, or both?
3. What is the first static fallback conversion from USD/token cost to M2 credits for estimates before ledger/resource pricing data is available?