Specification · solo @ 0.1

solo — Freelancer Invoicing · Area Specification

last conformance run: 2/2 scenarios pass · 9 acts mapped 2026-09-03 02:29 UTC

SpecActsInvariantsScenarios

Spec

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.

1. Calibration: who this is for

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.

2. Scope

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.

3. Entities and lifecycles

4. The acts

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.

5. Invariants

6. Required read models

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).

7. Contract vs freedom

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.

8. Conformance (scenarios)

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.

9. Deferred — with reasons

ItemWhy deferred, not rejected
Credit notes / corrections on paid invoicesVoid covers the unpaid case; applied-cash unwinding needs its own design pass.
Recurring invoicesReal freelancer need; add with demand, not speculation.
Multi-currencySame 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 / interestTerms 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.

Acts

ActKindRequired
draft_invoicewritecustomer lines
update_draftwriteinvoice
issue_invoicewriteinvoice
void_invoicewriteinvoice reason
record_paymentwritecustomer amount
apply_paymentwritepayment invoice
invoicereadinvoice
documentreadinvoice
outstandingread
statementreadcustomer
create_customerenvironmentname
set_company_profileenvironmentname
update_customerenvironmentcustomer

Invariants

IdInvariant
S-1Money is integer cents, everywhere.
S-2An issued invoice is immutable — mistakes are void-and-reissue, on the record.
S-3The seller and bill-to blocks freeze at issuance; issuing without a profile or a client address is refused with the guide sentence.
S-4Numbers are sequential and never reused; a voided number stays burned.
S-5Invoice timing is the freelancer's agreement — the system records, never gatekeeps terms.
S-6No invented facts: amounts, dates and names come from the person or stay empty.
S-7Documents are produced, never sent; the doc link views one document (DRAFT-stamped until issued) and nothing more.
S-8Every write is a logged act with an actor; refusals are logged too.
S-9Journal derivation balances: issue to AR/Revenue, receipt to Cash/Deposits, application to Deposits/AR.

Scenarios

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.

the first invoice: profile, client, draft, issue, partial payment pass

The whole product in one sitting. S-1 cents, S-3 seller frozen at issue, S-5 the terms are the agreement, S-7 the doc link is produced not sent.
Environment: set_company_profile, create_customer
#ActExpectWhy
1draft_invoiceok status="draft" total=545000S-1: 17 × $250.00 + $1,200.00 = $5,450.00, all integer cents. S-5: net-30 is THEIR agreement — recorded, not judged
2update_draftok total=582500drafts are worksheets — add the forgotten line, totals recompute
3issue_invoiceok status="issued" seller_name="Vega Consulting" seller_as_issued=trueS-3: the seller block froze from the profile; the doc link now exists for the freelancer to SEND THEMSELVES (S-7)
4record_paymentok
5apply_paymentok
6outstandingok count=1 total_open=282500partial payment leaves the honest remainder on the morning list
7statementok closing_balance=282500

immutability, burned numbers, and the refusal that teaches pass

S-2 issued is immutable, S-3 the no-profile refusal IS the interactive guide, S-4 void burns the number, S-8 refusals are logged.
Environment: create_customer
#ActExpectWhy
1draft_invoiceok status="draft" total=90000qty defaults to 1; drafting needs no profile — issuing does
2issue_invoicerefused No company details yet — ask for the company name firstS-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
3set_company_profileok
4issue_invoiceok status="issued" seller_name="Nora Kis Design"
5update_draftrefused Only a draft can be editedS-2: issued is immutable — the refusal names the path (void-and-reissue)
6void_invoiceok status="void"S-4: INV-0001 stays burned; the doc link dies with it
7draft_invoiceok id="INV-0002"the next number, never the old one