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>
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 | |