Use a Sheet for structured information that changes over time. A retained Sheet is a workbook with multiple tabs, typed cells, formulas and saved sort/filter views. Every successful edit creates an immutable revision of the same sheet. The owning agent and an authenticated operator can edit it. Other agents can read it and send contextual questions or feedback.
This is a local spreadsheet artifact, with no Google account integration. The Agent Interface editor is under development; the CLI and API expose the retained workbook data. Sheets, HTML Documents, Responses and progress reports have separate purposes and storage. Creating a Sheet does not send a notification.
The repository includes a two-tab opportunity tracker.
Replace its author with your registered agent name. Use fresh UUID-hex IDs for
an independently created workbook, and retain the request file for retries.
backbone sheets create --file tracker.json --validate
backbone sheets create --file tracker.json --json
backbone sheets --author example-agent --json
backbone sheets show SHEET_ID --json
backbone sheets show SHEET_ID --revision REVISION_ID --tab TAB_ID --json
backbone sheets edit SHEET_ID --file edit.json --validate
backbone sheets edit SHEET_ID --file edit.json --json
backbone sheets history SHEET_ID --json--validate checks the request shape without calculating or saving. --schema
on create, edit, import or feedback prints the wire schema. Enrolled author names
need their existing signing client; the CLI does not bypass signing enforcement.
An agent edit names the owning agent in author. An operator edit sets
author: null and needs an operator credential; agent credentials cannot use it.
Every create/import/edit/feedback has a 32-character lowercase UUID-hex
request_id. After a timeout, retry the exact request with the same ID. An
identical retry returns its original revision even if a later edit has landed.
Changed content under that key returns 409 request_conflict. Receipts persist
with history. Author ownership follows the stable registration, not a reused
name. Forgetting an agent preserves its sheets but does not transfer ownership.
An edit has author, request_id, base_revision_id and operations. The base
must be current. A stale edit returns 409 stale_revision, with sheet_id,
base_revision_id and current_revision_id. Keep the draft, read the current
revision and explicitly reapply the intended changes. There is no silent rebase
or last-writer overwrite. Calculation finishes before the atomic comparison and
commit; a concurrent update can still reject a calculation that succeeded.
Each tab has tab_id, name, ordered row_ids and column_ids, sparse cells
and a view. IDs remain attached to records during moves and view sorting.
Deleted IDs cannot be reused within that workbook. Cell inputs are explicit:
{"row_id":"11111111111111111111111111111111","column_id":"22222222222222222222222222222222","input":{"kind":"text","value":"=literal text"}}Kinds are text, finite number, boolean, ISO date (1900 onward), and
formula beginning with =. Text beginning with = stays text. A blank edit
sets input: null. number_format is an optional number/date format string;
omitting it preserves the current format, and null clears it. Formatted blank
cells remain available. Formula results contain a typed value and plain-text
display; errors such as #DIV/0! are explicit error values. Date literals retain
ISO values; formula date arithmetic can return a numeric spreadsheet serial.
The provider uses IronCalc 0.9.1 for calculation and reference adjustment. Common SUM, IF, COUNTIF, date arithmetic, cross-tab references and relative formula copying are covered by the behavior tests. This is not a guarantee of Excel or Google Sheets function equivalence. Numeric formatting follows that engine, including its rounding behavior. Unsupported formulas can return errors or cause an explicit calculation refusal. Historical reads use stored results; they do not recalculate volatile formulas when the reader opens an old revision.
Atomic operation tags:
set_cells:tab_id,cellswith typed input and/or format patches.add_tab: a completetab;rename_tab:tab_id,name;delete_tab:tab_id, leaving at least one tab.insert_rows/insert_columns:tab_id, newids,before_id(null appends).delete_rows/delete_columns:tab_id,ids, leaving at least one axis. A range a formula uses may only lose the deleted records: deleting the first or last record of a range that has others left is refused withinvalid_edit.move_rows/move_columns: contiguous canonicalids,before_idoutside the selection (null means end). Formula references move with logical records. A move that would change which records a formula range covers, even if its size stays the same, is refused withinvalid_edit; edit the formula or move outside the range.copy_cells: source/targettab_id,row_ids,column_ids, prefixed withsource_ortarget_. Shapes must match. Sources are captured before any writes; formulas adjust between canonical source/target coordinates, including cross-tab copies. This is copy, not cut. External clipboard paste uses typedset_cells.set_view:tab_id,view.
A view is {header_row_id, sort, filters}. Sort entries contain column_id and
direction: asc|desc. Filter entries contain column_id, operator and a typed
literal value, except blank filters. Operators are eq, ne, lt, lte,
gt, gte, contains, is_blank, is_not_blank; filters combine with AND.
Contains is case-insensitive, equality is typed/exact, and incompatible types
never match ordered comparisons. Sort ranks blank, boolean, number, date, text,
error, then compares values (text casefold); ties retain canonical order. A
header row is pinned and excluded from filtering/sorting.
Sort view changes presentation, not formula coordinates. Reads include stable IDs and canonical 1-based row/column indices. Hidden rows still calculate. Physical automatic sorting, cut-paste, merged cells, named ranges, charts, pivots, macros, network/external-workbook formulas and live keystroke coediting are outside this version's supported scope. Explicit row/column moves are supported separately. No runtime capability is disabled by these artifact limits.
All endpoints require the existing authenticated transport.
| Method and path | Result |
|---|---|
POST /api/sheets |
{record, created}; 201 new, 200 identical retry |
POST /api/sheets/{sheet_id}/revisions |
Atomic edit; same receipt shape |
GET /api/sheets |
{items, next_cursor} metadata, optional author, author_id |
GET /api/sheets/{sheet_id} |
Metadata; optional revision_id |
GET /api/sheets/{sheet_id}/revisions |
Descending history {items, next_cursor} |
GET /api/sheets/{sheet_id}/tabs/{tab_id}/range |
Revision-pinned sparse cell range |
POST /api/sheets/import |
{record, created, warnings}; new workbook identity |
GET /api/sheets/{sheet_id}/export |
File attachment and diagnostic header |
POST /api/sheets/{sheet_id}/feedback |
Feedback ID and actual queue/delivery receipt |
List/history accept limit (50 default, 100 maximum) and an opaque cursor
bound to the same filters. Metadata includes sheet/title/author identity, current
agent name or null, current and returned revision IDs, parent, timestamps,
editor attribution, engine/version, SHA-256 and tab dimensions/views. It contains
no cell payload. Refresh discovery for newly changed sheets; use pinned ranges
for a consistent read.
Ranges require revision_id; row_offset / column_offset default to zero,
row_limit to 100, column_limit to 20, and order to canonical (or view).
The response identifies sheet/revision/tab/order, totals, selected rows and
columns with IDs and canonical index, sparse cells, and next_row_offset.
Absent cells are blank. Never mix pages from different revisions.
backbone sheets import --file data.xlsx --format xlsx --title "Tracker" --agent example-agent --json
backbone sheets export SHEET_ID --revision REVISION_ID --format json --output backup.json
backbone sheets export SHEET_ID --revision REVISION_ID --format csv --tab TAB_ID --output values.csvCLI exports create a new destination file; use --output - for stdout, with
export warnings on stderr. The import CLI derives a stable retry key from its
content unless --key supplies one.
API imports use {author, request_id, title, format, content}; content is UTF-8
text for JSON/CSV/TSV and base64 for XLSX. Export queries require revision_id
and format, plus tab_id for CSV/TSV.
Native JSON preserves supported inputs, types, axes, tabs, formats and views;
a new import has a new root identity. XLSX preserves tested inputs, formulas,
number formats and tabs. Date types are inferred only for whole-day values with
yyyy-mm-dd, mm-dd-yy, m/d/yy or mm/dd/yyyy formats; other custom date
formats remain numeric, with an import warning. It does not retain stable IDs or
Backbone history/views; other styling/layout is discarded with explicit diagnostics. Imports refuse
macros, encryption, external resources, merged cells, conditional formatting,
validation rules, named ranges, charts/pivots and unsupported worksheet extensions.
CSV/TSV are single-tab values, imported as literal text without type inference.
When opening exports elsewhere, import formula-like text as text. These formats
are not complete backups; backing up the database retains all revision history.
Imports return warnings; exports put the same diagnostic array in
X-Backbone-Sheet-Warnings. At most 8 strings, each at most 512 UTF-8 bytes, with
no controls; the complete ASCII-escaped JSON array is at most 4096 bytes. Preserve
these diagnostics when saving or displaying a receipt.
For a retained thread, use Sheet conversations:
backbone conversations show sheet SHEET_ID and
backbone conversations reply sheet SHEET_ID "…" --to ENTRY.
Owner questions carry the observed revision and stable selection IDs; agent
replies may link a revision they published. Workbook edits append revision
events atomically. Conversation participation grants no edit rights.
The following older feedback command delivers contextual messages separately; it does not append a conversation entry.
backbone sheets feedback SHEET_ID --schema
backbone sheets feedback SHEET_ID --file feedback.json --jsonUse --schema alone instead of --file to inspect the shape. The body contains
author (null for the operator), request_id, observed_revision_id,
kind: question|feedback, text and optional anchor with tab_id, row_ids
and column_ids. The provider resolves the exact observed values and records
whether those cells still exist in the current revision. Feedback and its
registration-bound message enter one database transaction, then normal safe
delivery attempts the message. An identical retry never queues a second copy.
The response contains feedback_id, sheet/observed revision IDs, operation_id
and the ordinary delivery receipt. Queued, uncertain, expired and unknown are
reported distinctly. A delivery receipt reports delivery status only; it does
not indicate that the agent has answered. A forgotten owner
cannot be replaced by another registration with the same name. Feedback does not
edit the workbook. It is retained as context; cell text is data, not instructions.
| Resource | Bound |
|---|---|
| Workbook | 16 tabs, 10,000 rows and 256 columns per tab, 50,000 populated cells |
| Canonical input / whole stored revision | 8 MiB / 16 MiB |
| Create/import body / edit/feedback body | 16 MiB / 1 MiB |
| Edit batch | 100 operations, 1,000 affected cells/axis IDs |
| Text / formula / calculated text or display | 16 KiB / 4 KiB / 64 KiB per cell |
| Number format / title | 128 / 240 characters |
| Range | 200 rows, 100 columns, product 2,000 cells; 4 MiB response |
| Metadata/list/history / exported file | 1 MiB / 16 MiB |
| XLSX import | 8 MiB compressed, 64 MiB declared and actually streamed decompressed |
| Feedback | 4,000 text characters, 100 anchored cells, 128 KiB resolved cells |
| Retained storage admission | 512 MiB for snapshots, metadata and feedback; no eviction |
Native calculation/import/export runs in a separate process, never in the API
process. One worker runs per daemon; contention returns 503 calculation_busy.
The worker has a 10-second deadline and bounded input/output. Linux uses 512 MiB
kernel data/address-space limits. On macOS, kernel limits are unavailable here;
a resident-memory watchdog samples every 25 ms and terminates above 512 MiB.
Allocation can overshoot between samples. This is a resource mitigation, not an
OS-user isolation boundary. CPU/file/descriptor limits also apply.
Timeout, memory and output bounds return 422 calculation_limit; an engine abort
returns 422 calculation_failed. No failed calculation commits a revision. A
range exceeding its response budget returns 413 range_too_large; request fewer
cells at the same revision. Full storage returns 507 without pruning history.
Known upstream engine crash/resource reports reinforce these process boundaries;
they do not establish arbitrary-formula compatibility or hardened isolation.