oficina / fichas Public

Record Paul's rulings: retention, restore, seal tiers, reach

Six rulings from 2026-09-19.

Versioning: "versioned means we can see changes". Revision 1 said "no
snapshots, restore is a different feature", which was muddled — any prior
state reconstructs from a complete diff chain, so snapshot-versus-diff is a
storage choice, not a capability. The real constraint is that restore
requires an unbroken chain and erasure requires breaking it. So values are
kept for a bounded window, restore lives inside that window, actor and
changed-field-names are kept forever, and erasure tombstones payloads and
says plainly which versions are no longer restorable. A silently broken
chain that still offers a restore button reconstructs a wrong object and
looks authoritative doing it.

Seal tiers are per field definition and configurable in the app, defaulted
by field type and changed by migration rather than toggle — a per-team tier
setting would make the leak list per-team and unstateable.

Reach: source-enforced "offer to fichas" marks, granted-only in v1, reached
passages excluded from model prompts, no hand-off of answers built on them.

The seal-tier default table is now the only thing blocking slice 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 4d0e6531cf99967e02cf181aa0799984e03f6560 parent fab37e4
1 file changed, +69 −20
  • docs/superpowers/specs/2026-09-19-fichas-design.md +69 −20

No CI script — run details

CI log
amadan: no CI script — add an executable .amadan/ci or a `ci` target to the Makefile
diff --git a/docs/superpowers/specs/2026-09-19-fichas-design.md b/docs/superpowers/specs/2026-09-19-fichas-design.md
index 7e018b8..9edaaa2 100644
--- a/docs/superpowers/specs/2026-09-19-fichas-design.md
+++ b/docs/superpowers/specs/2026-09-19-fichas-design.md
@@ -27,7 +27,8 @@ been ruled on.** Do not read a proposal as a ruling.
| # | Decision | Rejected | Who, when |
|---|---|---|---|
| 1 | **Fichas is a fifth per-team instance app**, own DEK, own origin, beside docs, sheets, calendar and memoria | Cards as a memoria feature, which would make memoria an app of record and break its decision 7; cards as a Sheets view, where the unit of record is a cell in a grid and a ficha's is a typed record with relations | Paul, 2026-09-19 |
-| 2 | **Every change to every object is logged: what changed, and who did it** | — | Paul, 2026-09-19 |
+| 2 | **Every change to every object is logged: what changed, and who did it.** "Versioned" means *see* changes, so values are kept for a bounded window and restore lives inside it (§5) | Restore forever, which requires an unbroken diff chain that erasure must be able to break | Paul, 2026-09-19 |
+| 2a | **Seal tiers are per field definition, configurable in the app** — but defaulted by field type, and changed by migration rather than by toggle (§4) | A per-team tier setting, which makes the leak list per-team and unstateable | Paul, 2026-09-19 |
| 3 | **DabbleDB is the ancestor, not Access.** Steal schema-by-refinement, entity promotion and views-as-objects. Refuse the generic-database-builder positioning | "Access for Oficina" — the framing whose best execution (DabbleDB) still found no market | Paul named both ancestors 2026-09-19; the positioning conclusion is **proposed** |
| 4 | **Cards and entries are different objects.** A card is a mutable entity; an entry is an append-only event belonging to a card | One object type with a flag, which is what Hypercard and Access both did and why their lifecycles blur | **Proposed** |
| 5 | **A ficha holds a reference — `(app, instance, item)` — never a copy** | Copying sibling content into fichas, which makes fichas a second app of record for content it did not author | **Proposed** |
@@ -94,9 +95,24 @@ print. *"Notes containing X"* needs FTS5 over content the index can read —
which is the same decision memoria §7 already made for its chunk store, and
it is the expensive one.
+**Is the tier configurable per schema, in the app?** Paul, 2026-09-19:
+yes — the tier is a property of the field *definition*. Three caveats that
+make it rarely-configured rather than a setting:
+
+- **Changing a tier after data exists is a migration, not a toggle.** Sealed
+ → plaintext decrypts every card and rewrites it in the clear,
+ retroactively, for data entered under a different promise. It must look
+ like a migration with a warning.
+- **Per-team configurability makes the leak list per-team.** memoria's §3.2
+ works because "what is in the bucket" has one answer for everyone and can
+ be stated once. Per-team tiers mean no README can state it.
+- **So the default follows from the field's type**, stated once: dates and
+ enums plaintext (ranges and grouping are needed; content value is low),
+ identifiers as prints, free text sealed.
+
**Get this list right and the storage engine is an implementation detail.
-Get it wrong and no engine helps.** This list does not exist yet and slice
-1 cannot start without it.
+Get it wrong and no engine helps.** The per-type default table does not
+exist yet and slice 1 cannot start without it.
## 5. The event log — versioning, audit and S1's feed are one table
@@ -126,16 +142,38 @@ calls deletion "the hard case"; ruling 14 makes retirement stated and
timed. **A card that is deleted while the log still holds its old values
makes "delete" a lie.**
+**Revision 1 of this file said "no snapshots, restore is a different
+feature". That was muddled and Paul caught it.** Any prior state
+reconstructs from a complete chain of field-level diffs, so snapshot-versus-
+diff is a storage choice and not a capability. The real constraint is
+narrower and it is the whole of the deletion problem:
+
+> **Restore requires an unbroken chain. Erasure requires breaking it.**
+
+Concretely: a person asks to be removed, their Customer card is deleted, and
+their name, notes and call history persist in the log's sealed payloads. The
+delete removed current state; the log kept the history. **Fichas holds notes
+about named third parties who never signed up**, which is why this bites
+harder here than in Sheets.
+
+So, ruled by Paul 2026-09-19 — *"versioned means we can see changes"*:
+
- **Kept permanently:** kind, object, actor, timestamp, **which fields
changed**. Survives the object's deletion — tombstone the payload, keep
the actor, because *"who deleted this"* is the question an audit log most
needs to answer.
-- **Kept for a bounded window:** sealed before/after **values**. memoria's
- breaker counters are the precedent at 8 days, and that spec calls the
- retention "load-bearing rather than tidy".
-- **Not kept:** full prior-state snapshots. Restore-to-version is a
- different feature, is the expensive one, and is the one that makes
- deletion undeliverable. **Not requested; do not build it by accident.**
+- **Kept for a bounded window:** the sealed before/after **values**. This is
+ the "see changes" requirement, and it works for as long as anyone
+ realistically looks. memoria's breaker counters are the precedent at
+ 8 days, and that spec calls the retention "load-bearing rather than tidy".
+- **Restore is available inside that window**, where the values still
+ exist — reconstructed from the diff chain, not from a stored snapshot.
+ Beyond it, a reader can see *that* a field changed and *who* changed it,
+ and cannot roll it back.
+- **Erasure tombstones payloads** and **states plainly that versions before
+ that point are no longer restorable.** A silently broken chain that still
+ offers a restore button is the failure to avoid: it reconstructs a wrong
+ object and looks authoritative doing it.
**This table is also S1.** An append-only log with an autoincrement id *is*
"an opaque position in the sibling's event log, issued by the sibling,
@@ -198,28 +236,39 @@ there and restated here only as they bind fichas:
## 10. Open
-- **NEEDS PAUL — the reach credential.** Fable recommends (a) + a
- sibling-side designation; Astra recommends (c); the spec author
- recommends the third path neither was asked about — **source-enforced
- standing "offer to fichas/memoria" grants**, for which memoria §3.5's
- published-only mode ("the sibling carries an explicit 'offer to memoria'
- mark that *it* evaluates") is the existing precedent. It needs no new
- credential and is not blocked on the home's identity layer. **Unruled;
- §9 and §6's S9 line both wait on it.**
-- **The seal-tier list (§4) does not exist.** Slice 1 is blocked on it.
+- **RULED 2026-09-19 — the reach credential is source-enforced standing
+ grants.** Not Fable's token-plus-designation and not Astra's person-proof:
+ **an explicit "offer to fichas/memoria" mark the sibling itself
+ evaluates**, for which memoria §3.5's published-only mode is the existing
+ precedent. Naming an asker then adds no read authority, so doctrine
+ §6.1's justification is restored verbatim rather than paid for, and it is
+ not blocked on the home's identity layer. See the reach draft §4.
+- **RULED — `granted` only in v1.** `private` is out of scope until there is
+ a second team. It is the HR-note class.
+- **RULED — reached passages are excluded from model prompts.** The team
+ owner's AI opt-in was given for team content; a label on the answer
+ discloses only what already happened. Excluding them is the one option
+ needing no new consent.
+- **RULED — a saved answer built on reached content cannot be handed off.**
+ Doctrine §7 already has the shape: "a source the receiver cannot check is
+ a source it must not accept."
+- **The seal-tier list (§4) does not exist.** **The last thing blocking
+ slice 1.**
- **Third-party personal data.** Fichas holds notes about people who never
signed up. That is a lawful-basis and subprocessor question, not a
retention one, and TensorX's zero-retention posture does not answer it
(`memoria/ZDR-TREAT-AS-LOCAL.md` §6). Not a blocker at one user; a
blocker at the second team.
-- **Whether fichas embeds at all**, or is keyword-only in memoria.
+- **Whether fichas embeds at all**, or is keyword-only in memoria. Paul,
+ 2026-09-19: no preference. Decide when there is content; keyword-only is
+ the cheaper default and §10's egress ruling points that way.
## 11. Deliberately not built
| Thing | Trigger |
|---|---|
| A type designer | A second stack asks for it, in use, not in principle |
-| Restore-to-version | Paul asks for it. §5 says why it is not free |
+| Restore beyond the value-retention window | Never, under §5. It requires an unbroken diff chain, and erasure must be able to break it |
| Reshaping by sentence — "split this column", "these are the same company" | **Slice 2. It is the thesis** (decision 3: the LLM is what replaces HyperTalk, and the reason this is not a 2008 rerun) — but it needs data to reshape, and there is none until slice 1 has been lived in |
| Editing an entry | Never as an edit. A correction is a new entry that supersedes (§3) |
| Cross-team anything | Never, under suite ruling 2 |