From a476e8e4120251c022cf359a2e6a9303ba41556d Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 13 Sep 2026 09:31:04 +0000 Subject: [PATCH 1/2] Update public agent-rate guidance to $400 per hour per agent --- README.md | 10 +++++----- plugins/billing/README.md | 6 +++--- plugins/billing/commands/rate.md | 4 ++-- src/billing.mjs | 2 +- src/cli-schema.mjs | 4 ++-- src/commands.mjs | 2 +- src/rates.mjs | 20 ++++++++++---------- src/timer.mjs | 2 +- src/tools.mjs | 2 +- test/rates.test.mjs | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 441918cf..930134c7 100644 --- a/README.md +++ b/README.md @@ -1299,7 +1299,7 @@ each useful on its own — the timer needs no client, the rate needs no gateway > not exist. `billing import` is what closes that gap, which is why it comes > first. > -> The standalone billing carries the same rate model (`$100/hour/agent/upto:4`) +> The standalone billing carries the same rate model (`$400/hour/agent/upto:4`) > and bills **agent-hours**. ```sh @@ -1325,14 +1325,14 @@ fixed field list — `--billing.po` works because it says what it means. `/business`, `/merchant` and `/customer` are the same command. ```sh -moshcode rate set default $100/hour/agent/upto:4 +moshcode rate set default '$400/hour/agent' moshcode rate set acme-inc 0.5 SOL/day --prefer SOL,USDC --accept fiat moshcode rate set initech $5000/project ``` -`$100/hour/agent/upto:4` is the sentence from the contract, parsed: price, -period, unit, and the cap that made the client sign. Four agents cost four -hundred an hour and **so do six**. Order after the price does not matter. +`$400/hour/agent/upto:4` is the sentence from the contract, parsed: price, +period, unit, and the cap that made the client sign. Four agents cost $1,600 +an hour and **so do six** when the contract explicitly caps billing at four. Order after the price does not matter. ```sh moshcode billing acme-inc # a preview — writes nothing diff --git a/plugins/billing/README.md b/plugins/billing/README.md index be0afa86..9c390eaa 100644 --- a/plugins/billing/README.md +++ b/plugins/billing/README.md @@ -5,7 +5,7 @@ Slash commands for rates and invoices, built on the hours `@profullstack/timer` tracked. ``` -/billing:rate set acme '$100/hour/agent/upto:4' +/billing:rate set acme '$400/hour/agent/upto:4' /billing:hours --client acme --month /billing:invoice --client acme --from-timer --month /billing:report @@ -25,8 +25,8 @@ moshcode install billing ## What it is for -A rate is the sentence from the contract, parsed. `$100/hour/agent/upto:4` means -four agents cost four hundred an hour and so do six, and the invoice bills +A rate is the sentence from the contract, parsed. `$400/hour/agent/upto:4` means +four agents cost sixteen hundred an hour and so do six, and the invoice bills **agent-hours** so the client can check the line by hand: `quantity × rate` always equals `amount`. diff --git a/plugins/billing/commands/rate.md b/plugins/billing/commands/rate.md index 6383cc8d..8937a549 100644 --- a/plugins/billing/commands/rate.md +++ b/plugins/billing/commands/rate.md @@ -1,6 +1,6 @@ --- description: Read or set what your time costs, written the way the contract says it. -argument-hint: "[set '$100/hour/agent/upto:4']" +argument-hint: "[set '$400/hour/agent/upto:4']" allowed-tools: Bash(billing rate:*), Bash(billing client:*) --- @@ -21,7 +21,7 @@ A price, then any of these in any order: - a unit that gets multiplied: `agent`, `seat`, `person`, `team` - `upto:N` to cap the multiplier, `min:N` for a minimum billed period -`$100/hour/agent/upto:4` means four agents cost four hundred an hour, and so do +`$400/hour/agent/upto:4` means four agents cost sixteen hundred an hour, and so do six. `0.5 SOL/day`, `250 USDC/task` and `$5000/project` all parse too. ## Rules diff --git a/src/billing.mjs b/src/billing.mjs index d656cc77..299c8193 100644 --- a/src/billing.mjs +++ b/src/billing.mjs @@ -191,7 +191,7 @@ function draftInvoice(argv, write, run) { return 0; } if (!invoice.rate) { - write(err(`no rate for ${bone(found.id)} — ${acid(`/rate set ${found.id} $100/hour/agent/upto:4`)}`)); + write(err(`no rate for ${bone(found.id)} — ${acid(`/rate set ${found.id} $400/hour/agent/upto:4`)}`)); return 1; } diff --git a/src/cli-schema.mjs b/src/cli-schema.mjs index a61d04c9..655a718b 100644 --- a/src/cli-schema.mjs +++ b/src/cli-schema.mjs @@ -812,7 +812,7 @@ export const CORE_CLI_COMMANDS = [ group: "business", description: "what an hour of agent time costs", synopsis: [ - ["moshcode rate set ", "$100/hour/agent/upto:4"], + ["moshcode rate set ", "$400/hour/agent/upto:4"], ["moshcode rate [list] | show | rm ", ""], ], verbs: "RATE_VERBS", @@ -822,7 +822,7 @@ export const CORE_CLI_COMMANDS = [ ["--json", "machine-readable", ""], ], examples: [ - ["moshcode rate set default $100/hour/agent/upto:4", "four agents cost four hundred; six also cost four hundred"], + ["moshcode rate set default '$400/hour/agent'", "four agents cost $1,600/hour; six cost $2,400/hour"], ["moshcode rate set acme 0.5 SOL/day --prefer SOL,USDC --accept fiat", ""], ["moshcode rate set acme $5000/project", "a flat fee, added once per invoice"], ], diff --git a/src/commands.mjs b/src/commands.mjs index 456bd641..655a1226 100644 --- a/src/commands.mjs +++ b/src/commands.mjs @@ -877,7 +877,7 @@ const COMMANDS = [ cliVerb("timer", "track time (timer(\"on\", client) … timer(\"off\"))"), cliVerb("client", "clients/businesses/merchants — create, list, set, payee"), cliVerb("team", "who may do what (create, add, grant, revoke, can)"), - cliVerb("rate", "what agent time costs (rate(\"set\", \"acme\", \"$100/hour/agent/upto:4\"))"), + cliVerb("rate", "what agent time costs (rate(\"set\", \"acme\", \"$400/hour/agent/upto:4\"))"), cliVerb("billing", "turn tracked time into an invoice (--mark claims it, --send hands it over)"), cliVerb("payments", "the rail invoices go out on (connect, default, disconnect)"), cliVerb("plugin", "install/manage moshcode plugins from the marketplace"), diff --git a/src/rates.mjs b/src/rates.mjs index fbf2dc3b..c22d1a27 100644 --- a/src/rates.mjs +++ b/src/rates.mjs @@ -1,6 +1,6 @@ // What an hour of agent time costs, written the way people say it out loud. // -// /rate set acme $100/hour/agent/upto:4 +// /rate set acme $400/hour/agent/upto:4 // // One line that carries four decisions: the price, the period it is charged // for, the thing that is multiplied (an agent, a seat, a person), and the point @@ -101,15 +101,15 @@ function parseAmount(token) { * * The grammar is positional only in its first segment (the price); everything * after it is recognised by what it says rather than where it sits, so - * `$100/agent/hour` and `$100/hour/agent` mean the same thing. People do not + * `$400/agent/hour` and `$400/hour/agent` mean the same thing. People do not * remember an order they were never told. */ export function parseRate(spec) { const text = String(spec ?? "").trim(); - if (!text) throw new Error("a rate looks like $100/hour/agent/upto:4"); + if (!text) throw new Error("a rate looks like $400/hour/agent/upto:4"); const parts = text.split("/").map((p) => p.trim()).filter(Boolean); const price = parseAmount(parts.shift()); - if (!price) throw new Error(`can't read a price out of ${JSON.stringify(text)} — try $100/hour/agent`); + if (!price) throw new Error(`can't read a price out of ${JSON.stringify(text)} — try $400/hour/agent`); const rate = { ...price, per: "hour", unit: "flat", cap: null, min: null }; let sawPeriod = false; @@ -141,7 +141,7 @@ export function parseRate(spec) { // silently multiply the invoice by every hour tracked. if (!sawPeriod && rate.unit === "flat" && rate.cap === null) rate.per = "project"; if (rate.cap !== null && rate.unit === "flat") { - throw new Error("upto: caps a unit, so say what it caps — $100/hour/agent/upto:4"); + throw new Error("upto: caps a unit, so say what it caps — $400/hour/agent/upto:4"); } return rate; } @@ -235,14 +235,14 @@ export function splitSettlement(argv) { const USAGE = [ "usage: /rate set [--prefer SOL,USDC] [--accept fiat]", " /rate [list] [--json] · /rate show · /rate rm ", - " spec: $100/hour/agent/upto:4 · 0.5 SOL/day · $5000/project · 250 USDC/task", + " spec: $400/hour/agent/upto:4 · 0.5 SOL/day · $5000/project · 250 USDC/task", ]; /** * `/rate` and `/rates`. * * `set:` with a colon is accepted because that is how it was first written down - * (`/rates set: $100/hour/agent/upto:4`), and refusing punctuation somebody + * (`/rates set: $400/hour/agent/upto:4`), and refusing punctuation somebody * already typed teaches them nothing. */ export function rateCommand(argv = [], { write = console.log } = {}) { @@ -257,7 +257,7 @@ export function rateCommand(argv = [], { write = console.log } = {}) { if (json) { write(JSON.stringify(rates, null, 2)); return 0; } if (!names.length) { write(info("no rates yet.")); - write(` ${acid("/rate set default $100/hour/agent/upto:4")}`); + write(` ${acid("/rate set default $400/hour/agent")}`); return 0; } write(table( @@ -299,7 +299,7 @@ export function rateCommand(argv = [], { write = console.log } = {}) { const found = resolveRateTarget(business, positional[1] || "default"); const who = found.ok ? found.id : String(positional[1]).toLowerCase(); const rate = rateFor(business, who); - if (!rate) { write(err(`no rate for ${JSON.stringify(who)} and no default — /rate set ${who} $100/hour/agent`)); return 1; } + if (!rate) { write(err(`no rate for ${JSON.stringify(who)} and no default — /rate set ${who} $400/hour/agent`)); return 1; } if (json) { write(JSON.stringify(rate, null, 2)); return 0; } write(` ${bone(who)} ${acid(formatRate(rate))}${rate.source !== who ? ash(` (from ${rate.source})`) : ""}`); write(` ${ash(describeRate(rate))}`); @@ -342,7 +342,7 @@ function parseSafely(spec) { * The arithmetic everybody does in their head and gets wrong once a quarter. * Two things make it more than a multiplication: * - * - the cap. `$100/hour/agent/upto:4` means four agents cost four hundred an + * - the cap. `$400/hour/agent/upto:4` means four agents cost sixteen hundred an * hour and *so do six* — the cap is the promise that made the client sign, * and it has to be applied here rather than remembered at invoice time. * - the floor. `min:1` bills a fifteen-minute call as an hour, which is the diff --git a/src/timer.mjs b/src/timer.mjs index f3b92040..715a73b5 100644 --- a/src/timer.mjs +++ b/src/timer.mjs @@ -192,7 +192,7 @@ function stopTimer(args, write, now) { } else if (charge?.flat) { write(` ${ash("flat project fee — /billing adds it once, not per entry")}`); } else if (!rate) { - write(` ${ash("no rate for this one —")} ${acid(`/rate set ${entry.client || "default"} $100/hour/agent`)}`); + write(` ${ash("no rate for this one —")} ${acid(`/rate set ${entry.client || "default"} $400/hour/agent`)}`); } write(` ${ash(`entry ${entry.id}`)}`); return 0; diff --git a/src/tools.mjs b/src/tools.mjs index fec1e212..8fd6edf7 100644 --- a/src/tools.mjs +++ b/src/tools.mjs @@ -118,7 +118,7 @@ export const TOOLS = { billing: { desc: "Profullstack billing - clients, rates and invoices from tracked hours", bin: "billing", - // The other half. It carries the rate model /rate parses ($100/hour/agent/ + // The other half. It carries the rate model /rate parses ($400/hour/agent/ // upto:4) and bills agent-hours from the timer's entries. `billing import` // brings across a ledger that started in ~/.moshcode/business.json. install: { cmd: "npm", args: ["install", "-g", "@profullstack/billing"] }, diff --git a/test/rates.test.mjs b/test/rates.test.mjs index 4c5d9734..da2ed326 100644 --- a/test/rates.test.mjs +++ b/test/rates.test.mjs @@ -45,7 +45,7 @@ test("the spellings people actually type all land", () => { test("a rate that cannot be read says which word it choked on", () => { assert.throws(() => parseRate("$100/fortnight"), /fortnight/); assert.throws(() => parseRate("free"), /can't read a price/); - assert.throws(() => parseRate(""), /\$100\/hour\/agent/); + assert.throws(() => parseRate(""), /\$400\/hour\/agent/); // A cap with nothing to cap is the error that matters most: it reads as a // discount and silently is not one. assert.throws(() => parseRate("$100/hour/upto:4"), /caps a unit/); From 2d183a79009dbb8d7e51a0d0fcb2bf8003af25b0 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 13 Sep 2026 09:32:31 +0000 Subject: [PATCH 2/2] Release moshcode 0.98.3 with current agent-rate guidance --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1646fec6..d9d4cc87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "moshcode", - "version": "0.98.2", + "version": "0.98.3", "type": "module", "description": "moshcode — a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript", "repository": {