fix(memoro): decrypt DetailView fields + stamp createdAt on memo create

Two pre-existing bugs in the memoro module that became visible after
the Phase 5 LLM auto-title work landed. Both are independent of the
Phase 5 framework — neither was introduced by it — but the auto-title
was the first feature to systematically write to memo.title, which is
when the broken read path stopped hiding behind always-null titles.

Bug 1: DetailView shows ciphertext instead of plaintext

  apps/mana/apps/web/src/lib/modules/memoro/views/DetailView.svelte
  passed `useDetailEntity({ table: 'memos', ... })` WITHOUT setting
  `decrypt: true`. The crypto registry has memos.{title, intro,
  transcript} marked as encrypted, so the inputs were binding to
  raw `enc:1:Ghj1eJV0zz4PgfRL...` ciphertext strings instead of
  plaintext. Nobody noticed before because:

    - title was always null (no UI path to set it until Phase 5)
    - intro is rarely used
    - transcript was the only visible encrypted field, and the
      garbled `enc:1:...` string in the transcript area was apparently
      attributed to "broken transcription" rather than "broken read"

  Add `decrypt: true` to the useDetailEntity options. Same flag the
  other Mana modules already use for their encrypted DetailViews.

Bug 2: createdAt and updatedAt never set on memo records

  apps/mana/apps/web/src/lib/modules/memoro/stores/memos.svelte.ts
  create() built a LocalMemo object without populating createdAt or
  updatedAt. The LocalMemo type declares both as required strings,
  but TypeScript didn't catch the omission because the store relied
  on a TS Type assertion / partial-shape pattern.

  The Dexie creating hook in apps/mana/apps/web/src/lib/data/database.ts
  only auto-stamps userId + __fieldTimestamps — it does NOT auto-stamp
  createdAt. Module stores are expected to set their own timestamps
  (consistent with the todo, calendar, contacts, notes stores etc.).

  So every memoro memo had `createdAt === undefined`, and the
  DetailView's `new Date(memo.createdAt ?? '').toLocaleDateString('de')`
  rendered as "Erstellt: Invalid Date" for every single memo.

  Fix: set both timestamps explicitly in create() before the Dexie
  add. Existing memos in the wild are still broken — they'd need a
  one-shot migration to backfill createdAt from the
  __fieldTimestamps map, but that's a bigger commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-09 12:22:01 +02:00
parent fbb71f9366
commit ddde284f17
2 changed files with 17 additions and 1 deletions

View file

@ -29,6 +29,7 @@ export const memosStore = {
audioDurationMs?: number;
processingStatus?: LocalMemo['processingStatus'];
}) {
const now = new Date().toISOString();
const newLocal: LocalMemo = {
id: crypto.randomUUID(),
title: data.title ?? null,
@ -41,7 +42,15 @@ export const memosStore = {
isPublic: false,
blueprintId: data.blueprintId ?? null,
language: data.language ?? null,
};
// createdAt + updatedAt are required by LocalMemo's type but the
// previous create() never set them — DetailView showed
// "Erstellt: Invalid Date" for every memo. The Dexie creating
// hook only auto-stamps userId + __fieldTimestamps; module
// stores have to set their own createdAt/updatedAt explicitly
// (consistent with the rest of the Mana modules).
createdAt: now,
updatedAt: now,
} as LocalMemo;
const plaintextSnapshot = toMemo(newLocal);
await encryptRecord('memos', newLocal);
await memoTable.add(newLocal);

View file

@ -20,6 +20,13 @@
const detail = useDetailEntity<LocalMemo>({
id: () => memoId,
table: 'memos',
// title, intro, transcript live in the encryption registry — without
// `decrypt: true` the inputs would bind to raw `enc:1:...` ciphertext
// strings instead of plaintext. Pre-existing bug surfaced when the
// LLM auto-title started populating the title field for the first
// time on 2026-04-09; previously the field was always null and the
// transcript was the only encrypted field shown, but no one noticed.
decrypt: true,
onLoad: (val) => {
editTitle = val.title ?? '';
editIntro = val.intro ?? '';