Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
1d7284b
feat: add memory metadata repository foundation
nonnil May 15, 2026
dca9350
feat: expose memory metadata mutation APIs
nonnil May 15, 2026
f61ad7d
feat: add memory action UI primitives
nonnil May 15, 2026
1a62698
feat: wire browse memory actions
nonnil May 15, 2026
575cbf1
feat: manage taxonomy from right rail
nonnil May 15, 2026
d7085b8
feat: wire reader memory actions
nonnil May 15, 2026
a677e05
feat: add settings and OpenAI auth state
nonnil May 15, 2026
896a3e8
feat: require explicit reader highlight actions
nonnil May 15, 2026
38ce920
feat: add flashback section bookmarks
nonnil May 15, 2026
b274545
feat: relax imported media display policy
nonnil May 15, 2026
cad69b1
test: stabilize task 18 integration flows
nonnil May 15, 2026
8020e75
fix: keep browse loader database open
nonnil May 15, 2026
4275356
refactor: share segmented toggle button
nonnil May 15, 2026
1e8247c
refactor: unify right rail highlight lists
nonnil May 15, 2026
8627b4f
fix: show check icon for read memories
nonnil May 15, 2026
0c9e3ab
fix: clarify memory action menu hover
nonnil May 15, 2026
f1ffbd5
Merge remote-tracking branch 'origin/workflow18-read-status' into fea…
nonnil May 15, 2026
2b15ed7
fix: shorten highlight tab label
nonnil May 15, 2026
e66e649
fix: align task 18 review followups
nonnil May 16, 2026
6e2e7af
merge workflow18 product language plan
nonnil May 16, 2026
0b17eb0
feat: migrate product language to flashbacks and moments
nonnil May 16, 2026
7527c96
fix: refine flashback navigation language
nonnil May 16, 2026
0643392
fix: use paint tool icon for theme tab
nonnil May 16, 2026
39281d7
fix: refine paint tool icon
nonnil May 16, 2026
5142a6d
fix: align theme nav icon scale
nonnil May 16, 2026
7413687
fix: persist moment content hash
nonnil May 16, 2026
89b9fed
docs: plan memory delete hardening
nonnil May 16, 2026
e126f4e
docs: plan task 18 review followups
nonnil May 16, 2026
a96552b
fix: harden memory deletion flow
nonnil May 16, 2026
31318c3
feat: describe backup commit actions
nonnil May 16, 2026
d2fbcf2
fix: align task 18 review followups
nonnil May 16, 2026
7fdfffd
fix: harden task 18 review followups
nonnil May 16, 2026
3d90811
fix: harden browser capture imports
nonnil May 16, 2026
1dfebb4
fix: delegate readable extraction to defuddle
nonnil May 17, 2026
2994b13
fix: harden review follow-up consistency
nonnil May 17, 2026
6f6dfd2
fix: address memory action review threads
nonnil May 17, 2026
735547d
fix: address memory action review follow-ups
nonnil May 17, 2026
4db27e2
fix: wire reader moment actions
nonnil May 17, 2026
c2d1a98
fix: address memory action review follow-up
nonnil May 17, 2026
ed47a3d
fix: address moment review follow-up
nonnil May 17, 2026
100c4ce
fix: complete moment toggle actions
nonnil May 17, 2026
efa396a
fix: prevent partial browser captures
nonnil May 17, 2026
c860c72
fix: address task 18 review followups
nonnil May 17, 2026
99cbad3
fix: address remaining task 18 review
nonnil May 17, 2026
c53b1b1
fix: address backup and reader review followups
nonnil May 17, 2026
d6c69ca
fix: keep tag API helpers in server route build
nonnil May 17, 2026
71ff5aa
fix: address memory action review feedback
nonnil May 17, 2026
28818b6
fix: preserve content before memory delete backup
nonnil May 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ content, and git backup for the markdown store.

The foundation implementation is now more than scaffold. The current baseline
includes SolidStart/Bun runtime commands, Drizzle/SQLite persistence, markdown
content storage, add-memory import, memory browsing, reader routes, highlights,
content storage, add-memory import, memory browsing, reader routes, flashbacks,
git backup, backup failsafe recovery, Defuddle-based extraction, Tailwind
styling, and the local browser-assisted import extension.

Expand Down
6 changes: 3 additions & 3 deletions docs/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ from the foundation design and should stay aligned with it.
- [Overview](architecture/overview.md): runtime shape, module boundaries, and
dependency rules.
- [Data and storage](architecture/data-and-storage.md): SQLite metadata,
markdown store, highlight persistence, and ownership of canonical state.
- [Flows](architecture/flows.md): add memory, extraction fallback, highlight,
markdown store, flashback persistence, and ownership of canonical state.
- [Flows](architecture/flows.md): add memory, extraction fallback, flashback,
and git backup flows.
- [UI and routing](architecture/ui-and-routing.md): canonical routes, shell
layout, filters, highlight browse, composer, and reader behavior.
layout, filters, flashback browse, composer, and reader behavior.

## References

Expand Down
57 changes: 37 additions & 20 deletions docs/architecture/data-and-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Runtime tables:
- `categories`
- `memory_tags`
- `memory_categories`
- `highlights`
- `flashbacks`
- `backup_environment_stamps`
- `backup_failsafe_alerts`

Expand All @@ -70,11 +70,11 @@ one exists. Alert kinds distinguish path drift, missing backup repository,
remote push failure, and backup content inconsistency so the UI can offer only
the recovery actions that are safe for that condition.

## Highlight Model
## Flashback Model

Highlights are both metadata and content mutations.
Flashbacks are SQLite metadata rendered into reader HTML at read time.

`highlights` stores:
`flashbacks` stores:

- `id`
- `memory_id`
Expand All @@ -83,30 +83,47 @@ Highlights are both metadata and content mutations.
- `suffix`
- `start_offset`
- `end_offset`
- `content_hash`
- timestamps

`highlights.memory_id` is the canonical relation. API responses may shape this
as `memory.highlights: Highlight[]`, but memories should not store a separate
highlight ID array as source-of-truth state.
`flashbacks.memory_id` is the canonical relation. API responses may shape this
as `memory.flashbacks: Flashback[]`, but memories should not store a separate
flashback ID array as source-of-truth state.

Highlight browse and search views use `text`, `prefix`, `suffix`, and the
related memory title. The highlight table remains the canonical source for
highlight snippets; no separate denormalized highlight feed is introduced in
New flashback rows use canonical reader-text offsets. `content_hash` uses the
`sha256:<hex>` format and hashes the same canonical reader text used for offset
calculation after line endings are normalized to `\n`. If the current reader
text hash does not match the row, the reader must not render that flashback at a
guessed location.

Flashback browse and search views use `text`, `prefix`, `suffix`, and the
related memory title. The flashback table remains the canonical source for
flashback snippets; no separate denormalized flashback feed is introduced in
the initial design.

Persisted highlights are inserted into `CONTENT.md` as inline marks:
`CONTENT.md` is not mutated for normal flashback persistence. The reader applies
records as transient inline marks when rendering:

```html
<mark data-highlight-id="...">selected text</mark>
<mark data-flashback-id="...">selected text</mark>
```

The reader pipeline must allow `mark` and `data-highlight-id` while still
The reader pipeline must allow `mark` and `data-flashback-id` while still
sanitizing unsafe HTML.

Highlight removal uses the same text-range model as highlight creation. When a
user selects text that is already highlighted, only the selected range is
unhighlighted. Exact matches delete the corresponding `highlights` row and
remove the mark. Partial matches shrink the existing range or split it into
multiple remaining highlight ranges, each represented in SQLite and in
`CONTENT.md`. This prevents a nested or wider highlight from being removed when
the user intended to toggle off only a sentence or phrase.
Flashback removal uses the same text-range model as flashback creation. When a
user selects text that is already flashbacked, only the selected range is
unflashbacked. Exact matches delete the corresponding `flashbacks` row and
remove the rendered mark. Partial matches shrink the existing range or split it
into multiple remaining flashback ranges in SQLite. This prevents a nested or
wider flashback from being removed when the user intended to toggle off only a
sentence or phrase.

Because the built-in git backup does not back up SQLite directly, flashback
changes write a deterministic metadata export at:

```text
{storePath}/memories/{memoryId}/FLASHBACKS.json
```

That file is a backup/export artifact, not the runtime source of truth.
83 changes: 45 additions & 38 deletions docs/architecture/flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ Flow:
5. Write `{storePath}/memories/{memoryId}/CONTENT.md`.
6. Enqueue markdown backup work.

If extraction succeeds, save extracted title, body, description, favicon URL,
and markdown body.
If extraction succeeds, save extracted title, description, favicon URL, and the
markdown body produced by Defuddle.

If extraction fails or returns insufficient body content, still create a
link-only memory. Record extraction status and error details in SQLite.
If extraction fails or returns empty markdown, still create a link-only memory.
Record extraction status and error details in SQLite.

Raw HTML is not stored in the initial design.

Default extraction runs behind an interruptible runtime boundary. The import
timeout budget covers fetch, validation, parser work, and markdown conversion;
if the budget is exhausted, the importer returns link-only fallback instead of
persisting late extraction output.
timeout budget covers fetch, validation, Defuddle parser work, and Defuddle
markdown generation; if the budget is exhausted, the importer returns link-only
fallback instead of persisting late extraction output.

## Browser-Assisted Import

Expand All @@ -42,47 +42,49 @@ Flow:
4. The extension sends JSON to `/api/browser-import` on the local TRAUMA server
with a bearer token.
5. The server validates enablement, token, origin, content type, payload size,
URL shape, timestamp, and extracted text shape.
URL shape, timestamp, and captured snapshot shape.
6. The server creates the memory through the same add-memory persistence path:
SQLite metadata, `CONTENT.md`, and backup enqueue.
7. The extension opens the created memory route or reports the server error.

The extension is a privileged local client, not a trusted persistence layer. It
may provide browser-only access to the visible DOM, but final validation,
markdown creation, SQLite writes, and backup state remain server-owned. Raw
extension HTML is untrusted and must not bypass the server sanitization and
markdown-store contracts.
server-side Defuddle extraction, SQLite writes, and backup state remain
server-owned. Raw extension HTML is untrusted and must not bypass the server
sanitization and markdown-store contracts.

## Highlight
## Flashback

Reader content is not generally editable. Highlight creation is the only
content mutation exposed in read mode. The same selection gesture also toggles
off existing highlights.
Reader content is not generally editable. Flashback creation changes SQLite
metadata and transient reader rendering; it does not rewrite `CONTENT.md`. The
same selection gesture also toggles off existing flashbacks.

Flow:

1. User selects text in `/memories/:id`.
2. Frontend determines whether the selected range is already fully highlighted.
3. If the range is not already highlighted, the frontend renders an optimistic
highlight immediately.
4. If the range is already highlighted, the frontend optimistically removes
highlight styling only from the selected range.
2. Frontend determines whether the selected range is already fully flashbacked.
3. If the range is not already flashbacked, the frontend renders an optimistic
flashback immediately.
4. If the range is already flashbacked, the frontend optimistically removes
flashback styling only from the selected range.
5. Frontend sends selected `text`, `prefix`, `suffix`, `start_offset`, and
`end_offset` to the server with the intended toggle operation.
6. Server creates, deletes, shrinks, or splits `highlights` rows so SQLite
represents exactly the highlighted ranges that remain.
7. Server inserts or removes `<mark data-highlight-id="...">...</mark>` ranges
in `CONTENT.md` to match SQLite.
8. Server enqueues markdown backup work.

Highlight toggle rules:

- Selecting unhighlighted text creates a highlight for the selected range.
- Selecting an already-highlighted range unhighlights the selected range only.
- Selecting a subset of a larger highlight preserves the unselected highlighted
text by shrinking or splitting marks and metadata.
- Selecting across multiple existing highlights removes only the selected
overlap from each affected highlight.
6. Server resolves the selection against canonical reader text, stores
`start_offset`, `end_offset`, and `content_hash`, then creates, deletes,
shrinks, or splits `flashbacks` rows so SQLite represents exactly the
flashbacked ranges that remain.
7. Server writes `{storePath}/memories/{memoryId}/FLASHBACKS.json` as a
deterministic metadata export for git backup.
8. Server enqueues backup work for the metadata export.

Flashback toggle rules:

- Selecting unflashbacked text creates a flashback for the selected range.
- Selecting an already-flashbacked range unflashbacks the selected range only.
- Selecting a subset of a larger flashback preserves the unselected flashbacked
text by shrinking or splitting metadata.
- Selecting across multiple existing flashbacks removes only the selected
overlap from each affected flashback.

Selection payload:

Expand All @@ -92,11 +94,15 @@ Selection payload:
4. `start_offset`
5. `end_offset`

The server stores offsets in canonical reader text and guards them with
`content_hash` in `sha256:<hex>` format. Hash-mismatched flashbacks are treated
as stale and are not rendered at a guessed location.

If persistence fails, the optimistic UI state is rolled back or surfaced as
failed.

If highlight persistence returns backup failsafe metadata, the frontend must
refresh the global backup failsafe alert before showing the local highlight
If flashback persistence returns backup failsafe metadata, the frontend must
refresh the global backup failsafe alert before showing the local flashback
failure state.

## Git Backup
Expand All @@ -109,11 +115,12 @@ Flow:
2. Backup work is placed on the in-process sequential queue.
3. The backup worker uses `projectPath` as the working directory.
4. The worker stages only changes under `storePath`.
5. The worker commits with the configured message template.
5. The worker commits with the configured message template, including the
backup action when `{action}` is present.
6. The worker pushes only when configured.
7. SQLite backup status fields are updated.

Backup failures do not roll back memory creation or highlight creation.
Backup failures do not roll back memory creation or flashback creation.

On startup, TRAUMA should find pending, queued, or failed backup states that are
eligible for retry and re-enqueue them. `queued` is process-local, so queued rows
Expand Down
48 changes: 24 additions & 24 deletions docs/architecture/ui-and-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ Initial canonical routes:
- `/`
- `/memories`
- `/memories/:id`
- `/highlights`
- `/flashbacks`

`/` redirects to `/memories`.

`/memories` is the canonical browse and filter route. Query string state holds
filters and view options:

```text
/memories?q=...&category=...&tag=...&highlight=...&view=list|grid
/memories?q=...&category=...&tag=...&flashback=...&view=list|grid
```

`/highlights` is the canonical route for browsing highlighted excerpts across
`/flashbacks` is the canonical route for browsing flashbacked excerpts across
memories.

`/category`, `/tags`, and `/memories/new` are not initial routes. Category/tag
Expand All @@ -33,38 +33,38 @@ Desktop layout:

- Left shared navigation.
- Center content area.
- Right category/tag/highlight panel.
- Right category/tag/Flashback panel.

The left navigation is an app-shell component shared by all routes. It should
not be implemented as a page-specific component.

The right panel lists categories, tags, and recent highlights. Category and tag
items update the `/memories` query filter. Highlight shortcuts apply
`/memories?highlight=<highlight id>`. Source-memory navigation is handled by
the `/highlights` row title/link or reader anchors, not the primary right-panel
The right panel lists categories, tags, and Flashback shortcuts. Category and tag
items update the `/memories` query filter. Flashback shortcuts apply
`/memories?flashback=<flashback id>`. Source-memory navigation is handled by
the `/flashbacks` row title/link or reader anchors, not the primary right-panel
shortcut.

## Search And Filters

`/memories?q=...` searches memory metadata and highlight metadata. The query
must match title, URL, description, category names, tag names, highlighted text,
and stored highlight prefix/suffix context. This is not full body search; body
`/memories?q=...` searches memory metadata and flashback metadata. The query
must match title, URL, description, category names, tag names, flashbacked text,
and stored flashback prefix/suffix context. This is not full body search; body
FTS remains future work.

`highlight=...` filters memories to a specific highlight ID. This is mainly
used by right-panel highlight shortcuts.
`flashback=...` filters memories to a specific flashback ID. This is mainly
used by right-panel flashback shortcuts.

## Highlights View
## Flashbacks View

`/highlights` is a highlight-first browse view.
`/flashbacks` is a flashback-first browse view.

Each highlight row shows the source memory title, muted prefix context,
highlighted text, and muted suffix context. The visual treatment should feel
Each flashback row shows the source memory title, muted prefix context,
flashbacked text, and muted suffix context. The visual treatment should feel
close to a GitHub pull request file-review view: dense rows, quote-focused
content, clear source labels, and no full article rendering.

Clicking the source title or quote opens `/memories/:id` at the corresponding
highlight anchor.
flashback anchor.

## Responsive Behavior

Expand Down Expand Up @@ -106,18 +106,18 @@ design changes the route model.
The initial markdown reader supports:

- GitHub Flavored Markdown.
- Syntax highlighting.
- Syntax flashbacking.
- HTML sanitization.
- Footnotes.
- Heading anchors.
- Table of contents.
- Controlled external embeds.
- Highlight marks.
- Flashback marks.

Text selection inside reader content is a highlight toggle. Selecting
unhighlighted text creates a highlight. Selecting text that is already
highlighted removes highlight styling from the selected text only, preserving
any unselected highlighted text around it.
Text selection inside reader content is a flashback toggle. Selecting
unflashbacked text creates a flashback. Selecting text that is already
flashbacked removes flashback styling from the selected text only, preserving
any unselected flashbacked text around it.

External embeds auto-load in the initial design. This has privacy and network
side effects; future configuration may allow lazy or disabled embeds.
2 changes: 1 addition & 1 deletion docs/operations/local-self-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ TRAUMA does not auto-initialize a new repository. It creates a critical failsafe
alert so the operator can choose `revert` or `migrate` explicitly.

Backup work is asynchronous. A failed backup does not invalidate memory
creation, highlight creation, or markdown writes. Failures are recorded and
creation, flashback creation, or markdown writes. Failures are recorded and
surfaced through metadata.

When push is enabled, a missing configured remote name is treated as local-only
Expand Down
14 changes: 7 additions & 7 deletions docs/quality/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ Playwright should cover the main user workflows:
- Markdown file creation.
- `/memories` list rendering.
- `/memories/:id` reader rendering.
- `/highlights` highlight excerpt rendering.
- `/flashbacks` flashback excerpt rendering.
- Category/tag filtering.
- `/memories?q=...` matching highlight text.
- Highlight selection and persistence.
- Highlight toggle removal when selecting already-highlighted text.
- `/memories?q=...` matching flashback text.
- Flashback selection and persistence.
- Flashback toggle removal when selecting already-flashbacked text.
- Backup status display.

E2E tests should use controlled URLs or fixtures so extraction behavior is
Expand Down Expand Up @@ -54,8 +54,8 @@ Unit or integration tests should cover:
- Drizzle repositories.
- Importer success and failure mapping.
- Markdown store writer.
- Highlight marker insertion.
- Highlight marker removal, shrink, and split behavior.
- Flashback marker insertion.
- Flashback marker removal, shrink, and split behavior.
- Backup queue behavior.
- Backup environment failsafe drift, bootstrap, recovery, and push-failure
behavior.
Expand All @@ -79,7 +79,7 @@ mise exec -- bun run scripts/trauma-backup-failsafe.ts delete-missing-record --c

## Completion Bar

A change that affects storage, import, highlights, backup, routing, or reader
A change that affects storage, import, flashbacks, backup, routing, or reader
rendering should include either E2E coverage or a clear reason why focused tests
are sufficient.

Expand Down
Loading