last conformance run: 2/2 scenarios pass · 9 acts mapped 2026-09-03 02:29 UTC
Status: 0.1-draft. The fourth area, deliberately tiny.
Solo is NOT o2c-lite — it is a different calibration, extracted from a different life. o2c bills what shipped because a warehouse must; a freelancer has no warehouse, no fulfilment, and no credit committee. What they have is an agreement with a client and a need for clean, immutable invoices and an honest picture of who owes what. Solo records exactly that.
One person (or a very small team) selling their own work: consultants, developers, designers, trades. A handful of clients, invoices with free-text lines, payment by whatever was agreed. USD only at 0.1, limit stated. The chat agent is often the ONLY interface — the doctrine is written as an interactive guide, and refusals steer the conversation, not just the tool call.
In: clients (core customer, reused), drafting and issuing invoices, voiding with reasons, recording and applying payments, outstanding and statements, the printable document with a shareable link, journal derivation for the accountant.
Out (§9): orders/fulfilment (that is o2c), credit gating (the freelancer IS the credit authority), multi-currency, recurring invoices, sending anything anywhere.
/doc/<space>/<token> renders the real document, stamped DRAFT until issued (0.1.1: the preview a person looks at before the point of no return, so nobody hand-rolls a document). At issue the same link becomes the invoice — the link the freelancer forwards to their client — and <token>.pdf is the same document as a file (issued invoices only). Void does not kill the link: the document stays readable and printable, stamped VOID, because a record you cannot read is not a record; open is 0 on a void invoice so no reader has to remember the status.Writes (6): draft_invoice, update_draft, issue_invoice, void_invoice, record_payment, apply_payment. Reads (4): invoice, document, outstanding, statement. Environment (core, existing): create_customer, update_customer, set_company_profile.
invoice (the whole document: lines, seller and bill-to snapshots, applied/open, the doc link), document (0.1.1: the rendered page as a picture, DRAFT-stamped until issued, and the PDF file once issued — so an agent looks at the real render before issue and hands over the file after), outstanding (issued-unpaid, oldest first, days overdue, totals), statement (one client's invoices and payments, chronological, closing balance).
Contract: draft-only mutability, seller freeze, burned numbers, bounds on application, produce-never-send. Freedom: numbering prefix, tax determination (rates are captured and frozen; deciding them is the freelancer's job), how the agent phrases the guide.
01 the first invoice: profile → client → draft → issue (doc fields present) → partial payment → apply → outstanding shows the remainder · 02 immutability: update after issue refused; issue without profile refused with the guide sentence; void with reason; number burned; paid-invoice void refused.
| Item | Why deferred, not rejected |
|---|---|
| Credit notes / corrections on paid invoices | Void covers the unpaid case; applied-cash unwinding needs its own design pass. |
| Recurring invoices | Real freelancer need; add with demand, not speculation. |
| Multi-currency | Same whole-area concern as everywhere. USD stated plainly. |
| Sending (email the client) | The doc link makes it one step away, which is exactly why the boundary must be crossed deliberately or not at all. |
| Late fees / interest | Terms live in the agreement; automating them is policy the system refuses to own at 0.1. |
---
Change log: 0.1-draft (2026-09-02) — drafted with Peter's refocus from an o2c door to a freelancer invoice generator; the interactive-guide doctrine and S-5 came from that conversation. 0.1.1 (2026-09-04) — client address + tax id, bill-to block frozen at issue, preview link from the first draft (DRAFT-stamped), document read returning the rendered page and the PDF; void invoices stay readable (VOID-stamped) with open = 0.
| Act | Kind | Required |
|---|---|---|
draft_invoice | write | customer lines |
update_draft | write | invoice |
issue_invoice | write | invoice |
void_invoice | write | invoice reason |
record_payment | write | customer amount |
apply_payment | write | payment invoice |
invoice | read | invoice |
document | read | invoice |
outstanding | read | — |
statement | read | customer |
create_customer | environment | name |
set_company_profile | environment | name |
update_customer | environment | customer |
| Id | Invariant |
|---|---|
S-1 | Money is integer cents, everywhere. |
S-2 | An issued invoice is immutable — mistakes are void-and-reissue, on the record. |
S-3 | The seller and bill-to blocks freeze at issuance; issuing without a profile or a client address is refused with the guide sentence. |
S-4 | Numbers are sequential and never reused; a voided number stays burned. |
S-5 | Invoice timing is the freelancer's agreement — the system records, never gatekeeps terms. |
S-6 | No invented facts: amounts, dates and names come from the person or stay empty. |
S-7 | Documents are produced, never sent; the doc link views one document (DRAFT-stamped until issued) and nothing more. |
S-8 | Every write is a logged act with an actor; refusals are logged too. |
S-9 | Journal derivation balances: issue to AR/Revenue, receipt to Cash/Deposits, application to Deposits/AR. |
Each scenario is a file of acts with expected outcomes. ok means the act must succeed with the listed fields; refused means the act must be refused with a sentence containing the listed text. Refusals are contract.
set_company_profile, create_customer| # | Act | Expect | Why |
|---|---|---|---|
| 1 | draft_invoice | ok status="draft" total=545000 | S-1: 17 × $250.00 + $1,200.00 = $5,450.00, all integer cents. S-5: net-30 is THEIR agreement — recorded, not judged |
| 2 | update_draft | ok total=582500 | drafts are worksheets — add the forgotten line, totals recompute |
| 3 | issue_invoice | ok status="issued" seller_name="Vega Consulting" seller_as_issued=true | S-3: the seller block froze from the profile; the doc link now exists for the freelancer to SEND THEMSELVES (S-7) |
| 4 | record_payment | ok | |
| 5 | apply_payment | ok | |
| 6 | outstanding | ok count=1 total_open=282500 | partial payment leaves the honest remainder on the morning list |
| 7 | statement | ok closing_balance=282500 |
create_customer| # | Act | Expect | Why |
|---|---|---|---|
| 1 | draft_invoice | ok status="draft" total=90000 | qty defaults to 1; drafting needs no profile — issuing does |
| 2 | issue_invoice | refused No company details yet — ask for the company name first | S-3: the refusal is the GUIDE — it tells the agent exactly what to gather, one question at a time. This is the interactive-onboarding mechanism, as a testable sentence |
| 3 | set_company_profile | ok | |
| 4 | issue_invoice | ok status="issued" seller_name="Nora Kis Design" | |
| 5 | update_draft | refused Only a draft can be edited | S-2: issued is immutable — the refusal names the path (void-and-reissue) |
| 6 | void_invoice | ok status="void" | S-4: INV-0001 stays burned; the doc link dies with it |
| 7 | draft_invoice | ok id="INV-0002" | the next number, never the old one |