oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1# Product
2
3<!-- impeccable:product-schema 1 -->
4
5## Platform
6
7web
8
9## Users
10
11**Confirmed by Paul, 2026-09-01: Paul and his collaborators, dogfooding.**
12Real use, small N. Design for the people actually in it rather than a
13hypothesised market — a feature nobody here uses is not evidence of
14anything.
15
16They are running the CARLOS suite already, so Sheets is not entered cold:
17a person arrives from the suite home with a session, and the spreadsheet
18is one of several apps on one instance. The job is the ordinary one —
19put numbers in a shared grid, have the other person see them — under a
20storage rule that is stricter than the ordinary one.
21
22Two roles exist beyond the team: a **guest** holds no team-wide rights
23and sees exactly the sheets granted to them; a **link viewer** is
24anonymous, read-only, and mints no membership at all. Both are real
25audiences for the read surfaces even though neither is the primary user.
26
27## Product Purpose
28
29A spreadsheet for one team, on that team's own instance, whose contents
30are sealed at rest.
31
32Success is that a team can do its actual spreadsheet work — grid editing,
33formulas, live collaboration, import and export — without the storage
34layer ever holding a plaintext cell, and without the product lying to
35them about what that does and does not protect.
36
37## Positioning
38
39Three claims a neighbouring product could not truthfully copy, all
40confirmed binding by Paul on 2026-09-01:
41
421. **Sealed at rest, honestly scoped.** The bucket never holds a
43 plaintext cell. The instance holds the key while it runs, so a
44 compromised live host *can* read an open sheet — and the README says
45 so. **We never claim "we can't read your data."** The UI must never
46 imply otherwise, in copy, in iconography, or by omission.
472. **One instance per team.** Sharing is membership across teams: the
48 sheet never moves, and an outside person becomes a scoped member of the
49 team that owns it. There is no federation story across teams and there
50 will not be one. Within a team, Sheets is one of several apps sharing
51 one trust domain (suite ruling 1), and a same-team sibling may pull
52 team-visible sheets under the suite contract.
533. **No affordance is script-required.** Sign-in, the sheet list, sharing
54 and membership, settings, version history, comments, import and export
55 all work scriptless, as does a paginated table view of any sheet with
56 per-cell edit forms. The grid editor is an enhancement on top of a
57 floor, not the floor.
58
59## Operating Context
60
61- **One binary per instance**, `CGO_ENABLED=0`, cgo-free SQLite,
62 deployed to CARLOS. Review happens on a deployed canary, never
63 localhost.
64- **The suite home is a separate product in a separate repo.** Sheets
65 depends on it for identity and consumes a five-call contract; it does
66 not define it. Passkeys enroll at the home's origin only — instances
67 never enroll them.
68- **Sheets sits beside Docs and the home** under the Oficina suite, which
69 shares a design system (`estilo`). Chrome that appears in more than one
70 app is the suite's, not Sheets'.
71- Live collaboration is cell-level last-write-wins over SSE. Version
72 history replays inputs and recomputes; derived values are never logged.
73
74## Capabilities and Constraints
75
76**Built and working as of 2026-09-02:** sign-in, sheet list and creation
77with a live preview thumbnail on every row, a grid backed by a Go calc
78engine, live sync across tabs over SSE, a scriptless paginated table
79view with per-cell editing, XLSX import and export (import by upload or
80by dragging a file onto the list; it runs as a background job and lands
81on the new sheet), and a contrast notice for imported fills.
82
83The editing surface is a menu bar (File, Edit, Insert, Format, Data,
84Help), a formatting toolbar, a name box and formula bar, and a tab
85strip. Cells carry bold, italic, alignment, a number format, a fill and
86a text colour; columns and rows can be resized and stay resized. A
87colour picked here is stored as hue and chroma with no lightness, so it
88follows the reader's light/dark choice; a colour an imported file
89authored is painted exactly as the file wrote it.
90
91**Not reachable yet, though the code is there.** This line previously
92read "XLSX/CSV import and export", which was true of the converter and
93false of the product. Import stopped being an example of that on
942026-09-02 and **export stopped being one the same day** — `xlsx.Export`
95now has a route and File ▸ Download as .xlsx reaches it. What remains
96true is CSV: there is no CSV code in the repo at all, and the line above
97does not claim any.
98
99**Constraints that bind design:**
100
101- **No build step.** Jspreadsheet CE v5 is vendored as a static asset —
102 no CDN, no bundler. Its own formula engine is disabled; Go owns calc.
103- **300-line cap on any JS module we write**, test-enforced, ratchet-down
104 only. The vendored grid blob is exempt and named as such.
105- **Light/dark is per person, not per document** (confirmed binding,
106 2026-09-01), and must resolve before first paint from a cookie read
107 server-side. Cell fills resolve against the theme, so a late preference
108 repaints every coloured cell. Never a fetch.
109- **Every drag-only affordance has a menu equivalent.**
110- **No cell content, sheet title or tab name in any log line, error
111 string, job status or metric label.** Errors carry coordinates and
112 codes. This constrains error copy directly.
113- Cell values are IEEE doubles end to end; currency is a number format,
114 not a type.
115- Cell fill is stored as OKLCH intent; an imported hex is retained as
116 authored, even when it fails contrast (see Accessibility).
117- Link sharing is viewer-or-commenter, **never editor** — an anonymous
118 editor would write unattributed rows into version history.
119
120**Explicitly undecided:** the suite's own domain (the passkey relying
121party is provisionally an `oncarlos.com` host). Sheets' own visual world
122beyond the suite theme — see Brand Commitments.
123
124## Brand Commitments
125
126- **Name:** Sheets, within the **Oficina** suite (CARLOS).
127- **Design system:** `rastrillo` (the framework) with **`estilo`** as the
128 suite's shared variant. estilo's theme *is* rastrillo's `day` — not
129 derived from it, identical — until a **named need** says a token has to
130 differ. "It would look better" is explicitly not such a need; the bar
131 is "Sheets' grid requires a denser row token that day does not
132 declare". Any divergence is recorded as a decision, not applied.
133- **Markup grammar:** the attribute spelling (`rst-btn="primary compact"`),
134 never the class spelling. Utilities stay classes. Adopt per screen,
135 never per element.
136- **Voice:** plain, exact, and never reassuring beyond the facts. The
137 repo's own prose is the reference — it names its residual risks in the
138 same breath as its guarantees.
139- **Authorship is disclosed.** 🤖 / 👨 markers, published prompt and
140 carbon ledgers. Every path works with AI switched off.
141
142## Evidence on Hand
143
144- `docs/superpowers/specs/2026-08-30-sheets-design.md` — the design spec.
145 §1 is a dated, attributed settled-decisions table with the rejected
146 alternative beside each ruling. §2 is the leak list. §14 is the
147 deferred table, each row carrying a written trigger.
148- `AGENTS.md` — binding engineering rules, several of them design-facing.
149- A working deployment at `https://sheets.bds.oncarlos.com`.
150- `tools/drive/drive.mjs` — drives the real binary in a real browser,
151 for live sync across two tabs.
152- `tools/drive/chrome.mjs` — its sibling, for the editing chrome: the
153 menu bar, the toolbar, the formula bar, the cell paint, export, and
154 the 320px reflow budget. It earned its place on the day it was
155 written, by finding a menu bar that worked perfectly from the keyboard
156 and not at all with a mouse.
157
158**Absences future work must not fabricate:** there are no customers, no
159testimonials, no benchmarks, no pricing and no usage numbers. There is no
160market claim of any kind. Sheets is being dogfooded by its author and his
161collaborators, and nothing in the product may suggest more than that.
162
163## Product Principles
164
1651. **Say the true thing, including the unflattering half.** The trust
166 model is a genuine differentiator *and* has a named residual risk. Copy
167 that states the first without the second is a defect, not a
168 simplification.
1692. **The scriptless path is the product, not the fallback.** If an
170 affordance exists in the grid and not in the table view, the table view
171 is unfinished. "Fallback" is the wrong word and invites the wrong
172 design.
1733. **One instance, one team, no federation.** Never design a flow that
174 implies data can move to another instance, or that a person outside
175 this team is a first-class participant.
1764. **The chrome is the suite's; the grid is ours.** Anything that appears
177 in Docs and the home too belongs to `estilo` and should look identical
178 there. A spreadsheet's menu bar and formula bar are Sheets'.
1795. **A preference resolves before first paint or it is a bug.** This is a
180 correctness rule that happens to be about design.
181
182## Accessibility & Inclusion
183
184- **WCAG AA (4.5:1) is the floor for text**, and it is test-enforced
185 against the theme's own pairs.
186- **A retained imported fill may fail that floor, and that is correct.**
187 Rendering an imported file as authored is the ruling; silently
188 correcting a colour would lie about the document. The obligation that
189 follows is to *say so* — a per-cell marker plus a notice — and leave
190 the decision to the person. Never quietly recolour a cell.
191- **The page header's rule is decorative and carries no contrast floor.**
192 It is deliberately absent from rastrillo's contrast pair table; a later
193 pass must not argue it up to 1.4.11's 3:1.
194- **The theme switcher must work with scripting off** — a person who
195 needs a high-contrast theme is not well served by being told to enable
196 JavaScript first.
197- Three light/dark states, not two: "follow the machine" is a real
198 preference.
199