# visimark

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/michal-niedzwiedzki/visimark  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/visimark

## Description
MCP server for VisiMark: lets an agent verify a Markdown document's numbers match their formulas.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "visimark": {
    "command": "npx",
    "args": ["-y","visimark"]
  }
}
```

## Documentation & README

# VisiMark

**A document integrity layer for Markdown: it checks that every number in a document still matches the formula that produced it, so an agent's correct formula can't ship with a wrong total.**

VisiMark checks the numbers in Markdown documents, especially ones an agent wrote. Every computed value carries its formula, a machine proves the two still agree, and CI fails when they don't. The document stays plain Markdown.

This matters most when the Markdown is written or edited by an AI agent.
Agents are reliable at writing formulas and unreliable at the arithmetic those
formulas describe: `Net = Price * Qty` is something an agent gets right,
`50.00` is something it guesses. Write the formula instead of the number and
the number stops being a claim and becomes a derivation — reviewable in a
diff, re-runnable, enforceable in CI. The agent writes, VisiMark verifies, Git
records the result.

A VisiMark document is an ordinary Markdown file. It renders correctly on
GitHub, in VS Code preview, and through pandoc to both HTML and Word, today,
with no plugin — verified, not assumed. What VisiMark adds is that every
computed number in it carries the formula that produced it, that a machine can
prove the two still agree, and that a change to either shows up as a small,
readable diff.

## A wrong invoice that renders clean

[`docs/example-invoice-drift.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/example-invoice-drift.md) is a real B2B
invoice after someone raised the on-call hours from 12 to 20 and updated
nothing that depends on it. It renders as a clean, plausible invoice on
GitHub, in a Markdown preview, anywhere — which is the entire argument for
this project. Every number in it carries the formula that produced it; the
syntax follows further down. Here is `visimark check` reading the same file a
reviewer just skimmed and approved:

```console
$ visimark check docs/example-invoice-drift.md
docs/example-invoice-drift.md

  STALE   lines.Net       · On-call support         3120.00 ≠ 5200.00    Qty * Rate
  STALE   lines.VAT       · On-call support          717.60 ≠ 1196.00    Net * vat
  STALE   lines.Gross     · On-call support         3837.60 ≠ 6396.00    Net + VAT
  STALE   lines.Gross     · Discovery workshop      4428.50 ≠ 4428.00    Net + VAT
  STALE   lines.net_total                          23300.00 ≠ 25380.00   SUM(Net)
  STALE   lines.vat_total                           5359.00 ≠ 5837.40    SUM(VAT)
  STALE   lines.gross_total                        28659.00 ≠ 31217.40   SUM(Gross)
  STALE   lines.gross_total                        28659.00 ≠ 31217.40   SUM(Gross)
  STALE   schedule.Amount · Signature               8597.70 ≠ 9365.22    Share * lines.gross_total
  STALE   schedule.Amount · Delivery of backend    11463.60 ≠ 12486.96   Share * lines.gross_total
  STALE   schedule.Amount · Acceptance              8597.70 ≠ 9365.22    Share * lines.gross_total
  STALE   schedule.covered                         28659.00 ≠ 31217.40   SUM(Amount)
  STALE   terms.early_pay_total                    28085.82 ≠ 30593.05
  STALE   terms.early_pay_saved                      573.18 ≠ 624.35
  STALE   8 prose anchors bound to the values above

  DATE    schedule.Due    · Delivery of backend   "15.10.2026"
          Dates must be ISO 8601 calendar dates: YYYY-MM-DD.
          Unambiguous — `visimark fmt --fix-dates` rewrites it to 2026-10-15.

  DATE    schedule.Due    · Acceptance            "11/12/2026"
          Dates must be ISO 8601 calendar dates: YYYY-MM-DD.
          Ambiguous: 2026-12-11 or 2026-11-12, 29 days apart. Fix by hand.

  NOTE    schedule.Days   · 2 rows not verified (upstream DATE errors)

  UNDEF   terms.eur_total   unknown name `fx_rate`
          did you mean `fx_eur`?

  VECTOR  recon.variance    `schedule.Amount` is a column, not a value.
          Wrap it in an aggregate: SUM(schedule.Amount)

  CYCLE   late_fees.base → late_fees.fee → late_fees.total → late_fees.base

  27 problems (22 stale, 5 errors)
$ echo $?
1
```

Twenty-seven problems: a payment date ambiguous by twenty-nine days, a cell
someone nudged by hand to make a column look right, a circular reference — all
invisible on the rendered page, all caught before a human had to notice.

## Why this matters now

More and more of the documents that carry numbers are written as text and kept
in Git: reports and research notes, estimates and budgets, quotes and
invoices, project plans, financial summaries. Text is excellent for
collaboration, review and version control. But ordinary Markdown has no way to
say *this number came from these inputs, and it must still agree with them* —
so the moment an input changes, every figure downstream of it is a guess until
a human re-checks it by hand.

VisiMark adds that missing layer. The working stack for collaborating with an
agent is a text editor over lightly formatted artifacts; Markdown already
covers the prose, and this covers the calculation. The arithmetic is not the
value — the audit trail is.

## What it looks like

````markdown
| Item  | Price | Qty |   Net |
|-------|------:|----:|------:|
| pen   |  5.00 |  10 | 50.00 |
| paper |  0.10 | 100 | 10.00 |

```vmark #order
Net   = Price * Qty
total = SUM(Net)
```

Order total: **60.00**<!--vmark=order.total-->
````

Four ideas, and that is the whole format:

- A **`vmark` block** declares formulas for the table above it, and names the sheet.
- **Columns are uniform** — `Net = Price * Qty` is one rule for every row, not a
  formula per cell. Columns with no rule are inputs, and are never overwritten.
- **Aggregates are scalars**, declared alongside. This is what replaces the
  totals row; tables stay rectangular.
- An **anchor** materialises a scalar into a sentence. The HTML comment is
  invisible in every target renderer, so the prose reads normally while the
  number stays machine-checkable.

## The tutorial

[`docs/tutorial.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/tutorial.md) is the end-to-end tutorial: twenty-eight
short chapters from a plain Markdown table to a checked document, a CI job and a
script reading values back out. It teaches the language in dependency order, every
finding `check` can report, and the one habit that keeps a green check meaningful.
Every transcript in it is real.

There is a side-by-side reader for it at
[`docs/tutorial.html`](https://visimark.dev/tutorial.html),
which shows each block's Markdown source next to its rendering, in lockstep.

## Worked examples

[`docs/example-invoice.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/example-invoice.md) is a complete B2B invoice
that computes itself: line items, VAT, a payment schedule derived from the gross
total, early-payment terms, a currency conversion, and a reconciliation that
proves the instalments sum to the invoice. Its appendix explains each mechanism.

[`docs/example-invoice-drift.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/example-invoice-drift.md) is that same
invoice with the drift shown at the top of this README — the `27 problems`
transcript above is `check` reading this exact file, and its appendix walks
through every one of the 27 problems.

[`docs/example-quote-plain.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/example-quote-plain.md) is the other
direction: a quote with no VisiMark in it at all — no `vmark` block, no
anchor, just a table and a total written in prose, the way an agent hands one
over before anyone has wired it up. `visimark infer` reads it and proposes the
rules that reproduce every number already there:

```console
$ visimark infer docs/example-quote-plain.md
docs/example-quote-plain.md  table at line 10 — 4 rows, 7 columns

  column rules
    Revenue    = Seats * Fee                  4/4 rows
    Materials  = Revenue * 0.08               4/4 rows
    Delivered  = Revenue + Materials          4/4 rows

  constants worth naming
    0.08   also appears as "8%" in prose, line 18

  scalars matching figures in prose
    72        line 17  = SUM(Seats)                seats_total
    27600.00  line 18  = SUM(Revenue)              revenue_total
    2208.00   line 19  = SUM(Materials)            materials_total
    29808.00  line 19  = SUM(Delivered)            delivered_total
    530.00    line 20  = AVG(Fee)                  fee_avg

  no rule found — treating as inputs
    Module, Format, Seats, Fee

  also fits, not proposed
    Delivered = Revenue * 1.08    prefers a rule over materialised columns
    Delivered = Materials * 13.5  prefers a rule over materialised columns

docs/example-quote-plain.md  table at line 24 — 3 rows, 4 columns

  column rules
    Amount  = Share * unnamed1.delivered_total  3/3 rows

  scalars matching figures in prose
    29808.00  line 30  = SUM(Amount)               amount_total

  no rule found — treating as inputs
    Stage, Share, Due

  also fits, not proposed
    Amount = Share * 29808    prefers a rule over materialised columns

4 rules, 0 aliases, 6 scalars, 6 anchors.
```

A rule is proposed only if it reproduces every row exactly, at that column's
own precision — never a best fit, never a threshold, and `also fits, not
proposed` is listed rather than silently dropped, because a rule over
materialised columns beating one with a bare constant is a judgment call worth
seeing. `--write` inserts exactly the blocks and anchors above and rewrites
nothing else. A rule that fits every row but one is never written; it is
reported as a near-miss instead — the tool telling you the document already
has a wrong number in it, before anyone runs `check` on it. The document's own
appendix walks through every mechanism, including that near-miss case.

`infer` is the way in for the document `check` would otherwise have nothing to
say about: one with no formulas at all. Pairing the two closes the loop —
`infer` gets a plain table wired up, and `check` keeps it that way.

## How it works

VisiMark parses the document, builds a dependency graph across every sheet,
sorts it topologically, and evaluates in decimal arithmetic. Circular
dependencies are reported with the full path through the cycle.

```mermaid
flowchart LR
  parse[Parse Markdown] --> deps[Build dependency graph]
  deps --> sort[Topological sort]
  sort --> evaluate[Evaluate in decimal]
  evaluate --> cycle{"Cycle?"}
  cycle -->|yes| report[Report the CYCLE path]
  cycle -->|no| values[Computed values]
```

The CLI is the product. An agent must be able to verify a document without an
editor; a VS Code extension is a later, thin wrapper.

## The commands

Five of them. `check` is the one that matters; the rest exist to get a
document into a state `check` can be strict about, or to explain what it did.

```mermaid
flowchart LR
  plain[Plain table] -->|"infer --write"| wired[Rules and anchors in the file]
  wired -->|edit an input or a rule| stale[Stored values disagree]
  stale -->|fmt| wired
  stale -->|check| fail[Exit 1]
  wired -->|check| pass[Exit 0]
  wired -.-> evalCmd["eval / explain"]
```

| Command | What it does | Options | What it writes | Exit codes |
|---------|--------------|---------|----------------|------------|
| `visimark check FILE...` | Recomputes every formula and reports the numbers that no longer agree with it | — | nothing, ever | `0` clean · `1` findings · `2` bad usage or unreadable file |
| `visimark fmt FILE...` | Repairs stale values in place, by splicing the bytes of each number it owns | `--fix-dates` rewrites unambiguous non-ISO dates | computed cells and anchored values only — never inputs, prose or headings | `0` clean · `1` problems it cannot fix remain · `2` bad usage or unreadable file |
| `visimark infer FILE...` | Works out which rules reproduce the numbers a document already has, and proposes them | `--write` inserts what it proposed | nothing, unless `--write` — and then it only ever inserts | `0` whatever it finds, because it is advisory · `2` bad usage or unreadable file |
| `visimark eval FILE` | Prints the computed values — all of them, or one by name | `--get NAME`, `--json` | nothing | `0` · `2` bad usage, unreadable file, or no such name |
| `visimark explain FILE` | Prints each sheet's inputs, rules and evaluation order | `#sheet` limits it to one sheet | nothing | `0` · `2` bad usage, unreadable file, or no such sheet |
| `visimark ref [NAME]` | Prints what a builtin function does — signature, parameters, errors, worked examples — or lists all sixteen | `--json` | nothing | `0` · `2` no such function |

Every option, every exit code and every finding `check` can report is
tabulated in [`docs/cli-reference.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/cli-reference.md).

`check` is read-only, so it is safe to point at anything. `fmt` repairs stale
values and *only* stale values: every other kind of problem is a question a
person has to answer, so it reports those and leaves them alone.

### A green check has to mean something

A document with no formulas in it has nothing to disagree with, so a checker
that only compares numbers to rules would call it clean — the most misleading
answer it could give. `check` reports a table with no rules attached to it as a
problem in its own right:

```console
$ visimark check quote.md
quote.md

  COVERAGE a table with no `vmark` rules — nothing in this document is checked
           run `visimark infer` to derive them, or mark it `<!--vmark:no-formulas-->`

  1 problem (0 stale, 1 error)
```

Two things keep that from being annoying. It needs a table to be present, so
prose — a README, a changelog — is never asked for arithmetic it does not have.
And it is counted across the whole document, so a reference table that really
is all input passes as long as some other table carries a rule.

When a document genuinely has nothing to derive, say so in the document:

```markdown
<!--vmark:no-formulas-->
```

That marker is the only way out, and deliberately so. It lives in the file
rather than in a workflow flag, so it travels with the content, shows up in
review, and turns up in a `grep`. `visimark infer --write` writes it for you
when it finds nothing whatsoever to derive — and refuses to when it found a
near-miss or two rules it cannot choose between, because those mean the
document *does* have arithmetic and wants a person to look. The marker is
checked like anything else: add rules to a marked document later and `check`
tells you the marker is now wrong.

## In CI

The whole point of `check` is that it runs somewhere other than a human's
judgment, so the CI story is one line:

```bash
npx visimark check **/*.md
```

That exits non-zero on the first disagreement, which is all most CI systems
need. A GitHub Actions workflow can do the same with the composite action
this repo ships ([`action.yml`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/action.yml)) instead of hand-rolling the
`npx` line:

```yaml
- uses: michal-niedzwiedzki/visimark@v0.1.5
  with:
    files: "docs/**/*.md"
```

There is nothing to configure and no strictness dial to find: pointing it at a
glob is the whole setup. Any document under that glob with a table and no rules
is a failure, which is why the `<!--vmark:no-formulas-->` marker above belongs
in the file rather than in this workflow — the decision is about a document,
not about a CI run.

A project already on `remark`/`remark-lint` adds the same checks with
[`remark-lint-visimark`](https://www.npmjs.com/package/remark-lint-visimark)
instead — see [`docs/ci.md` chapter 24](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/ci.md#24-the-remarkunified-plugin).

A project on [`markdownlint`](https://github.com/DavidAnson/markdownlint) adds
them with
[`markdownlint-rule-visimark`](https://www.npmjs.com/package/markdownlint-rule-visimark)
— see [`docs/ci.md` chapter 25](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/ci.md#25-the-markdownlint-custom-rule).

An agent reaches the same engine over
[MCP](https://modelcontextprotocol.io) with
[`visimark-mcp`](https://www.npmjs.com/package/visimark-mcp), which serves
every command as a tool, the authoring discipline as resources, and writes
nothing unless an operator opens the write gate:

```bash
npm i -g visimark-mcp   # or: bun add -g visimark-mcp
npx visimark-mcp        # or: bunx visimark-mcp
claude mcp add visimark -- npx -y visimark-mcp
```

The full surface is [`docs/mcp.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/mcp.md), and chapter 29 of
[`docs/ci.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/ci.md#29-the-mcp-server) covers running it beside a CI
check.

## Diffable by construction

An `.xlsx` is a zip of XML: change one cell and code review can tell you the
file changed, and essentially nothing more. VisiMark documents review like
source, and that is a design constraint rather than a side effect of being
text.

`fmt` never re-renders the Markdown. It locates each value it owns by position
and splices the original byte buffer, so a rewrite touches the characters of
that number and nothing else — no reflowed paragraphs, no renormalised emphasis
markers, no realigned table columns, none of the four-hundred-line diff a
round-trip through a Markdown printer would produce for a one-cell change. It
also writes only what it owns: computed cells and anchored values. Input
columns, prose and headings are human territory and are never touched.

Raising one input in the worked invoice — on-call hours from 12 to 20, the
very edit the drift example above leaves unpropagated — makes `fmt` update 6
cells and 9 anchors, and the result is a **13-line diff in a 127-line
document**. Every changed line is a figure that genuinely depends on that
input, so the diff *is* the propagation: a reviewer sees the VAT, the three
milestone instalments, the early-payment terms and the EUR conversion all move
together, and can check that they moved for the right reason.

The other half is that the diff contains everything. The formula lives in the
document, so a changed rule shows up as a changed rule. Nothing outside the file
can alter a number — no plugins, no config, no clock. And because an aggregate
takes a column rather than an expression, every intermediate is materialised on
the page: a total is always the sum of numbers the reviewer can see.

## What it refuses to do

Where a value could mean two things, VisiMark errors rather than guesses.

Dates are ISO 8601 only — `YYYY-MM-DD`, ten characters. `15.10.2026` is
rejected with an offered fix, because 15 cannot be a month. `11/12/2026` is
rejected outright, because it is 11 December or 12 November depending on where
its author lives, and no amount of care catches that by reading. Thousands
separators are rejected for the same reason.

A column may carry a currency symbol or a physical unit — `$5.50`, `12 N` —
and VisiMark strips it to compute and puts it back when it writes. What it will
not do is let one column mean two things: a column holding both `$5.00` and
`€5.00` is an error, not a sum. The decoration is inert, never converted and
never propagated through a formula.

A name bound twice in one scope is an error rather than a silent overwrite.

There are no boolean literals. Comparisons produce booleans and `IF()` consumes
them, but a boolean is never written into a cell — a materialised value is a
number, a date, or a string, so the word `true` in a column stays the string it
looks like.

**There is no plugin architecture, and there will not be one.** A document's
numbers depend on its own text and the version of VisiMark reading it, and on
nothing else — no extension modules, no config file, no environment, no
network, no clock. A registry of host-supplied functions would produce
documents whose arithmetic cannot be checked from the document, which is the
one thing the format exists to prevent. When the built-in vocabulary is too
small the answer is a new primitive in the engine, readable by everyone and
runnable by everyone; when a value genuinely comes from outside, it belongs in
an input column where a human wrote it down. Requests to grow that vocabulary —
and proposals for any other language or tooling change — go through
[`docs/vocabulary-catalogue.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/vocabulary-catalogue.md), which records
every one and the decision on it; the review process is
[`docs/issue-runbook.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/issue-runbook.md).

This makes the format smaller, not merely stricter: there is no locale, no
configuration, and no rule for what a bare `/` means.

## Status

All five commands are implemented, in TypeScript. Install the `visimark`
command with `bun add -g visimark` or `npm i -g visimark` — it runs under
whichever of Bun or Node is on your PATH — or run it without installing with
`npx visimark`. (On Windows the `npx` / `npm i -g` shims need `sh` on PATH,
which Git Bash or WSL provide.) All three worked examples pass as the
acceptance suite — `check` on the drift invoice reproduces the transcript above
byte-for-byte, `fmt` leaves the clean invoice untouched, and `infer` on
[`docs/example-quote-plain.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/example-quote-plain.md) — a quote with no
formulas in it at all — reproduces the transcript in that document's own
appendix. The design is
written up in [`docs/visimark-design.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/visimark-design.md), including the
deferred work and the known tensions; the implementation plan is
[`docs/superpowers/plans/2026-09-03-visimark-cli.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/superpowers/plans/2026-09-03-visimark-cli.md).

The editor support is implemented too: one language server
(`packages/visimark-lsp`) wrapping the same engine, and a VS Code client
(`editors/vscode`) — live diagnostics, `fmt` behind the editor's own
format-on-save, quick fixes, inlay hints, CodeLens and hover.

The extension is not published to a marketplace yet; to build and install it
from a clone (Bun, like the rest of the repo's tooling):

```bash
bun run vscode-install     # build, package and install (also reinstalls)
bun run vscode-uninstall   # remove it again
```

Reload the window afterwards, then open `docs/example-invoice-drift.md`. Both
targets need the `code` CLI on your PATH. For development, press <kbd>F5</kbd>
instead — that runs the extension straight from `editors/vscode` in a separate
Extension Development Host, so uninstall the packaged copy first or you will see
every diagnostic twice.

The **Obsidian plugin** is for people who keep notes in Obsidian, read them on
a phone and will never open a terminal. It marks every computed value in reading
mode and Live Preview, explains where a value came from, and sweeps a whole vault
for notes that disagree with themselves. It is a client of the engine rather than
of the language server, it is not published to npm, and it does nothing on a note
that has no `vmark` block. Install it from the
[latest plugin release](https://github.com/michal-niedzwiedzki/visimark/releases)
(the ones tagged without a `v`, such as `0.2.1`) with
[BRAT](https://github.com/TfTHacker/obsidian42-brat) or by copying its three
files into a vault —
[`editors/obsidian/README.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/editors/obsidian/README.md) has the steps and
says what each feature does.

Releases are tag-driven: pushing a `vX.Y.Z` tag publishes the engine to npm and
the extension to both the VS Code Marketplace and Open VSX. The workflow needs
three repository secrets — `NPM_TOKEN`, `VSCE_PAT` and `OVSX_PAT`. The checklist
for cutting one is [`docs/releasing.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/releasing.md).

## For agents

[`skills/visimark/SKILL.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/skills/visimark/SKILL.md) is an agent skill for
authoring and verifying these documents. Copy it to `~/.claude/skills/visimark/`
to install it. Its central warning is one worth stating here too: a green check
is evidence of agreement, not of derivation. Change an input and confirm the
checker starts complaining before believing a document is wired up. The
`COVERAGE` finding described above exists so that an agent cannot report a
green build on a document with no build in it, but the habit is still the
better safeguard.

There is also an MCP server, [`visimark-mcp`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/mcp.md), for an agent
working in a repository it has never seen: `npx visimark-mcp` or
`bunx visimark-mcp`, or `claude mcp add visimark -- npx -y visimark-mcp`. It
serves the skill above as a resource, so the discipline arrives with the
verifier rather than separately. It is read-only unless started with
`--allow-write` **and** given a host-declared root.

Editor support is specified in
[`docs/visimark-editor-plugins-design.md`](https://github.com/michal-niedzwiedzki/visimark/blob/HEAD/docs/visimark-editor-plugins-design.md):
one language server — continuous `check` as diagnostics, `fmt` behind
the editor's own format-on-save, quick fixes, and inlay hints that show the
computed value without touching the bytes — with VS Code as the first client.

```
git clone … && cd visimark && bun install
bun test                    # the full suite, the three examples included
bunx visimark check docs/example-invoice-drift.md
```

`bun install` builds the engine and links the `visimark` command into
`node_modules/.bin`, so `bunx visimark` works in a fresh clone. To run the CLI
straight from source without a build, use `bun packages/visimark/src/cli/main.ts
check FILE`.

The project began as a CSV-based idea and moved to Markdown so that several
small sheets can live inside one master document, and so that the file renders
as a document rather than as data. The name is a nod to VisiCalc — the first
spreadsheet software, originally developed for the Apple II by VisiCorp and
later ported to the IBM PC.

## Out of scope

VisiMark is deliberately not a spreadsheet replacement. No grid, no
presentation layer, no cell styling, no locale, no Excel file compatibility,
and no attempt at Excel's function library. Use other tools for neat
presentation — and a spreadsheet when what you want is a spreadsheet.

Use VisiMark when what you want is a document: plain text, readable without
the tool, reviewable in an ordinary pull request, writable by a human or an
agent — with numbers that can be checked on every commit.

<!--vmark:no-formulas-->

