Skip to content

AmberCalc: columnar spreadsheet

First-party on-device program #7 (ADR-0042).

Implementation status (GWP-752). Every operation in the Tier-2 summary is built and driven from the keyboard: entry, cell/range references, the aggregate floor, recalculation, typed columns (INFO), the per-column printers, sort/filter (LINK / EQ), cell editing (QUOTE), clearing and cancelling (NIL), CAR-drill with a BACK nav stack, GoTo, and the ADR-0035 trackpoint handlers. Two things remain deliberately open, both flagged in Tier 3: signed-relative coordinates (@-1$0) are still absolute-only in the parser, and the hledger-style query language is still the tracked ownership question — the filter mechanism exists and would drive it. v1.1 | 2026-07-18

Canonical per ADR-0042. This spec resolves the design stub’s four open questions to the v1 design taken with Josh on 2026-06-25 (see the design doc ../../plans/2026-06-25-ambercalc-blackledger-design.md (deleted 2026-07-29; recover from git history) §3 + Appendix A). The formula model and the :world / dossier-commit FFI surface it leans on are formalized in the AmberCalc formula/FFI ADR (ADR-0049); where this and that ADR disagree, the ADR wins. See the program roster.

Gate: draft (stub). Basics from the ADR-0042 charter and the Tier 2 spec; the treatment pass has not been written and this stub invents no design.

The deck’s columnar spreadsheet: the generic, always-on, content-free analytics lens (the VisiCalc / ses.el floor). A calculator over a table, where “type the money column and the data comes alive.” Its headline tenant is forensic accounting (the Black Ledger cart), but AmberCalc knows nothing about accounts or fraud; it crunches whatever table the world region holds.

Navigate a grid by cell cursor, type a column to turn dead text into sortable / summable data, write a formula in host Lisp, and export a finding to DOSSIER. The repeating decision: which column to type and which cell to dive next in the hunt for the anomaly.

Owed to the treatment pass.

  • Reuses: the host Fe evaluator (formulas are Lisp, ses.el-style), the canonical cell grid, the trackpoint cart-FFI, the World Engine’s dataset region, and the sanctioned dossier-commit path.
  • New surface required: the SES formula model over cell coordinates, formalized in ADR-0049.
  • Deferred to the tiers below (see Open / deferred in Tier 3).

Gate: approved (the v1 design; merged through PR review).

AmberCalc is the deck’s columnar spreadsheet: the generic, always-on, content-free analytics lens (the VisiCalc / ses.el floor). It is a calculator over a table: a grid, a cell cursor, formulas, recalc, typed columns, sort/filter, and a sanctioned export to DOSSIER. It ships in the system image and runs bare on an empty world; it is never superseded by a cart.

Like every first-party program, AmberCalc is a lens and a cart supplies the world it looks at (enrichment-contract.md §1). Its headline tenant is the forensic-accounting domain: Black Ledger (cart 0x03) hands AmberCalc a generated shell-company ledger and overlays the FORENSICS vocabulary on the navigable grid AmberCalc returns. But AmberCalc is content-free: it knows nothing about accounts, double-entry, or fraud (see What AmberCalc does NOT own). It crunches whatever table the world region holds.

OperationHow
enter a value / label / formulafirst-keystroke type routing (digit / " / ()
reference a cell / range(cell @r$c) / (range @r$c @r$c) in a formula
aggregatesum / avg / min / max / count over a range (host Fe builtins)
type a column$ currency · @ date · # int: turns text into sortable/summable data
displayper-column printer (currency / date / acct / raw)
navigateCAR / CDR / BACK · GoTo @r$c · trackpoint aim+click
ingest a dataset:world handle from launch-app (live region)
export a finding(dossier-commit <key>): sanctioned fact-node, never a raw write

AmberCalc is a calculator over a table. It does not own:

  • Accounts. The colon-path account namespace is the world the cart installs, not an AmberCalc concept.
  • Double-entry. The zero-sum invariant, elided-amount inference, register-vs-balance lenses are Black Ledger’s ledger model.
  • Fraud / forensics. TRACE, FLAG, CROSS/RECONCILE, CHAIN, reconcile-by-assertion, scoring, the verdict: all are the cart’s FORENSICS grammar overlaid on AmberCalc’s grid.
  • Missions / economy. Contracts, the objective graph, the move budget / case clock, credits / reputation / mastery, threat caps. AmberCalc emits no economy events; durable consequence is the cart’s via the sanctioned outcome path.

AmberCalc gives the table a navigable, typed, formula-bearing surface; the cart gives the table meaning. (Black Ledger’s design is specified separately in ../cartridges/modules/black-ledger.md.)

Gate: approved (the v1 design; merged through PR review).

Implementation seam (GWP-608 / GWP-614): file names, FFI signatures, coordinate/formula grammar, and ADR contracts appear from here down.

  • Renders on the canonical 128×75 surface (see the Canonical Hardware Specification). Content draws on Rows 1–73; Row 0 (firmware status bar) and Row 74 (firmware action bar) are firmware-owned and never drawn by AmberCalc. Grid dimensions are runtime-queried via the cell-API getters ((cell-cols) → 128, (cell-rows-usable) → 73), not hard-coded.
  • Per-region integer scale (per the animation / scale convention): a dense transaction table renders at 1× (≈73 body rows visible), column headers at 2×. Exact per-region scale figures are an on-glass tuning detail (deferred; see Open / deferred).
  • One cell cursor. Movement is by CAR/CDR navigation (descend / next sibling) and by trackpoint (aim + click; see Navigation).
  • GoTo over scrolling. For large grids, jump-by-coordinate to a target cell is the primary long-distance motion; scrolling follows the cursor. This is the chord-scarce-friendly idiom (jump, don’t page).

AmberCalc adopts Org-mode’s @row$col addressing, not A1.

  • All-digit: @3$2 is row 3, column 2. “Which letter is column 28” is hostile on a digit-first keyboard with a small glyph set; @r$c parses trivially and reads naturally on the digit pad.
  • Signed-relative is the relative form: @-1$0 is the cell directly above the current cell; absolute (@3$2) is the default.
  • Ranges are @r$c..@r$c: e.g. @2$1..@4$3 is the 3×3 block from row 2 col 1 to row 4 col 3.

Formula language: host Fe sexp, SES model (resolves stub Q1)

Section titled “Formula language: host Fe sexp, SES model (resolves stub Q1)”

Formulas are KEC Lisp expressions, not a bespoke mini-DSL. On a machine that already tree-walks Fe, the spreadsheet is a Lisp environment, exactly as ses.el is to Emacs:

  • A cell is a bound symbol. (cell @3$2) resolves the value stored at that coordinate; a cell’s formula is an ordinary Fe expression evaluated in an environment where coordinate symbols are bound to cell values.

  • A range is a real Lisp list. (range @2$1 @4$1) evaluates to the list of values in that span, so the aggregate functions are just function application over a list: (sum (range @2$1 @4$1)), (count (range …)), (map …) are not special syntax.

  • The aggregate floor (the 47-year VisiCalc set) is exposed as ordinary Fe builtins: sum, avg, min, max, count, plus arithmetic (+ - * /) and if. Anything else an operator can write as Lisp is available; the formula surface is the host language, not a fenced subset.

  • First-keystroke type routing (the universal spreadsheet idiom) decides cell intent from the first character typed, eliminating a “is this text or a value” mode:

    First keystrokeCell becomes
    a digit (09)a number
    "a label (literal text)
    (a formula (an Fe expression)

Because formulas are host Fe, the spreadsheet inherits the deck’s existing evaluator, REPL inspectability, and macro/lambda recording for free: a column’s formula can be recorded once and replayed, scripted, or inspected at the TERM prompt.

Value / display split: per-column printers

Section titled “Value / display split: per-column printers”

Stored value ≠ rendered glyphs. As in ses.el, a column carries a printer function that maps the stored value to its on-screen representation:

PrinterRenders
currencyright-aligned amount with a thousands separator (and the currency glyph)
dateISO date (YYYY-MM-DD)
acctcolon-path account string (Shell:Cayman:Holdings:042)
rawthe value verbatim, no transform

The printer is display only: sorting, filtering, and aggregation operate on the stored value, never the rendered string. This is how “type the money column and the data comes alive” works, and it satisfies the screen-design rule that numerics right-align to a consistent column and never truncate.

Borrowing VisiData’s typed columns: typing a column turns dead text into sortable / summable data. Applying a type both sets the column’s stored interpretation and selects its printer:

TypeSigilEffect
currency$text → money values; currency printer; sortable / summable
date@text → dates; date printer; sortable / range-filterable
int#text → integers; raw/numeric printer; sortable / summable

Typing a column (a single keystroke on the column) is the first satisfying beat of any investigation and the precondition for sort / filter / aggregate over that column.

Dataset ingestion: :world handle, not :payload blob (resolves stub Q2)

Section titled “Dataset ingestion: :world handle, not :payload blob (resolves stub Q2)”

AmberCalc reads its dataset through the :world handle, not a :payload snapshot. A generated shell-company ledger (or any non-trivial dataset) is large and live: exactly the case the enrichment contract reserves for :world (a handle to the cart-installed mission-data region), not a copied :payload blob (enrichment-contract.md §3–§4):

(launch-app :ambercalc
:world ledger-topology ; handle to the live mission-data region (the ledger)
:payload '(:view register :threat 3)) ; small params only: initial view, scale
  • :world is the dataset. AmberCalc reads and navigates the region live: a node a cart reveals stays revealed, and AmberCalc sees mission-state changes as they happen. The mission-data region is run-state, never durable: it resets at the mission-instance boundary, and nothing AmberCalc does to the region touches UDS.
  • :payload carries only small launch parameters: the initial view, the display scale, a threat level, never the dataset itself.

Supersession (explicit). This supersedes the walkthrough/stub shorthand (launch-app :ambercalc :payload ledger-dataset), which passed the whole dataset as a payload blob. That framing contradicted the enrichment contract’s :payload (small params) / :world (live region handle) split; the dataset now arrives via :world, and the stub’s :payload open question is closed. A program launched bare (no cart context) gets the empty/default world and still runs; enrichment never gates (ADR-0042).

Export to DOSSIER: fact-node via dossier-commit (resolves stub Q3)

Section titled “Export to DOSSIER: fact-node via dossier-commit (resolves stub Q3)”

A “finding” is a DOSSIER fact-node, not a free write. AmberCalc (and the Black Ledger vocabulary on top of it) never raw-writes DOSSIER; it calls the sanctioned (dossier-commit <key>) primitive and the engine refiles the fact under the matching profile (enrichment-contract.md §5–§6, dossier-data-model.md §4). This is the same sanctioned-write boundary mission outcomes use (ADR-0040 §6): durable consequence happens only through key emission, never through touching the world region.

A finding carries the canonical fact-node fields (dossier-data-model.md §3):

FieldWhat AmberCalc supplies
:keythe typed, addressable key id: e.g. acct:shell:caribbean-trust:beneficiary
:valuethe datum (the amount, the account, the name)
:provprovenance: (:mission … :via ambercalc :phase analyze)
:confconfidence: :rumored / :inferred / :verified, set by trace depth
:linkstyped edges: :routes-to, :owns, :contradicts, …

dossier-commit lands the fact in the DOSSIER inbox; the engine auto-refiles it under the profile its :key type names (or holds it for the operator to refile). AmberCalc cannot create profiles or write canonical intel directly: it reports a discovery and the engine commits it.

  • CAR / CDR: keyboard cell motion. CAR descends into the head (drill a cell / a reference), CDR moves to the next sibling (next cell / row), BACK ascends (pop the nav stack). These reuse the canonical Lisp-primitive key semantics verbatim, so AmberCalc cell-diving and Black Ledger account-tracing share the same dive/pop core.
  • GoTo: jump-by-coordinate (@r$c) for long-distance motion on large grids, in preference to scrolling.
  • Trackpoint: aim the cursor at a cell or column and click to dive (VisiData’s two-cursor feel), complementing CAR/CDR. AmberCalc consumes the v0.1 trackpoint cart-FFI per ADR-0035: (cursor-position) to read the cell under the cursor, (on-trackpoint-move …) to react to cell crossings, (on-trackpoint-click …) to react to a click. The cursor is in cell coordinates on the main grid and is clamped to the content area (Rows 1–73): it never lands on the firmware-owned Row 0 / Row 74. v0.1 is a single merged cursor with a primary click; per-pointer differentiation is deferred (ADR-0035 / ADR-0032 v2).
  • Launch: (launch-app :ambercalc :world <region-handle> :payload '(:view … :scale …)) — NoshAPI Tier 1 (ADR-0005, amendment 2026-06-21). :world is the live mission-data region (the dataset); :payload is small params only.
  • Export: (dossier-commit <key>) — sanctioned fact-node deposit (ADR-0005, amendment 2026-06-21).
  • Trackpoint: (cursor-position) / (on-trackpoint-move h) / (on-trackpoint-click h) — cell-coordinate cursor on Rows 1–73 (ADR-0035).
  • Display: draws on the cartridge/content rows (1–73) per the canonical grid; grid queried via the cell-API getters.
  • Authored in KEC Lisp. Program source lives at runtime/programs/ambercalc/ (ADR-0042 Decision-5).
  • Grid scale figures. Exact 1× vs 2× per-region scale (rows/columns visible at each) is an on-glass tuning detail (the stub’s Q4); deferred to bring-up.
  • Query language ownership. Whether AmberCalc exposes a first-party hledger-style filter language (amt:>N, status:, acct:) or Black Ledger contributes it as cart vocabulary on top of the grid. Tracked with the Black Ledger spec.
  • assert-balance / reconcile-by-assertion. The win mechanic and its exact FFI signature live in Black Ledger, not AmberCalc; AmberCalc only provides the navigable typed grid the cart asserts against.
  • 2026-07-18 tier migration: content re-cut under tier headings from the v1 design; no mechanics changed. Grid-scale/formula/coordinate detail moved behind the Tier 3 implementation seam; the identity and operations summary stayed at Tier 2. No design drift against ADR-0049 / ADR-0005 was found.
  • ../../plans/2026-06-25-ambercalc-blackledger-design.md (deleted 2026-07-29; recover from git history) §3 + Appendix A — the v1 design + TUI-spreadsheet research grounding (ses.el / Org-mode / VisiData / ledger-cli).
  • enrichment-contract.md — lens / world / :world-handle / dossier-commit contract (§3–§6).
  • dossier-data-model.md — the fact-node schema a finding commits as (§3–§6).
  • ../cartridges/modules/black-ledger.md — the forensic-accounting cart that enriches AmberCalc.
  • ../runtime/baselines/audit.md — the :audit mission baseline (the bare transaction generator Black Ledger supersedes).
  • ADR-0042 — the thirteen first-party programs; carts enrich, never gate.
  • ADR-0005 — NoshAPI (launch-app, dossier-commit; amendment 2026-06-21). The AmberCalc formula/FFI ADR (ADR-0049) amends this surface for the SES formula model.
  • ADR-0035 — trackpoint cart-FFI (cursor-position / on-trackpoint-move / on-trackpoint-click, content-rows clamp).
  • ADR-0040 — the UDS sanctioned-write boundary dossier-commit respects.

Assigned by Josh in the kn86-inspo workbench. Full map: cart-inspiration-map.md.

SourceCategory
VisiCalc (1979)spreadsheet
VisiDataspreadsheet
csvlensspreadsheet
l123spreadsheet
sc-imspreadsheet
sheetsuispreadsheet