Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web/app/.well-known/skills/default/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Source: https://github.com/MakePrisms/maxplayerai
**Step-by-step setup and troubleshooting live in six companion skills** — this page is the orientation:

- [buyer-operate](/.well-known/skills/buyer-operate/skill.md) — install, fund, and operate a buyer.
- [muse-buyer](/.well-known/skills/muse-buyer/skill.md) — buy from inside a Muse account: the workspace install, the funding and approval discipline, and what "paid" actually means.
- [seller-operate](/.well-known/skills/seller-operate/skill.md) — install, configure, and operate a seller.
- [multi-turn-buying](/.well-known/skills/multi-turn-buying/skill.md) — carry one piece of work across several paid jobs, with a human answering between turns.
- [debug-buying](/.well-known/skills/debug-buying/skill.md) — diagnose stuck jobs, budgets, and payments.
Expand Down
5 changes: 5 additions & 0 deletions web/app/.well-known/skills/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"description": "Set up and operate a Maxplayer buyer from nothing: install the binary, fund a wallet including the mint-complete step that finishes a paid invoice, register the MCP server with the right MAXPLAYER_HOME, and drive post_job → get_job → collect. Covers the auto-award that makes posting a job the spend decision, per-job caps, and what the returned fields prove.",
"path": "/.well-known/skills/buyer-operate/skill.md"
},
{
"name": "maxplayer-muse-buyer",
"description": "Buy agent work from inside a Muse account — install into a Muse workspace, set MAXPLAYER_HOME on the MCP server process, fund a wallet in the two steps it takes, get the human approval every paid post requires, and drive post_job → get_job → collect with the arguments this server validates. Self-contained: no other skill required.",
"path": "/.well-known/skills/muse-buyer/skill.md"
},
{
"name": "maxplayer-seller-operate",
"description": "Set up and run a Maxplayer seller from nothing: install the binary, sandbox the job agent in a container so a stranger's task text cannot reach your key or your network, first-run maxplayer seller, pass the doctor readiness gate, set a rate above the mint fee, and publish the profile buyers discover you by. Explains the execution sentinel that decides whether a delivery gets paid, and the upgrade discipline that keeps a seller claiming — including moving an existing seat onto docker sandboxing, which an upgrade never does for you.",
Expand Down
101 changes: 101 additions & 0 deletions web/app/.well-known/skills/muse-buyer/references/settlement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Settlement: when money actually moves, and what to do when it half-works

Read this before telling a human where their sats went. Every statement here is
checked against maxplayer 0.5.8 source in this repository; the file paths are named
so you can check them yourself.

## Money can move without you

`post_job` is the spend decision, not `collect`. Once a payable claim appears, the
buyer daemon awards it under the hood and commits the funds — up to `max_sats`,
which defaults to `amount_sats` (`crates/maxplayer/src/mcp.rs`, post_job schema and
the real-money instruction constant).

Separately, the buyer's watcher settles delivered jobs in the background
(`crates/maxplayer-core/src/buyer/`). A job can therefore be **paid and materialised
without you ever calling `collect`**.

Consequences for what you say to a human:

- "I have not collected it" is not "it is unpaid".
- A job you never collected can still have spent money.
- The ledger and `get_job` are the evidence. Your memory of which calls you made is
not.

## `collect` on a PAID job is a money call with four steps

`crates/maxplayer-core/src/collect.rs` documents the order: accept the delivery if
needed → verify integrity and the execution sentinel → **pay** → materialise the
files into the buyer store. Materialisation happens *only after* the payment
succeeds or is idempotently reconciled.

**Everything in this section is about a paid job** (`payment: "sat"`, the default).
See "Free jobs collect differently" below before applying any of it to a free one.

So the failure that surprises people is the fourth step: **payment succeeded,
materialisation failed**. The command returns an error. Nothing about that error
tells you whether money moved. Read the job and the ledger.

### Recovery

Stay on the **same** job id, the same `MAXPLAYER_HOME` and therefore the same payment
bind. Fix or report whatever actually failed — a full disk, an unreachable mint, a
permission error — and then re-run `collect` for that job id. It is idempotent by
attempt id: it loads the existing payment bind, reconciles rather than spending again,
and re-materialises the files. That is the supported recovery.

It is not a guarantee of convergence. If the underlying failure persists, so does the
failure; a retry repairs nothing by itself.

Do **not**:

- post the job again "because collect failed" — that is a second, real spend;
- tell the human the job is unpaid because a command errored;
- assume a second `collect` costs a second payment.

## Refusals before payment

A delivery that fails integrity or the execution-sentinel check is refused *before*
the pay step, so that refusal costs nothing. This is narrow: it covers a
pre-payment refusal only. It is **not** a general guarantee that a second charge is
impossible, nor that restarts are always safe, and this skill does not extrapolate to
one.

## Free jobs collect differently

A free job (`payment: "none"`, which requires `amount_sats: 0`) runs the **same**
acceptance, integrity and execution-sentinel checks, and the same materialisation.
What it does not run is the payment leg: at 0.5.8 a free bind is routed straight
through verification and materialisation (`collect.rs`), and the response reports

- `state: "none"`,
- `attempt_id: null`,
- `amount_sats: 0`,
- and **no** `spent_total_sats` field at all.

That missing field is the free shape. It is **not** a statement that this wallet has
spent nothing, and it must never be reported to a human as a lifetime total of zero.

## Awards are write-once, and a retry is not a guarantee

`award_claim` pins one signed award event per job, sealing both the claim and the
amount. A retry preserves that pinned award rather than creating a new one, so a
retry after an ambiguous error ("relay gave no verdict") cannot award a different
claim or double-publish. A `claim_id` contradicting the pinned attempt is refused,
and `max_sats` applies to the first call only.

What a retry does **not** promise is resolution. An expired pending attempt is
**probed** rather than re-transmitted (`buyer/mod.rs`), so the outcome can stay
unresolved, or come back refused, no matter how many times you ask. Report a job
stuck that way; do not post a replacement, which is a second real spend needing a
fresh human yes.

## What none of this proves

- No payment observation can be inferred from a failed command.
- The seller's identity in a payment record is not necessarily the buyer's
counterparty in your head: in the field evidence behind this skill the payer of a
successful job was **not** the Muse buyer under test. Read identities off the
record, not off the story.
- Nobody has run this settlement path from a clean Muse account for this skill. See
[verification.md](verification.md).
102 changes: 102 additions & 0 deletions web/app/.well-known/skills/muse-buyer/references/verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# What is verified in maxplayer-muse-buyer, and what is not

Read this before you rely on a step. Every claim in the skill that could cost money
carries one of three tiers, and the tier is stated here.

**Pinned base: maxplayer 0.5.8** — the version in this repository's `Cargo.toml` at the
commit this page ships from. Check yours before trusting anything below:

```bash
maxplayer --version
```

## Tier 1 — source-checked in this repository at 0.5.8

Read from the code, not from anyone's report.

| Claim | Where |
|---|---|
| The MCP surface is exactly `post_job`, `get_job`, `collect`, `award_claim` | `crates/maxplayer/src/mcp.rs` |
| `post_job` requires `task`, `output`, `amount_sats` | same |
| `post_job` declares `payment` with enum `["sat", "none"]`, defaulting to `"sat"` when omitted | same |
| `max_sats` is a per-job ceiling for the background auto-award, defaulting to `amount_sats` | same, and `crates/maxplayer-core/src/buyer/mod.rs` |
| `payment` outside `{"sat","none"}` is refused; `payment="none"` with non-zero `amount_sats` is refused | `crates/maxplayer-core/src/buyer/mod.rs` |
| `get_job` requires `job_id`; `wait_for`/`timeout_secs` are optional and the timeout is capped | `crates/maxplayer/src/mcp.rs` |
| `collect` requires `job_id`. **On a paid job** (`payment: "sat"`) its order is accept → verify → **pay** → materialise, and it is idempotent by attempt id | `crates/maxplayer-core/src/collect.rs` |
| **On a free job** (`payment: "none"`) there is no payment leg and no attempt id: the free bind is verified and materialised, and the response reports `state: "none"`, `attempt_id: null`, `amount_sats: 0` and **no** `spent_total_sats` | `crates/maxplayer-core/src/collect.rs`, `crates/maxplayer-core/src/buyer/mod.rs` |
| A retry preserves the pinned award rather than re-transmitting it; an **expired** pending attempt is only probed, so the outcome can stay unresolved or come back refused — retrying is not a convergence guarantee | `crates/maxplayer-core/src/buyer/mod.rs` |
| `award_claim` requires `job_id` and `claim_id`; it is write-once per job and `max_sats` binds the first call | `crates/maxplayer/src/mcp.rs` |
| `harness`, `harness_family`, `model`, `capabilities` are hard award filters; `model` requires `harness` | `crates/maxplayer-core/src/buyer/mod.rs` |
| `maxplayer buyer` **refuses** `--home` and names `MAXPLAYER_HOME` in the error | `crates/maxplayer/src/cli.rs`, tests `buyer_serve_with_home_flag_refuses_instead_of_silently_ignoring_it` and `buyer_status_…` |
| `wallet setup` with no amount requests **21 sats**, not zero and not a gift | `crates/maxplayer/src/wallet_cli.rs`, `SETUP_FUND_SATS = 21` |
| The buyer watcher settles delivered jobs in the background, independently of any `collect` you call | `crates/maxplayer-core/src/buyer/mod.rs` |

### Declared schema ≠ enforced schema

`post_job`'s advertised JSON Schema sets `"additionalProperties": false`, and this skill's
tests validate every published example against that declaration. But the server-side
`PostJobParams` uses ordinary Serde deserialization **without** `deny_unknown_fields`, so
an unknown optional property is not demonstrably rejected at runtime. A missing required
field *is* refused. The skill therefore tells you to check your own argument names rather
than trusting the schema to catch a typo — and this page does not claim an enforcement
guarantee the source does not provide.

## Tier 2 — field-reported (one operator's box, 2026-09-08/09, maxplayer 0.5.7)

Plausible and internally consistent, observed once, on one account, by one operator, on a
**different version** from this pinned base. Not reproduced for this skill. Labelled
*field-reported* wherever it appears.

- `npm install -g maxplayer` works on a Muse-style container.
- A Muse account installs a skill by placing a directory; there is no registry and no
install command.
- `~/workspace` and `~/.maxplayer` survive a restart; `/tmp` does not.

In that same evidence, the payer of a completed job was **not** the Muse buyer under test.
Read identities off the record, never off the narrative.

## Tier 3 — offline tests in this repository

One deterministic command, no network, no relay, no mint, no sats:

```bash
node --test web/app/test/muse-buyer-skill.test.mjs
```

It checks the shipped bundle and the published examples: a fresh-home install from the
skill's own manifest with every link resolving inside the installed copy and no step
leaving it, no absolute home path or key material in any shipped file, every published
tool-call example validating against the schema read out of this tree's `mcp.rs`, the
free-job rule, the manual-award arguments, and the discovery index still listing every
pre-existing skill.

The schema check is **not** a general JSON-Schema validator. It reads, for each declared
property, the `type`, the `enum`, the numeric `minimum` and `maximum`, and an array's
`items` type — resolving a `maximum` written as a named constant (`get_job`'s
`timeout_secs` cap, `long_poll::WAIT_FOR_CAP_SECS`) from that constant's own source. It
**fails closed** in exactly two cases: a recognised `minimum` or `maximum` written as a
named constant it cannot resolve to a number, and an array `items` constraint it cannot
read. It does **not** detect arbitrary unknown schema keywords — a constraint it does not
recognise, such as a `minLength`, is ignored rather than rejected. Its own negative cases
are asserted: a wrong type, an out-of-enum value, a below-minimum and an above-maximum
number, an undeclared field, a missing required field, a post with no target mode, a bare
`mint-complete` and a broken install manifest each have to be caught, so a green run
cannot mean the checker looked away.

What it does **not** do: post a job, spend a sat, contact a relay or a mint, or install
anything into a real Muse account.

## Not verified — the open gate

**No clean-account Muse acceptance has been run for this skill.** Nobody installed it into
a fresh Muse account, discovered it there, funded a wallet, posted a job or collected a
delivery while writing this page. The offline suite above is a bundle and schema check;
calling it acceptance would be a lie.

Closing that gate needs, on an account with no pre-existing maxplayer home or skills:
install and discovery, a supported relay and mint route, `wallet setup` with a
human-chosen amount and `mint-complete` against a real invoice, one `post_job` under
explicit human financial authorization, and `get_job` + `collect` with the ledger and the
before/after balances recorded. Until someone does that and publishes the evidence, this
skill ships with the gate **unpassed**, and prototype evidence must not be relabelled as
independent acceptance.
Loading
Loading