Advance cancellation revisions and document data key recovery
3 files changed,
+8
−2
AGENTS.md+2 −0internal/calendar/release_repairs_test.go+5 −0internal/calendar/store_event_write.go+1 −2
CI passed — run details
diff --git a/AGENTS.md b/AGENTS.md| index 7561bf2..a54894b 100644 |
| --- a/AGENTS.md |
| +++ b/AGENTS.md |
| @@ -259,3 +259,5 @@ reviewed. A review by a weaker model than the author is not a review. |
| ๐ค / ๐จ markers, `Co-Authored-By:` trailers. AI is a guest with a name |
| tag: opt-in, disclosed, and every path works with it switched off. |
| + |
| +Startup verifies the configured data key against `meta.sealed_key_check_v1`, or existing encrypted calendar names on first adoption. A refusal requires restoring the correct key/database pair. Never delete or replace the sentinel to make an unreadable database start. |
diff --git a/internal/calendar/release_repairs_test.go b/internal/calendar/release_repairs_test.go| index d62d616..943700b 100644 |
| --- a/internal/calendar/release_repairs_test.go |
| +++ b/internal/calendar/release_repairs_test.go |
| @@ -57,6 +57,11 @@ func TestFeedExportsOneMasterAndAllOverrides(t *testing.T) { |
| if err != nil { |
| t.Fatal(err) |
| } |
| + for _, row := range rows { |
| + if row.ID == overrideID && row.Sequence <= master.Sequence+1 { |
| + t.Fatal("cancellation did not supersede the live override") |
| + } |
| + } |
| if !strings.Contains(WriteICS("Test", rows, false), "STATUS:CANCELLED") { |
| t.Fatal("deleted override resurrected") |
| } |
diff --git a/internal/calendar/store_event_write.go b/internal/calendar/store_event_write.go| index b703f44..b924fec 100644 |
| --- a/internal/calendar/store_event_write.go |
| +++ b/internal/calendar/store_event_write.go |
| @@ -135,11 +135,10 @@ func (s *Store) DeleteEvent(ctx context.Context, personID, eventID int64) error |
| // told about, and its CANCEL ICS must outrank the REQUEST, so |
| // the soft-deleted row takes one more revision (booking spec |
| // ยง5.5). |
| - updates := map[string]any{"deleted_at": now} |
| + updates := map[string]any{"deleted_at": now, "updated_at": now, "sequence": gorm.Expr("sequence + 1")} |
| if _, booked, err := bookingFor(tx, eventID); err != nil { |
| return err |
| } else if booked { |
| - updates["sequence"] = gorm.Expr("sequence + 1") |
| if err := cancelBookingsForEvent(tx, eventID, now); err != nil { |
| return err |
| } |