VisiMark — examples
Table of Contents
# Invoice 2026/09/014

Invoice 2026/09/014

**Seller:** EPSI sp. z o.o., ul. Bura 21, 00-000 Burowce, NIP PL9998877666
**Buyer:** Northwind Logistics GmbH, Hafenstraße 8, 22222 Hamburg, USt-IdNr. DE881188118

Seller: EPSI sp. z o.o., ul. Bura 21, 00-000 Burowce, NIP PL9998877666 Buyer: Northwind Logistics GmbH, Hafenstraße 8, 22222 Hamburg, USt-IdNr. DE881188118

**Issued:** 2026-09-03    **Delivered:** 2026-08-31    **Payment due:** 2026-09-17

Issued: 2026-09-03    Delivered: 2026-08-31    Payment due: 2026-09-17

```vmark
vat              = 23%
early_pay_disc   = 2%
fx_eur [PLN/EUR] = 4.2650
```
vat              = 23%
early_pay_disc   = 2%
fx_eur [PLN/EUR] = 4.2650
## Services rendered

Services rendered

| Item                       | Unit  | Qty | Rate [PLN] | Net [PLN] | VAT [PLN] | Gross [PLN] |
|----------------------------|-------|----:|-----------:|----------:|----------:|------------:|
| Discovery workshop         | day   |   2 |    1800.00 |   3600.00 |    828.00 |     4428.00 |
| **Backend implementation** | hour  |  64 |     220.00 |  14080.00 |   3238.40 |    17318.40 |
| Code review retainer       | month |   1 |    2500.00 |   2500.00 |    575.00 |     3075.00 |
| On-call support            | hour  |  12 |     260.00 |   3120.00 |    717.60 |     3837.60 |
Item Unit Qty Rate [PLN] Net [PLN] VAT [PLN] Gross [PLN]
Discovery workshop day 2 1800.00 3600.00 828.00 4428.00
Backend implementation hour 64 220.00 14080.00 3238.40 17318.40
Code review retainer month 1 2500.00 2500.00 575.00 3075.00
On-call support hour 12 260.00 3120.00 717.60 3837.60
```vmark #lines
Net               = Qty * Rate
VAT   precision 2 = Net * vat
Gross             = Net + VAT

net_total   [PLN] = SUM(Net)
vat_total   [PLN] = SUM(VAT)
gross_total [PLN] = SUM(Gross)
```
Net               = Qty * Rate
VAT   precision 2 = Net * vat
Gross             = Net + VAT

net_total   [PLN] = SUM(Net)
vat_total   [PLN] = SUM(VAT)
gross_total [PLN] = SUM(Gross)
Net of tax the engagement comes to **23300.00 PLN**<!--vmark=lines.net_total|unit-->.
VAT at 23% adds **5359.00 PLN**<!--vmark=lines.vat_total|unit-->, giving a total due of
**28659.00 PLN**<!--vmark=lines.gross_total|unit-->.

Net of tax the engagement comes to 23300.00 PLN. VAT at 23% adds 5359.00 PLN, giving a total due of 28659.00 PLN.

## Payment schedule

Payment schedule

| Milestone           | Share | Amount [PLN] | Due        |
|---------------------|------:|-------------:|------------|
| Signature           |   30% |      8597.70 | 2026-09-10 |
| Delivery of backend |   40% |     11463.60 | 2026-10-15 |
| Acceptance          |   30% |      8597.70 | 2026-11-30 |
Milestone Share Amount [PLN] Due
Signature 30% 8597.70 2026-09-10
Delivery of backend 40% 11463.60 2026-10-15
Acceptance 30% 8597.70 2026-11-30
```vmark #schedule
Amount precision 2 = Share * lines.gross_total

covered [PLN] = SUM(Amount)
```
Amount precision 2 = Share * lines.gross_total

covered [PLN] = SUM(Amount)
The three milestones account for **28659.00 PLN**<!--vmark=schedule.covered|unit-->.

The three milestones account for 28659.00 PLN.

## Payment terms

Payment terms

```vmark #terms
early_pay_total [PLN] precision 2 = lines.gross_total * (1 - early_pay_disc)
early_pay_saved [PLN]             = lines.gross_total - early_pay_total
eur_total       [EUR] precision 2 = lines.gross_total / fx_eur
```
early_pay_total [PLN] precision 2 = lines.gross_total * (1 - early_pay_disc)
early_pay_saved [PLN]             = lines.gross_total - early_pay_total
eur_total       [EUR] precision 2 = lines.gross_total / fx_eur
Settlement within 7 days qualifies for a 2% early-payment discount, reducing the
amount due to **28085.82 PLN**<!--vmark=terms.early_pay_total|unit--> — a saving of
**573.18 PLN**<!--vmark=terms.early_pay_saved|unit-->.

Settlement within 7 days qualifies for a 2% early-payment discount, reducing the amount due to 28085.82 PLN — a saving of 573.18 PLN.

For reference, the gross total is approximately
**6719.58 EUR**<!--vmark=terms.eur_total|unit--> at the ECB reference rate of
4.2650 PLN/EUR recorded on the delivery date.

For reference, the gross total is approximately 6719.58 EUR at the ECB reference rate of 4.2650 PLN/EUR recorded on the delivery date.

## Reconciliation

Reconciliation

```vmark #recon
scheduled [PLN] = SUM(schedule.Amount)
variance  [PLN] = lines.gross_total - scheduled
assert variance == 0
```
scheduled [PLN] = SUM(schedule.Amount)
variance  [PLN] = lines.gross_total - scheduled
assert variance == 0
Scheduled instalments total **28659.00 PLN**<!--vmark=recon.scheduled|unit--> against a
gross invoice value of **28659.00 PLN**<!--vmark=lines.gross_total|unit-->, leaving a
variance of **0.00 PLN**<!--vmark=recon.variance|unit-->.

Scheduled instalments total 28659.00 PLN against a gross invoice value of 28659.00 PLN, leaving a variance of 0.00 PLN.

---

## Appendix — how this document computes

Appendix — how this document computes

Everything above renders as ordinary Markdown. The mechanics are four ideas.

Everything above renders as ordinary Markdown. The mechanics are four ideas.

**A `vmark` block declares formulas for the table above it.** The block carries the
sheet's identity as `#lines`, `#schedule` and so on. A block with no identity —
the one near the top of this file — declares document-level constants that every
sheet can read, which is why `vat` and `fx_eur` resolve without qualification.

A vmark block declares formulas for the table above it. The block carries the sheet's identity as #lines, #schedule and so on. A block with no identity — the one near the top of this file — declares document-level constants that every sheet can read, which is why vat and fx_eur resolve without qualification.

**Columns are uniform.** `Net = Qty * Rate` is one rule applied to every row of
the sheet, not a formula per cell. Columns not named in the block — `Item`,
`Unit`, `Qty`, `Rate`, `Share`, `Due` — are inputs, typed by a human and never
overwritten. There is no per-row exception and no totals row inside a table.

Columns are uniform. Net = Qty * Rate is one rule applied to every row of the sheet, not a formula per cell. Columns not named in the block — Item, Unit, Qty, Rate, Share, Due — are inputs, typed by a human and never overwritten. There is no per-row exception and no totals row inside a table.

**Aggregates are scalars, declared in the same block.** `net_total = SUM(Net)`
collapses a column to a single named value that lives outside any grid. This is
what replaces the totals row, and it is why the `#recon` block needs no table of
its own: a sheet is a namespace, and a table is optional.

Aggregates are scalars, declared in the same block. net_total = SUM(Net) collapses a column to a single named value that lives outside any grid. This is what replaces the totals row, and it is why the #recon block needs no table of its own: a sheet is a namespace, and a table is optional.

**A comment anchor materialises a scalar into prose.** `<!--vmark=lines.net_total-->`
binds the bold number in front of it to that name. The comment is invisible in
every Markdown renderer that permits raw HTML, so the sentence reads normally
while the number stays machine-checkable.

A comment anchor materialises a scalar into prose. <!--vmark=lines.net_total--> binds the bold number in front of it to that name. The comment is invisible in every Markdown renderer that permits raw HTML, so the sentence reads normally while the number stays machine-checkable.

Sheets address each other by qualified name, as `#schedule` does when it reads
`lines.gross_total` and `#recon` does when it aggregates `schedule.Amount`. A
foreign column is a vector, so it is legal only inside an aggregate — bare
`schedule.Amount` in a column formula is an error, because the two sheets have
no reason to share a row count.

Sheets address each other by qualified name, as #schedule does when it reads lines.gross_total and #recon does when it aggregates schedule.Amount. A foreign column is a vector, so it is legal only inside an aggregate — bare schedule.Amount in a column formula is an error, because the two sheets have no reason to share a row count.

**An `assert` states an invariant.** `assert variance == 0` in `#recon` is not a
binding — it stores nothing and `fmt` never touches it — but `check` evaluates it
and fails if it is ever false. Without it, a payment schedule whose shares stop
summing to the invoice total would leave a non-zero `variance`, and `check`
would still pass: every number would agree with its own formula. The assertion
is what turns "the reconciliation is computed" into "the reconciliation is
enforced".

An assert states an invariant. assert variance == 0 in #recon is not a binding — it stores nothing and fmt never touches it — but check evaluates it and fails if it is ever false. Without it, a payment schedule whose shares stop summing to the invoice total would leave a non-zero variance, and check would still pass: every number would agree with its own formula. The assertion is what turns "the reconciliation is computed" into "the reconciliation is enforced".

**A unit is declared in a bracket after a name, and checked.** `Rate [PLN]` says
each rate is złoty per item, `fx_eur [PLN/EUR]` says what the exchange rate
converts, and `eur_total [EUR]` says what the conversion must produce. The other
units — `Net [PLN]`, `net_total [PLN]` and the rest — were written by
`visimark infer --write`, which records the unit each formula derives; from then
on each one is checked like any other declaration. Dividing `PLN` by `PLN/EUR`
derives the `EUR` that `eur_total` declares, so multiplying by the rate instead,
or adding a euro amount to a złoty one, fails `check`. Every anchored figure
carries the `|unit` display rule, so `fmt` writes the unit from the declaration
along with the number, and a changed declaration rewrites the unit in the
sentence too.

A unit is declared in a bracket after a name, and checked. Rate [PLN] says each rate is złoty per item, fx_eur [PLN/EUR] says what the exchange rate converts, and eur_total [EUR] says what the conversion must produce. The other units — Net [PLN], net_total [PLN] and the rest — were written by visimark infer --write, which records the unit each formula derives; from then on each one is checked like any other declaration. Dividing PLN by PLN/EUR derives the EUR that eur_total declares, so multiplying by the rate instead, or adding a euro amount to a złoty one, fails check. Every anchored figure carries the |unit display rule, so fmt writes the unit from the declaration along with the number, and a changed declaration rewrites the unit in the sentence too.

The payoff is the last section. `visimark check` recomputes every formula and
exits non-zero if any stored number disagrees, so an arithmetic error in this
invoice fails CI instead of reaching the buyer — whether a human or an agent
wrote it.

The payoff is the last section. visimark check recomputes every formula and exits non-zero if any stored number disagrees, so an arithmetic error in this invoice fails CI instead of reaching the buyer — whether a human or an agent wrote it.

### Deliberately unresolved

Deliberately unresolved

A percent literal is a number: `23%` and `0.23` are the same value, so `vat` may
be written either way and arithmetic needs no special case. Because the tool
writes only computed cells, an input written as `30%` stays `30%` forever — the
display-format layer that Excel needs never has to exist here.

A percent literal is a number: 23% and 0.23 are the same value, so vat may be written either way and arithmetic needs no special case. Because the tool writes only computed cells, an input written as 30% stays 30% forever — the display-format layer that Excel needs never has to exist here.

Dates are settled: ISO 8601 calendar dates, `YYYY-MM-DD`, and nothing else, so
no date here can mean two things. Thousands separators in cells, decimal versus
binary floating point, and rounding policy remain open. See
`example-invoice-drift.md` for what the checker does with each.

Dates are settled: ISO 8601 calendar dates, YYYY-MM-DD, and nothing else, so no date here can mean two things. Thousands separators in cells, decimal versus binary floating point, and rounding policy remain open. See example-invoice-drift.md for what the checker does with each.