Flat Home layout: Notes/ flat (tasks and Daily notes included), attachments by type, no folder-derived meaning (DESIGN §40) #165

Closed
opened 2026-09-26 10:23:44 +00:00 by kayg · 51 comments
Owner

Implement docs/DESIGN.md §40 (owner grill 2026-09-26; read it first, it overrides §17/§18/§31 folder rules). No data migration: no Home holds real data yet.

Work items:

  1. Home creation (crates/calternal-server/src/wire.rs, the suffix list ~line 1043) and every test fixture: create only Notes, Photos, Documents, .calternal.
  2. Daily notes: daily_note_path → Notes/YYYYMMDD-dailynote.md (crates/calternal-notes-core/src/dayfile.rs); is_daily_note_path matches by file name (any folder), not by Notes/Journal/.
  3. Tasks: new tasks are written to Notes/ with task frontmatter; the Tasks index/views/CalDAV VTODO find tasks by frontmatter anywhere in the Home, not by Tasks/ prefix (crates/plugins/notes/src/store.rs , identity.rs replacen Notes→Tasks, tasks_store/tasks_api/tasks_dav). The log-entry child bullet links to the task note in Notes/.
  4. Attachments routing (one module, one table, unit-tested): composer attachments, paste into notes, uploads from note/editor, bookmark/web-clip images (plugins/notes/src/bookmarks.rs writes Attachments/ today), voice memos → the destinations in §40. Unknown types → Documents/Other/. Photos dated by EXIF capture date else date added.
  5. Settings → a screenshots destination option (Photos default, Documents alternative); Memories exclude screenshots.
  6. Remove every folder-name special case that gives meaning (notes_index.rs:224 and links.rs:755 lists of "Notes" | "Tasks" | "Attachments" | "Photos" | "Calendar" | "Files"; grep for others in crates and apps/web). The UI reads meaning from the Index only. Files in any user-made subfolder index the same way.
  7. Update CONTEXT.md vocabulary and the web app (empty states, Files sidebar defaults, composer) accordingly.
  8. Tests: a Home with notes/tasks/Daily notes in random nested folders still shows everything correctly in Journal, Tasks, Calendar, search. Adversarial probe: cursed names in the new destinations, collisions in Documents/Other.
    Coordinate: job import-calternaljs (#152) targets this layout; do not edit its importer module.
Implement docs/DESIGN.md §40 (owner grill 2026-09-26; read it first, it overrides §17/§18/§31 folder rules). No data migration: no Home holds real data yet. Work items: 1. Home creation (crates/calternal-server/src/wire.rs, the suffix list ~line 1043) and every test fixture: create only Notes, Photos, Documents, .calternal. 2. Daily notes: daily_note_path → Notes/YYYYMMDD-dailynote.md (crates/calternal-notes-core/src/dayfile.rs); is_daily_note_path matches by file name (any folder), not by Notes/Journal/. 3. Tasks: new tasks are written to Notes/ with task frontmatter; the Tasks index/views/CalDAV VTODO find tasks by frontmatter anywhere in the Home, not by Tasks/ prefix (crates/plugins/notes/src/store.rs , identity.rs replacen Notes→Tasks, tasks_store/tasks_api/tasks_dav). The log-entry child bullet links to the task note in Notes/. 4. Attachments routing (one module, one table, unit-tested): composer attachments, paste into notes, uploads from note/editor, bookmark/web-clip images (plugins/notes/src/bookmarks.rs writes Attachments/ today), voice memos → the destinations in §40. Unknown types → Documents/Other/. Photos dated by EXIF capture date else date added. 5. Settings → a screenshots destination option (Photos default, Documents alternative); Memories exclude screenshots. 6. Remove every folder-name special case that gives meaning (notes_index.rs:224 and links.rs:755 lists of "Notes" | "Tasks" | "Attachments" | "Photos" | "Calendar" | "Files"; grep for others in crates and apps/web). The UI reads meaning from the Index only. Files in any user-made subfolder index the same way. 7. Update CONTEXT.md vocabulary and the web app (empty states, Files sidebar defaults, composer) accordingly. 8. Tests: a Home with notes/tasks/Daily notes in random nested folders still shows everything correctly in Journal, Tasks, Calendar, search. Adversarial probe: cursed names in the new destinations, collisions in Documents/Other. Coordinate: job import-calternaljs (#152) targets this layout; do not edit its importer module.
Author
Owner

Item 3 clarification: crates/plugins/notes/src/store.rs checks target.starts_with("Tasks/"); replace that with a frontmatter check.

Item 3 clarification: crates/plugins/notes/src/store.rs checks `target.starts_with("Tasks/")`; replace that with a frontmatter check.
Author
Owner

Started job/flat-layout from dev at 44b15bec4f. Implementing docs/DESIGN.md §40 in the requested slices; importer module remains out of scope.

Started job/flat-layout from dev at 44b15bec4f4e78e788c85b7ec60d54892a293b84. Implementing docs/DESIGN.md §40 in the requested slices; importer module remains out of scope.
Author
Owner

Finding: crates/calternal-server/src/wire.rs::sync_homes still creates /Notes/Journal, /Tasks, /Attachments, /Calendar, and /Files (lines 1045-1052), while DESIGN §40 permits only Notes, Photos, Documents, and .calternal. Daily-note creation and task routing also still use folder prefixes in the Notes plugin. I am updating those paths and their consumers in the planned slices.

Finding: `crates/calternal-server/src/wire.rs::sync_homes` still creates `/Notes/Journal`, `/Tasks`, `/Attachments`, `/Calendar`, and `/Files` (lines 1045-1052), while DESIGN §40 permits only Notes, Photos, Documents, and .calternal. Daily-note creation and task routing also still use folder prefixes in the Notes plugin. I am updating those paths and their consumers in the planned slices.
Author
Owner

Finding: calternal_notes_core::daily_note_path wrote Notes/Journal/<date>-dailynote.md, and is_daily_note_path returned false for the same file name in any other folder. build_new_day_file also linked yesterday and tomorrow through Notes/Journal/. Fixed in 25320b1a: Daily notes now write under Notes/, use filename-based identity, and retain their date when task checkboxes are projected from nested paths.

Gate output:

test result: ok. 477 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.69s
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 12.69s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.00s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s
Finding: `calternal_notes_core::daily_note_path` wrote `Notes/Journal/<date>-dailynote.md`, and `is_daily_note_path` returned false for the same file name in any other folder. `build_new_day_file` also linked yesterday and tomorrow through `Notes/Journal/`. Fixed in `25320b1a`: Daily notes now write under `Notes/`, use filename-based identity, and retain their date when task checkboxes are projected from nested paths. Gate output: ``` test result: ok. 477 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.69s test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 12.69s test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.00s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s ```
Author
Owner

Finding: Daily note consumers still chose Notes/Journal/ or the canonical flat path directly. A Daily note moved to a user folder was omitted or a second file was created for its date. Journal scanning now recognizes the filename anywhere in Home, and date-based writers resolve the indexed Daily note first, with a Home scan fallback. Duplicate Daily notes for one date return a conflict.

Finding: Task attachment replay updated the Journal projection but did not rebuild task_attachments. The Markdown child link was durable while Tasks and Calendar could miss the relationship. Replay now refreshes both projections, including when it sees an already-written child link. Task path and reference identity now come from Notes paths and frontmatter, and Daily date queries inspect the filename suffix instead of a fixed path offset.

Commits: 80daba46 classifies Task notes from frontmatter and writes task identities under Notes; bbfcb325 indexes Markdown and Daily notes across Home and repairs Task attachment projections.

Gate output:

running 89 tests
...
test result: ok. 89 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 152.41s

running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
..........
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.42s

running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Finding: Daily note consumers still chose `Notes/Journal/` or the canonical flat path directly. A Daily note moved to a user folder was omitted or a second file was created for its date. Journal scanning now recognizes the filename anywhere in Home, and date-based writers resolve the indexed Daily note first, with a Home scan fallback. Duplicate Daily notes for one date return a conflict. Finding: Task attachment replay updated the Journal projection but did not rebuild `task_attachments`. The Markdown child link was durable while Tasks and Calendar could miss the relationship. Replay now refreshes both projections, including when it sees an already-written child link. Task path and reference identity now come from Notes paths and frontmatter, and Daily date queries inspect the filename suffix instead of a fixed path offset. Commits: `80daba46` classifies Task notes from frontmatter and writes task identities under Notes; `bbfcb325` indexes Markdown and Daily notes across Home and repairs Task attachment projections. Gate output: ``` running 89 tests ... test result: ok. 89 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 152.41s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests .......... test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.42s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ```
Author
Owner

Finding: Tags and Finder tag assignment used the Notes/ and Tasks/ prefixes as a Markdown category. Tags now index visible Markdown across Home, classify Task notes from frontmatter, and recognize Daily note dates by filename. Hidden paths remain excluded from the visible Markdown pass. Committed as 15332a31.

Gate output from cargo test -p calternal-tags --quiet:

running 10 tests
..........
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.42s

running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Finding: Tags and Finder tag assignment used the `Notes/` and `Tasks/` prefixes as a Markdown category. Tags now index visible Markdown across Home, classify Task notes from frontmatter, and recognize Daily note dates by filename. Hidden paths remain excluded from the visible Markdown pass. Committed as `15332a31`. Gate output from `cargo test -p calternal-tags --quiet`: ``` running 10 tests .......... test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.42s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ```
Author
Owner

Progress: committed flat-Home Markdown link resolution as cfddcc2a and the shared attachment routing table as 22c2ce93.

Finding: the first Notes build could not resolve calternal_plugin_files::web_clip_attachment_path (E0433). Files already depends on Notes, so a direct Notes-to-Files dependency would create a cycle. I moved the routing table and confined web-clip path builder to calternal-notes-core; Files and Notes now call the same helper.

Evidence:

  • cargo test -p calternal-notes-core --quiet: 482 + 13 + 7 + 11 tests passed, 0 failed.
  • cargo test -p calternal-plugin-files --quiet: 89 tests passed, 0 failed.

The attachment upload API now returns the installed Home-relative path so Notes links remain correct after server-side routing or collision renaming. Photos and Calendar Home-wide indexing is in progress.

Progress: committed flat-Home Markdown link resolution as cfddcc2a and the shared attachment routing table as 22c2ce93. Finding: the first Notes build could not resolve `calternal_plugin_files::web_clip_attachment_path` (E0433). Files already depends on Notes, so a direct Notes-to-Files dependency would create a cycle. I moved the routing table and confined web-clip path builder to `calternal-notes-core`; Files and Notes now call the same helper. Evidence: - `cargo test -p calternal-notes-core --quiet`: 482 + 13 + 7 + 11 tests passed, 0 failed. - `cargo test -p calternal-plugin-files --quiet`: 89 tests passed, 0 failed. The attachment upload API now returns the installed Home-relative path so Notes links remain correct after server-side routing or collision renaming. Photos and Calendar Home-wide indexing is in progress.
Author
Owner

Finding: switching the default Photos scope to the whole Home exposed a remaining path predicate in the full Photos scan. cargo test -p calternal-plugin-photos --quiet failed with RowNotFound because the scan queried descendants of an empty root as if Home paths began with /. The full scan now omits the root path clause for an empty root. Rerun passed: 39 passed, 0 failed, 2 ignored; 0 doc tests.

Photos settings now store the screenshot destination in .calternal/settings.json, default the library to Home, and treat an empty library root as Home during scans, search, and invalidation. The web-facing preferences endpoint preserves other settings fields.

Finding: switching the default Photos scope to the whole Home exposed a remaining path predicate in the full Photos scan. `cargo test -p calternal-plugin-photos --quiet` failed with `RowNotFound` because the scan queried descendants of an empty root as if Home paths began with `/`. The full scan now omits the root path clause for an empty root. Rerun passed: 39 passed, 0 failed, 2 ignored; 0 doc tests. Photos settings now store the screenshot destination in `.calternal/settings.json`, default the library to Home, and treat an empty library root as Home during scans, search, and invalidation. The web-facing preferences endpoint preserves other settings fields.
Author
Owner

Finding: the Notes Plugin still excluded Notes/Templates/*.md from the Note Index, and Notes and CalDAV VTODO creation could create nested or Tasks/ destinations. This made the folder name assign meaning and left a default Tasks/ creation path in the Reminders provider.

Templates now live in .calternal/notes/templates, which Home indexing skips as internal data. Visible Markdown in a user-created Notes/Templates/ folder is indexed as a regular Note. New Notes and template-created Notes accept only the flat Notes/ destination. Moving a Note into a subfolder now requires that the User already created the folder. CalDAV VTODO creation writes into Notes/.

Decision: retain the template API's optional folder field for compatibility, but accept only Notes; this keeps all calternal-created Notes flat. There is no migration because DESIGN §40 says no Home has real data yet.

Gate output from cargo test -p calternal-plugin-notes --quiet:

test result: ok. 90 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 255.89s

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
Finding: the Notes Plugin still excluded `Notes/Templates/*.md` from the Note Index, and Notes and CalDAV VTODO creation could create nested or `Tasks/` destinations. This made the folder name assign meaning and left a default `Tasks/` creation path in the Reminders provider. Templates now live in `.calternal/notes/templates`, which Home indexing skips as internal data. Visible Markdown in a user-created `Notes/Templates/` folder is indexed as a regular Note. New Notes and template-created Notes accept only the flat `Notes/` destination. Moving a Note into a subfolder now requires that the User already created the folder. CalDAV VTODO creation writes into `Notes/`. Decision: retain the template API's optional `folder` field for compatibility, but accept only `Notes`; this keeps all calternal-created Notes flat. There is no migration because DESIGN §40 says no Home has real data yet. Gate output from `cargo test -p calternal-plugin-notes --quiet`: ``` test result: ok. 90 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 255.89s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s ```
Author
Owner

Finding: cargo test -p calternal-plugin-calendar --quiet completed 40 tests and found two fixture/projection problems. items_are_placed_in_time_order_and_paginated still expected Notes/asset.png to be hidden because of its parent folder; Calendar now projects it as a photo by indexed MIME type. range_and_year_project_indexed_items_and_filter_shares exposed an ordering defect: Note reconciliation filters a Task link before task_items has been rebuilt. I am updating the fixture to create a Task Note and an ordinary Note in a user folder, and rebuilding the Task projection before Note Calendar logs.

Finding: `cargo test -p calternal-plugin-calendar --quiet` completed 40 tests and found two fixture/projection problems. `items_are_placed_in_time_order_and_paginated` still expected `Notes/asset.png` to be hidden because of its parent folder; Calendar now projects it as a photo by indexed MIME type. `range_and_year_project_indexed_items_and_filter_shares` exposed an ordering defect: Note reconciliation filters a Task link before `task_items` has been rebuilt. I am updating the fixture to create a Task Note and an ordinary Note in a user folder, and rebuilding the Task projection before Note Calendar logs.
Author
Owner

Finding fixed in 075197ab8516a8bdb99f765eac4b12e9acce8ce5. Home reconciliation now builds Task rows from Task Note frontmatter before rebuilding Note and Daily note projections. The Calendar regression fixture puts a Task Note and an ordinary Note under Projects/roadmap/; Calendar resolves both links. Photos under Notes/ are included based on indexed MIME type.

Targeted output from cargo test -p calternal-plugin-calendar --quiet:

running 42 tests
..........................................
test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 9.59s

running 1 test
.
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1.05s

running 3 tests
...
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.52s

running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Finding fixed in `075197ab8516a8bdb99f765eac4b12e9acce8ce5`. Home reconciliation now builds Task rows from Task Note frontmatter before rebuilding Note and Daily note projections. The Calendar regression fixture puts a Task Note and an ordinary Note under `Projects/roadmap/`; Calendar resolves both links. Photos under `Notes/` are included based on indexed MIME type. Targeted output from `cargo test -p calternal-plugin-calendar --quiet`: ``` running 42 tests .......................................... test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 9.59s running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1.05s running 3 tests ... test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.52s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ```
Author
Owner

Finding: the targeted Notes test (bun run --cwd apps/web test -- src/lib/notes/notes.test.ts) found one stale resolver expectation. Work/20260924-meeting-33333333 still received the old implicit Notes/ root, while §40 makes multi-part paths Home-relative. I am updating the test to require the explicit Home path Notes/Work/... and to leave the shorthand unresolved.

Finding: the targeted Notes test (`bun run --cwd apps/web test -- src/lib/notes/notes.test.ts`) found one stale resolver expectation. `Work/20260924-meeting-33333333` still received the old implicit `Notes/` root, while §40 makes multi-part paths Home-relative. I am updating the test to require the explicit Home path `Notes/Work/...` and to leave the shorthand unresolved.
Author
Owner

Finding from the merged adversarial build: cargo build -p calternal-server -p calternal-cli -p calternal-sync stopped at crates/plugins/files/src/index.rs:620; the unchanged-row branch returned Ok(()) after record_once changed to return Result<bool, Failure>. The correct successful no-op result is Ok(true), because false means the file changed during hashing and asks record_impl to retry. I am fixing this merge interaction and adding a regression test that checks the unchanged record does not add a change-feed row.

Finding from the merged adversarial build: `cargo build -p calternal-server -p calternal-cli -p calternal-sync` stopped at `crates/plugins/files/src/index.rs:620`; the unchanged-row branch returned `Ok(())` after `record_once` changed to return `Result<bool, Failure>`. The correct successful no-op result is `Ok(true)`, because `false` means the file changed during hashing and asks `record_impl` to retry. I am fixing this merge interaction and adding a regression test that checks the unchanged record does not add a change-feed row.
Author
Owner

Finding fixed in 1097eae1: after the merged index retry change, an unchanged Files row returned Ok(()) where record_once now returns Result<bool, Failure>. It now returns Ok(true) so the no-op ends successfully; false remains the retry signal for a file that changed during hashing. Added a regression that checks the item ID stays stable and the change-feed count does not increase.

Targeted output from cargo test -p calternal-plugin-files --quiet index_record_keeps_an_unchanged_row_without_new_feed_event:

running 1 test
.
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 90 filtered out; finished in 0.55s
Finding fixed in `1097eae1`: after the merged index retry change, an unchanged Files row returned `Ok(())` where `record_once` now returns `Result<bool, Failure>`. It now returns `Ok(true)` so the no-op ends successfully; `false` remains the retry signal for a file that changed during hashing. Added a regression that checks the item ID stays stable and the change-feed count does not increase. Targeted output from `cargo test -p calternal-plugin-files --quiet index_record_keeps_an_unchanged_row_without_new_feed_event`: ``` running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 90 filtered out; finished in 0.55s ```
Author
Owner

Adversarial progress after the merge:

  • The first live run showed that the probe still expected the Photos default root to be Photos; DESIGN §40 now uses the empty relative root for all of Home. The run also found attack2.py reading Notes/Journal/... after API-created Daily notes were written flat. Updated both probe files in 7cc7d5f4. python3 -m py_compile tests/adversarial/attack.py tests/adversarial/attack2.py passed.
  • The unknown attachment collision returned 412 with error code conflict and message upload destination changed; the probe also verified that the original file survived. The design does not specify an HTTP status here, so the probe accepts safe 409 or stale-reservation 412 responses.
  • cargo test -p calternal-plugin-files --quiet files_note_rename_and_move_rewrite_referrers_and_note_index passed: 1 passed, 0 failed. The live move probe still reported a combined Note path/backlink/referrer mismatch, so I added its observed values to the probe diagnostic and am investigating.
  • Other live findings need follow-up: a shared Photos timeline stayed empty for 12 seconds, and requests timed out during DAV, Journal PATCH, bookmark capture, and collaboration probes. The round also crashed when the stale Notes/Journal fixture was read. I will separate reproducible behavior defects from shared-host timeouts before final gates.

Restart probe: 0 findings. Hostile-bytes probe: 0 findings.

Adversarial progress after the merge: - The first live run showed that the probe still expected the Photos default root to be `Photos`; DESIGN §40 now uses the empty relative root for all of Home. The run also found `attack2.py` reading `Notes/Journal/...` after API-created Daily notes were written flat. Updated both probe files in `7cc7d5f4`. `python3 -m py_compile tests/adversarial/attack.py tests/adversarial/attack2.py` passed. - The unknown attachment collision returned 412 with error code `conflict` and message `upload destination changed`; the probe also verified that the original file survived. The design does not specify an HTTP status here, so the probe accepts safe 409 or stale-reservation 412 responses. - `cargo test -p calternal-plugin-files --quiet files_note_rename_and_move_rewrite_referrers_and_note_index` passed: 1 passed, 0 failed. The live move probe still reported a combined Note path/backlink/referrer mismatch, so I added its observed values to the probe diagnostic and am investigating. - Other live findings need follow-up: a shared Photos timeline stayed empty for 12 seconds, and requests timed out during DAV, Journal PATCH, bookmark capture, and collaboration probes. The round also crashed when the stale `Notes/Journal` fixture was read. I will separate reproducible behavior defects from shared-host timeouts before final gates. Restart probe: 0 findings. Hostile-bytes probe: 0 findings.
Author
Owner

The non-SLOW timeout findings from the live round are filed for isolated reproduction in #205. The probe already marks the Journal move queue’s expected timeout as SLOW; #205 records the other request timeouts that exceeded their 10–30 second client deadlines. The round also confirmed the server stayed alive. I will include #205 in the final report.

The non-SLOW timeout findings from the live round are filed for isolated reproduction in #205. The probe already marks the Journal move queue’s expected timeout as `SLOW`; #205 records the other request timeouts that exceeded their 10–30 second client deadlines. The round also confirmed the server stayed alive. I will include #205 in the final report.
Author
Owner

Finding: the Search Indexer still assigned Task kind from the Tasks/ path and parsed Daily note Log entries only under Notes/Journal/. Regression tests failed when a frontmatter Task lived at Projects/roadmap/launch.md and a Daily note lived at Archive/Journal entries/20260924-dailynote.md. Search now uses Task frontmatter and the Daily note basename; cargo test -p calternal-search passes (24 unit, 1 ask-evaluation, 1 citation, 14 Indexer, 4 operator, 1 relevance, 4 retrieval; 3 ignored).

Finding: cargo fmt --check reports formatting drift in Rust files changed by this branch, including Notes core, server wire, Calendar, Notes and Photos. I will format those files and rerun the gate before committing.

Finding: the Search Indexer still assigned Task kind from the `Tasks/` path and parsed Daily note Log entries only under `Notes/Journal/`. Regression tests failed when a frontmatter Task lived at `Projects/roadmap/launch.md` and a Daily note lived at `Archive/Journal entries/20260924-dailynote.md`. Search now uses Task frontmatter and the Daily note basename; `cargo test -p calternal-search` passes (24 unit, 1 ask-evaluation, 1 citation, 14 Indexer, 4 operator, 1 relevance, 4 retrieval; 3 ignored). Finding: `cargo fmt --check` reports formatting drift in Rust files changed by this branch, including Notes core, server wire, Calendar, Notes and Photos. I will format those files and rerun the gate before committing.
Author
Owner

Finding: the Photos Index used Photos/YYYY/YYYY-MM-DD/ as a timeline date even when the indexed capture timestamp differed. A regression with folder date 2024-06-02 and timestamp 2024-06-01 22:30 UTC failed with the folder's day; a viewer in Europe/Berlin should still see the timestamp on 2024-06-02. The timeline now uses the indexed capture time (or date-added fallback) in the viewer's zone and ignores the folder name. The focused Photos tests pass.

Finding: the Photos Index used `Photos/YYYY/YYYY-MM-DD/` as a timeline date even when the indexed capture timestamp differed. A regression with folder date 2024-06-02 and timestamp 2024-06-01 22:30 UTC failed with the folder's day; a viewer in Europe/Berlin should still see the timestamp on 2024-06-02. The timeline now uses the indexed capture time (or date-added fallback) in the viewer's zone and ignores the folder name. The focused Photos tests pass.
Author
Owner

Finding: cargo test -p calternal-plugin-files initially failed in timeout_kills_the_whole_media_process_group: the process exited between /proc/<pid>/stat polls, but the test kept the previous R state and reported a live decoder. The poll now clears that stale state when the process entry disappears. Evidence: the isolated test passes and the full Files suite passes with 101 tests.

Finding: strict Photos Clippy stopped in crates/plugins/files/src/uploads.rs on a nested duplicate attachment-purpose check (collapsible_match). The parser now uses a match guard with the same rejection behavior. Evidence: cargo clippy -p calternal-plugin-files --all-targets -- -D warnings passes.

Finding: `cargo test -p calternal-plugin-files` initially failed in `timeout_kills_the_whole_media_process_group`: the process exited between `/proc/<pid>/stat` polls, but the test kept the previous `R` state and reported a live decoder. The poll now clears that stale state when the process entry disappears. Evidence: the isolated test passes and the full Files suite passes with 101 tests. Finding: strict Photos Clippy stopped in `crates/plugins/files/src/uploads.rs` on a nested duplicate `attachment-purpose` check (`collapsible_match`). The parser now uses a match guard with the same rejection behavior. Evidence: `cargo clippy -p calternal-plugin-files --all-targets -- -D warnings` passes.
Author
Owner

Workspace test finding: cargo test failed in calternal-collab/tests/journal_race.rs because begin_agent returned 404 Not Found where the test expected 409 Conflict. The fixture names the Daily note Notes/Journal/20310802-dailynote.md, but daily_note_path("2031-08-02") now creates the canonical flat path Notes/20310802-dailynote.md. The live-room lookup therefore misses the note and returns 404 before the Daily note refusal check. I am updating the test fixture to use the canonical flat Home path, then I will rerun the affected test and crate suite.

Workspace test finding: `cargo test` failed in `calternal-collab/tests/journal_race.rs` because `begin_agent` returned `404 Not Found` where the test expected `409 Conflict`. The fixture names the Daily note `Notes/Journal/20310802-dailynote.md`, but `daily_note_path("2031-08-02")` now creates the canonical flat path `Notes/20310802-dailynote.md`. The live-room lookup therefore misses the note and returns 404 before the Daily note refusal check. I am updating the test fixture to use the canonical flat Home path, then I will rerun the affected test and crate suite.
Author
Owner

From the #125 job (Claude relaying): Daily notes uploaded directly under flat Notes/ (Notes/YYYYMMDD-dailynote.md) are not indexed; the Notes plugin still expects Notes/Journal/. Item 2 of this issue must cover indexing and every reader (Journal, Calendar, Analytics, search), with a test that a Daily note in Notes/ and one in a random subfolder both appear.

From the #125 job (Claude relaying): Daily notes uploaded directly under flat Notes/ (Notes/YYYYMMDD-dailynote.md) are not indexed; the Notes plugin still expects Notes/Journal/. Item 2 of this issue must cover indexing and every reader (Journal, Calendar, Analytics, search), with a test that a Daily note in Notes/ and one in a random subfolder both appear.
Author
Owner

Production-build Photos E2E finding: pressing Escape while the mode-header “More actions” menu is open emitted TypeError: Cannot read properties of null (reading 'items'). Playwright captured the stack in the compiled ModeHeader menu key handler (BXD4Bn4h.js, onkeydown); this happened after the nested Tile size submenu interaction. The page continued, but the exception confirms the exiting menu reads the now-null action-menu state. I am fixing the menu state snapshot and adding a zero-client-errors assertion to the E2E run. The selection portion of the old script also expected “Select photos” as a direct header button; it now follows the current “More actions” menu.

Production-build Photos E2E finding: pressing Escape while the mode-header “More actions” menu is open emitted `TypeError: Cannot read properties of null (reading 'items')`. Playwright captured the stack in the compiled `ModeHeader` menu key handler (`BXD4Bn4h.js`, `onkeydown`); this happened after the nested Tile size submenu interaction. The page continued, but the exception confirms the exiting menu reads the now-null action-menu state. I am fixing the menu state snapshot and adding a zero-client-errors assertion to the E2E run. The selection portion of the old script also expected “Select photos” as a direct header button; it now follows the current “More actions” menu.
Author
Owner

Finding after merging dev: the shared user-settings change moved Photos parsing to a section value, while Photos index, search and active-root readers still supplied full settings bytes. The focused Photos build exposed this integration mismatch before the merge commit. Added one adapter that parses the full document and delegates to the same section parser, plus a regression test. Evidence: cargo test -p calternal-plugin-photos passed (41 passed, 2 ignored); the merged Files unchanged-row regression passed (1 passed).

Finding after merging dev: the shared user-settings change moved Photos parsing to a section value, while Photos index, search and active-root readers still supplied full settings bytes. The focused Photos build exposed this integration mismatch before the merge commit. Added one adapter that parses the full document and delegates to the same section parser, plus a regression test. Evidence: `cargo test -p calternal-plugin-photos` passed (41 passed, 2 ignored); the merged Files unchanged-row regression passed (1 passed).
Author
Owner

Post-merge adversarial finding traced to stale Photos assertions, not an API defect. The probe sends Europe/Berlin on upload, which selects Photos/2024/2024-06-02/; it leaves the User's timezone setting absent. The source timestamp is 2024-06-01T22:30Z, so the Photos Index correctly groups it on June 1 in UTC, as required by DESIGN §39. Folder paths do not define the Photos view day under §40. Updated both probes in b9e0dfaf to check the June 1 timeline while retaining the June 2 Files path assertion. python3 -m py_compile tests/adversarial/attack.py tests/adversarial/attack2.py passed.

Post-merge adversarial finding traced to stale Photos assertions, not an API defect. The probe sends `Europe/Berlin` on upload, which selects `Photos/2024/2024-06-02/`; it leaves the User's timezone setting absent. The source timestamp is `2024-06-01T22:30Z`, so the Photos Index correctly groups it on June 1 in UTC, as required by DESIGN §39. Folder paths do not define the Photos view day under §40. Updated both probes in `b9e0dfaf` to check the June 1 timeline while retaining the June 2 Files path assertion. `python3 -m py_compile tests/adversarial/attack.py tests/adversarial/attack2.py` passed.
Author
Owner

Finding: the round-two logrewrite probe treated any non-200 Journal GET as an empty entry list. During shared-host contention, a 6-second GET timed out while the Daily note still had all five Log bullets, so the probe falsely reported 5 -> 0 and then said the target entry was gone. Commit 3ba137f1 makes the probe report failed reads and retain the last confirmed count. The timeout evidence is recorded in #205 for quiet-host reproduction.

Finding: the round-two `logrewrite` probe treated any non-200 Journal GET as an empty entry list. During shared-host contention, a 6-second GET timed out while the Daily note still had all five Log bullets, so the probe falsely reported `5 -> 0` and then said the target entry was gone. Commit `3ba137f1` makes the probe report failed reads and retain the last confirmed count. The timeout evidence is recorded in #205 for quiet-host reproduction.
Author
Owner

Adversarial follow-up: during the upload-slot stress phase, the local server returned HTTP 500 Authentication service unavailable for file-folder creation and repeatedly for TUS upload creation (reported for slot-burn requests 0–7 so far). The server remained alive. In the same interval, its log recorded recurring SQLite pool-acquire timeouts in background server/search workers and auth-backed work. This is a non-SLOW 5xx. I filed the evidence and requested a quiet-host reproduction in #205; the shared host had many concurrent build/browser jobs, so this round does not isolate the product-only trigger. No auth/pool behavior change was made from this run.

Adversarial follow-up: during the upload-slot stress phase, the local server returned HTTP 500 `Authentication service unavailable` for file-folder creation and repeatedly for TUS upload creation (reported for slot-burn requests 0–7 so far). The server remained alive. In the same interval, its log recorded recurring SQLite pool-acquire timeouts in background server/search workers and auth-backed work. This is a non-SLOW 5xx. I filed the evidence and requested a quiet-host reproduction in #205; the shared host had many concurrent build/browser jobs, so this round does not isolate the product-only trigger. No auth/pool behavior change was made from this run.
Author
Owner

Finding: calternal-search/src/semantic.rs::kind_for labels Markdown under top-level Tasks/ as a Task from its path, even though the Search Indexer uses Task frontmatter. The new regression failed before the fix: left: "task", right: "note" for Tasks/meeting.md. A user-created folder name must not assign meaning. I am removing this path-based classification and keeping the frontmatter-aware Search Indexer as the Task source.

Finding: `calternal-search/src/semantic.rs::kind_for` labels Markdown under top-level `Tasks/` as a Task from its path, even though the Search Indexer uses Task frontmatter. The new regression failed before the fix: `left: "task", right: "note"` for `Tasks/meeting.md`. A user-created folder name must not assign meaning. I am removing this path-based classification and keeping the frontmatter-aware Search Indexer as the Task source.
Author
Owner

Finding during the required merge of dev: Calendar's legacy Log timezone regression failed after Notes reconciliation. The second reconcile decoded a nullable SQLite timezone column as String, which produced an empty string for SQL NULL; record_log_timezone then could not update the row because it only updates timezone IS NULL rows. Changed that read to Option<String> and kept the Calendar regression assertion. Also changed one indexed image in the Calendar test to Archive/photo-0.jpg to verify MIME, not folder name, controls photo classification. Verification: cargo test -p calternal-plugin-calendar range_and_year_project_indexed_items_and_filter_shares -- --nocapture passed (1 passed, 41 filtered out).

Finding during the required merge of `dev`: Calendar's legacy Log timezone regression failed after Notes reconciliation. The second reconcile decoded a nullable SQLite `timezone` column as `String`, which produced an empty string for SQL `NULL`; `record_log_timezone` then could not update the row because it only updates `timezone IS NULL` rows. Changed that read to `Option<String>` and kept the Calendar regression assertion. Also changed one indexed image in the Calendar test to `Archive/photo-0.jpg` to verify MIME, not folder name, controls photo classification. Verification: `cargo test -p calternal-plugin-calendar range_and_year_project_indexed_items_and_filter_shares -- --nocapture` passed (1 passed, 41 filtered out).
Author
Owner

Adversarial finding after the dev merge: tests/adversarial/authz_matrix.py failed on GET /api/v1/analytics (analytics_report) because the new data API was missing from DATA_PREFIXES. This is a probe policy gap, not a server response finding. Added the Analytics route to the data policy list; I will rerun the authorization matrix against a fresh local server and report its result.

Adversarial finding after the `dev` merge: `tests/adversarial/authz_matrix.py` failed on `GET /api/v1/analytics` (`analytics_report`) because the new data API was missing from `DATA_PREFIXES`. This is a probe policy gap, not a server response finding. Added the Analytics route to the data policy list; I will rerun the authorization matrix against a fresh local server and report its result.
Author
Owner

Resolved the Files Note move race found by the real-server probe. Immediately after a successful move, the probe got 404/200/200 from Note, backlinks, and referrer reads; later, the file, Note index row, and link target were present at Notes/Archive/.... Root cause: adopt_change read before taking the per-User lock, so an event could capture the source bytes, wait for a move, then index those bytes at the old path.

Moved the lock before the read and added a regression that polls a Home event while the lock is held, moves the Note, then releases the lock. It failed first with the old path and passes after the fix. Commit: cdcfe7db.

Gate output:

running 92 tests
........................................... ............................................ 87/92
.... .
test result: ok. 92 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 60.84s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Resolved the Files Note move race found by the real-server probe. Immediately after a successful move, the probe got `404/200/200` from Note, backlinks, and referrer reads; later, the file, Note index row, and link target were present at `Notes/Archive/...`. Root cause: `adopt_change` read before taking the per-User lock, so an event could capture the source bytes, wait for a move, then index those bytes at the old path. Moved the lock before the read and added a regression that polls a Home event while the lock is held, moves the Note, then releases the lock. It failed first with the old path and passes after the fix. Commit: `cdcfe7db`. Gate output: ``` running 92 tests ........................................... ............................................ 87/92 .... . test result: ok. 92 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 60.84s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ```
Author
Owner

The time-boxed real-server adversarial round completed and the server stayed alive. It reported these non-SLOW no-response results, which I am filing here for owner review:

  • Bookmark capture storm: all 16 requests returned -1 (no HTTP response), so 0/16 IDs were created.
  • Collab Embed Note creation: 2 requests timed out.
  • Analytics concurrency storm: 2 requests timed out while 90 reads and 30 Log writes ran together.

The host was also running several independent adversarial/browser jobs during this round; this is evidence of shared-host contention, not proof that it caused the timeouts. The remaining findings were marked SLOW. The move consistency race was fixed separately in cdcfe7db.

The time-boxed real-server adversarial round completed and the server stayed alive. It reported these non-SLOW no-response results, which I am filing here for owner review: - Bookmark capture storm: all 16 requests returned `-1` (no HTTP response), so 0/16 IDs were created. - Collab Embed Note creation: 2 requests timed out. - Analytics concurrency storm: 2 requests timed out while 90 reads and 30 Log writes ran together. The host was also running several independent adversarial/browser jobs during this round; this is evidence of shared-host contention, not proof that it caused the timeouts. The remaining findings were marked SLOW. The move consistency race was fixed separately in `cdcfe7db`.
Author
Owner

Continuation after the VM restart: merged the current dev tip 21ee397d into job/flat-layout. The merge completed without conflicts as 48ccf609; it includes the Public Edit API and the latest sync-root changes. git diff --check HEAD^1 HEAD passed, and the flat Home watcher lock fix plus Analytics authorization route coverage are present in the merged tree.

The API merge requires one fresh adversarial round. The full runner also includes the pending authorization matrix, so I will run it once against this merged tree, then run the final workspace gates. Other worktrees currently have active adversarial/browser runs, so I am waiting to avoid adding shared-host load. The worktree has no uncommitted source changes.

Continuation after the VM restart: merged the current `dev` tip `21ee397d` into `job/flat-layout`. The merge completed without conflicts as `48ccf609`; it includes the Public Edit API and the latest sync-root changes. `git diff --check HEAD^1 HEAD` passed, and the flat Home watcher lock fix plus Analytics authorization route coverage are present in the merged tree. The API merge requires one fresh adversarial round. The full runner also includes the pending authorization matrix, so I will run it once against this merged tree, then run the final workspace gates. Other worktrees currently have active adversarial/browser runs, so I am waiting to avoid adding shared-host load. The worktree has no uncommitted source changes.
Author
Owner

Finding after merging dev: crates/plugins/files/src/public.rs::markdown_note_path required a Notes/ prefix before treating Markdown as a Note for Public Edit. This denied Public Edit for visible Markdown in a User-created Home folder or at Home root, although DESIGN §40 defines a Note by visible Markdown and says folder paths do not give meaning; DESIGN §22 applies Edit to a Notes item.

Evidence: cargo test -p calternal-plugin-files markdown_note_paths_follow_home_wide_note_index -- --nocapture failed before the change at Projects/roadmap.markdown. It passes after the fix (1 passed, 103 filtered out). The path check now follows the Notes Index, rejects hidden paths, and retains the existing exclusion for Daily notes. Commit: 20e34efc.

Decision where the Design is silent: Daily notes remain ineligible for public guest editing. The Design keeps Daily notes in a separate Journal flow, and this change preserves the existing exclusion while removing folder-based Note classification.

Finding after merging `dev`: `crates/plugins/files/src/public.rs::markdown_note_path` required a `Notes/` prefix before treating Markdown as a Note for Public Edit. This denied Public Edit for visible Markdown in a User-created Home folder or at Home root, although DESIGN §40 defines a Note by visible Markdown and says folder paths do not give meaning; DESIGN §22 applies Edit to a Notes item. Evidence: `cargo test -p calternal-plugin-files markdown_note_paths_follow_home_wide_note_index -- --nocapture` failed before the change at `Projects/roadmap.markdown`. It passes after the fix (1 passed, 103 filtered out). The path check now follows the Notes Index, rejects hidden paths, and retains the existing exclusion for Daily notes. Commit: `20e34efc`. Decision where the Design is silent: Daily notes remain ineligible for public guest editing. The Design keeps Daily notes in a separate Journal flow, and this change preserves the existing exclusion while removing folder-based Note classification.
Author
Owner

dev advanced again while the host was busy. I merged the current tip cf142cb4 cleanly as 535c4719; the Files thumbnail test change and media-sandbox checks are included. The nested Markdown Public Edit fix remains committed in 20e34efc. The worktree is clean. I will run one full adversarial pass against this final merged tree (including the authorization matrix), then the workspace gates.

`dev` advanced again while the host was busy. I merged the current tip `cf142cb4` cleanly as `535c4719`; the Files thumbnail test change and media-sandbox checks are included. The nested Markdown Public Edit fix remains committed in `20e34efc`. The worktree is clean. I will run one full adversarial pass against this final merged tree (including the authorization matrix), then the workspace gates.
Author
Owner

dev added the generated OpenAPI and API-client entries for the new Public Edit operations (eb9acfcc). I merged that contract update as 921db3df; the worktree is clean, and dev is currently at eb9acfcc. The adversarial round remains queued. At the latest capacity check, three neighboring adversarial scripts and ten Rust build processes were active, with no browser at that instant.

`dev` added the generated OpenAPI and API-client entries for the new Public Edit operations (`eb9acfcc`). I merged that contract update as `921db3df`; the worktree is clean, and `dev` is currently at `eb9acfcc`. The adversarial round remains queued. At the latest capacity check, three neighboring adversarial scripts and ten Rust build processes were active, with no browser at that instant.
Author
Owner

dev advanced to c82549b9 with the Search palette/API and OpenAPI updates. I merged that tip cleanly as 1dba9189; the worktree is clean. The post-merge adversarial round is queued against this combined tree, including the updated authorization probe. The host is still running multiple browser and build jobs.

`dev` advanced to `c82549b9` with the Search palette/API and OpenAPI updates. I merged that tip cleanly as `1dba9189`; the worktree is clean. The post-merge adversarial round is queued against this combined tree, including the updated authorization probe. The host is still running multiple browser and build jobs.
Author
Owner

The latest dev merges before the pending verification are recorded on this branch. 57f5556d (Date formats and Appearance, including updated authz/adversarial probes) merged as 8e408884; the only conflict was in crates/plugins/files/src/media.rs. I kept the PID reuse checks from dev and the stale-process-state reset from this branch, then passed git diff --cached --check. fbecfbc6 (the CLAUDE.md performance-review policy) merged as a9a17bc8; it states that periodic benchmarks are not a merge gate. The worktree is clean. The post-merge adversarial round and final workspace gates remain pending shared-host capacity.

The latest `dev` merges before the pending verification are recorded on this branch. `57f5556d` (Date formats and Appearance, including updated authz/adversarial probes) merged as `8e408884`; the only conflict was in `crates/plugins/files/src/media.rs`. I kept the PID reuse checks from `dev` and the stale-process-state reset from this branch, then passed `git diff --cached --check`. `fbecfbc6` (the CLAUDE.md performance-review policy) merged as `a9a17bc8`; it states that periodic benchmarks are not a merge gate. The worktree is clean. The post-merge adversarial round and final workspace gates remain pending shared-host capacity.
Author
Owner

dev advanced to 2a379b185677fd6d85980900c5e757af990d9527 while the shared host was busy. I merged it cleanly as 8302e4a7; it includes the collaboration block-delete/save fix and a larger editor adversarial sweep. The pending run will now exercise this merged tree. Neighboring adversarial, browser, and build jobs remain active, so the runner is still waiting for a clear window.

`dev` advanced to `2a379b185677fd6d85980900c5e757af990d9527` while the shared host was busy. I merged it cleanly as `8302e4a7`; it includes the collaboration block-delete/save fix and a larger editor adversarial sweep. The pending run will now exercise this merged tree. Neighboring adversarial, browser, and build jobs remain active, so the runner is still waiting for a clear window.
Author
Owner

Post-merge adversarial startup stopped before the probes. Cargo's sccache wrapper failed twice while compiling this worktree (calternal-notes-core and calternal-fs), trying to create temporary directories under the missing path /home/kayg/Developer/calternal-wt/glass-audit/target/tmp/. Multiple worktrees were building concurrently with separate TMPDIR values. The adversarial suite made no API requests in this attempt. I will retry with RUSTC_WRAPPER= to bypass the shared sccache wrapper while keeping this worktree's required Cargo environment.

Post-merge adversarial startup stopped before the probes. Cargo's `sccache` wrapper failed twice while compiling this worktree (`calternal-notes-core` and `calternal-fs`), trying to create temporary directories under the missing path `/home/kayg/Developer/calternal-wt/glass-audit/target/tmp/`. Multiple worktrees were building concurrently with separate `TMPDIR` values. The adversarial suite made no API requests in this attempt. I will retry with `RUSTC_WRAPPER=` to bypass the shared sccache wrapper while keeping this worktree's required Cargo environment.
Author
Owner

The live sharing probe reported an Index consistency finding. It uploaded AShared/unindexed.txt, waited 3 seconds, manually deleted the owner's files_index row, then listed Shared/{owner}/AShared as the recipient; a query immediately after the listing found the row present again. The probe expects recipient listing to serve the current Index page and queue reconcile in the background. Its own setup notes that delayed Root bus/Home watcher/search adopters can re-add the row, and this run had heavy shared-host load, so the listing is not yet isolated as the cause. I will verify the sequence before changing server behavior.

The live sharing probe reported an Index consistency finding. It uploaded `AShared/unindexed.txt`, waited 3 seconds, manually deleted the owner's `files_index` row, then listed `Shared/{owner}/AShared` as the recipient; a query immediately after the listing found the row present again. The probe expects recipient listing to serve the current Index page and queue reconcile in the background. Its own setup notes that delayed Root bus/Home watcher/search adopters can re-add the row, and this run had heavy shared-host load, so the listing is not yet isolated as the cause. I will verify the sequence before changing server behavior.
Author
Owner

The live protocol-abuse probe reported that two HTTP connections remained open after 35 seconds: a silent connection and a connection with a partial header. This may indicate an unbounded slow-client connection; the server remained alive and accepted 64 SSE streams in the same phase. The probe ran during heavy shared-host activity. I will inspect the listener timeout and reproduce this check before deciding whether a server fix is needed.

The live protocol-abuse probe reported that two HTTP connections remained open after 35 seconds: a silent connection and a connection with a partial header. This may indicate an unbounded slow-client connection; the server remained alive and accepted 64 SSE streams in the same phase. The probe ran during heavy shared-host activity. I will inspect the listener timeout and reproduce this check before deciding whether a server fix is needed.
Author
Owner

Merged current origin/dev as 66ea927acd (origin/dev 74d6072643). The Journal conflict kept the flat-layout Daily note path matcher and combined it with dev's LinkedHeading and toast behavior. The merge also includes adversarial probe fixes for slowloris and oversized request routing. I will run the required post-merge adversarial round before final gates.

Merged current origin/dev as 66ea927acd459c365172433fe592e4af213461b1 (origin/dev 74d6072643e59cf8668a0cbd9e02b3e36b79736e). The Journal conflict kept the flat-layout Daily note path matcher and combined it with dev's LinkedHeading and toast behavior. The merge also includes adversarial probe fixes for slowloris and oversized request routing. I will run the required post-merge adversarial round before final gates.
Author
Owner

Finding after merging dev at 895d3479: cargo test -p calternal-plugin-files found public_link_options_that_do_not_fit_are_refused returning HTTP 500 when it fetched a public Markdown Note after clearing the password. The Files test fixture applied built-in, Files, Notes and Tags migrations but omitted calternal_plugin::migrations(). Public link info checks Notes enablement through the plugin-state tables, so the incomplete fixture failed its query and returned public link state failed.

Added the missing plugin migration to the Files fixture and retained the status/body assertion for future failures. The exact test passed (1 passed); the full Files crate passed (108 passed, 0 failed). Photos also passed (42 passed, 2 ignored). This was a test-fixture defect, not a live-server finding.

Merge commit: 35a0e287.

Finding after merging `dev` at `895d3479`: `cargo test -p calternal-plugin-files` found `public_link_options_that_do_not_fit_are_refused` returning HTTP 500 when it fetched a public Markdown Note after clearing the password. The Files test fixture applied built-in, Files, Notes and Tags migrations but omitted `calternal_plugin::migrations()`. Public link info checks Notes enablement through the plugin-state tables, so the incomplete fixture failed its query and returned `public link state failed`. Added the missing plugin migration to the Files fixture and retained the status/body assertion for future failures. The exact test passed (1 passed); the full Files crate passed (108 passed, 0 failed). Photos also passed (42 passed, 2 ignored). This was a test-fixture defect, not a live-server finding. Merge commit: `35a0e287`.
Author
Owner

dev advanced to 5af2ff78 during the shared-host wait. That update separates system state from user data and adds a split-directory adversarial runner. I merged it into this branch and resolved two conflicts: the setup fixture now checks the flat Home folders under the configured user-data root, and the Daily note probes keep their flat Notes/ paths while reading from that root. The other Home-file probes now use the user-data root in split mode. Python and shell syntax checks pass. I will run the focused server/filesystem tests, then one full adversarial round through tests/adversarial/run-split.sh.

`dev` advanced to `5af2ff78` during the shared-host wait. That update separates system state from user data and adds a split-directory adversarial runner. I merged it into this branch and resolved two conflicts: the setup fixture now checks the flat Home folders under the configured user-data root, and the Daily note probes keep their flat `Notes/` paths while reading from that root. The other Home-file probes now use the user-data root in split mode. Python and shell syntax checks pass. I will run the focused server/filesystem tests, then one full adversarial round through `tests/adversarial/run-split.sh`.
Author
Owner

Starting Forgejo #165 in worktree /home/kayg/Developer/calternal-wt/flat-layout on branch job/flat-layout. Starting HEAD/base SHA: 9406e02f15e73008641cc37bac7bf40ee612e939. I will merge current dev once now, then finish the remaining implementation and required gates.

Starting Forgejo #165 in worktree `/home/kayg/Developer/calternal-wt/flat-layout` on branch `job/flat-layout`. Starting HEAD/base SHA: `9406e02f15e73008641cc37bac7bf40ee612e939`. I will merge current `dev` once now, then finish the remaining implementation and required gates.
Author
Owner

Finding for #165: apps/web/src/routes/tag/[tag]/+page.svelte resolves a tagged Log entry only when its path matches Notes/Journal/YYYYMMDD-dailynote.md. A Daily note in any other Home folder is indexed but the tag page returns no link. The Notes and Composer E2E flows also still assert the removed Journal/, Attachments/ and Tasks/ layout. I am updating the route and those existing expectations to follow §40.

Finding for #165: `apps/web/src/routes/tag/[tag]/+page.svelte` resolves a tagged Log entry only when its path matches `Notes/Journal/YYYYMMDD-dailynote.md`. A Daily note in any other Home folder is indexed but the tag page returns no link. The Notes and Composer E2E flows also still assert the removed `Journal/`, `Attachments/` and `Tasks/` layout. I am updating the route and those existing expectations to follow §40.
Author
Owner

Adversarial finding: the Calendar Event from Log request returned no response before the probe's 30-second client timeout. At the same time, the round's Photos uploads took 5.2–10.9 seconds and expected duplicate-account requests returned 409 in 5.4–5.5 seconds. This points to shared-host load, but the timeout is a non-SLOW finding, so I am tracking it in a separate issue. The probe was still running at the 15-minute timebox.

Adversarial finding: the `Calendar Event from Log` request returned no response before the probe's 30-second client timeout. At the same time, the round's Photos uploads took 5.2–10.9 seconds and expected duplicate-account requests returned 409 in 5.4–5.5 seconds. This points to shared-host load, but the timeout is a non-SLOW finding, so I am tracking it in a separate issue. The probe was still running at the 15-minute timebox.
Author
Owner

Finding for #165 from the production Notes E2E run: the old fixture posts a Note with folder: "Notes/Work" to POST /api/v1/notes. The server correctly returns 400 (new Notes are written directly in Notes/). I am updating the fixture to create the Note in flat Notes/, then move it into the user-created Notes/Work folder through the Files API.

Finding for #165 from the production Notes E2E run: the old fixture posts a Note with `folder: "Notes/Work"` to `POST /api/v1/notes`. The server correctly returns 400 (`new Notes are written directly in Notes/`). I am updating the fixture to create the Note in flat `Notes/`, then move it into the user-created `Notes/Work` folder through the Files API.
Author
Owner

The Composer production E2E reached its existing chrome precondition but stopped before attachment flows: the mode pill border computed to color(srgb 0.890196 0.890196 0.882353 / 0.6) while the existing check expects transparency. This is unrelated to flat Home routing and is tracked in #251. The Notes production E2E passed and captured the flat Notes explorer and Daily note views.

The Composer production E2E reached its existing chrome precondition but stopped before attachment flows: the mode pill border computed to `color(srgb 0.890196 0.890196 0.882353 / 0.6)` while the existing check expects transparency. This is unrelated to flat Home routing and is tracked in #251. The Notes production E2E passed and captured the flat Notes explorer and Daily note views.
Author
Owner

#165 complete

Branch job/flat-layout is pushed to origin. Head: 1dddb9f1c53d8e20c8d4f4c0bcc90d37bfbdd04e.

Built

Implemented the flat Home layout through Notes, Photos, and Documents. Daily notes and task notes resolve from Notes/; photos use Photos/YYYY/YYYY-MM-DD/; document uploads and attachment routing use Documents locations. Updated note links, tags, search, calendar references, upload handling, generated API contracts, and settings to follow the layout. Updated the E2E fixtures for the new paths.

The branch changes 79 files across apps/web/src/lib/{notes,photos,composer,files}/, web routes and E2E, crates/calternal-notes-core/, crates/plugins/{notes,files,photos}/, related calendar/search/tags/server integrations, contracts/openapi.json, packages/api-client/src/generated.ts, tests/adversarial/, Cargo.lock, and CONTEXT.md.

Two commits:

  • 38c22c3657a52beac3bdb91c4686d279861f0d40 — fix(web): resolve tagged Log entries from any folder
  • 1dddb9f1c53d8e20c8d4f4c0bcc90d37bfbdd04e — test(web): align E2E fixtures with flat Home layout

Gates

cargo fmt --check emitted no formatter diagnostics. The wrapper failed while capturing the exit code because zsh reserves its status variable:

run_gate:6: read-only variable: status

cargo clippy --all-targets -- -D warnings:

sccache: error: failed to execute compile
sccache: caused by: Failed to send data to or receive data from server
sccache: caused by: Failed to read response header
sccache: caused by: Connection reset by peer (os error 104)
error: could not compile `calternal-fs` (test "storage")
EXIT_CODE=101

This was an sccache connection failure in the unrelated calternal-fs storage test build.

cargo test:

EXIT_CODE=0

bun run check:

svelte-check found 0 errors and 0 warnings
EXIT_CODE=0

bun run test:

Test Files 80 passed (80)
Tests 585 passed (585)
EXIT_CODE=0

Targeted notes E2E, against the production build:

notes e2e: ok
NOTES_E2E_EXIT_CODE=0

The Composer E2E stopped at its existing mode-pill border assertion before its attachment flows:

AssertionError [ERR_ASSERTION]: the shared mode pill has no outer border
+ actual - expected
+ 'color(srgb 0.890196 0.890196 0.882353 / 0.6)'
- 'rgba(0, 0, 0, 0)'

Tracked separately as #251.

One 15-minute adversarial round was run and timeboxed:

ADVERSARIAL_EXIT_CODE=124

The round recorded a 30-second timeout for “Calendar Event from Log” under shared-host load; I filed it as #250 for reproduction. SLOW timings were treated as host load. Connection-refused messages after the timebox killed the server were teardown effects, not product findings.

Production screenshots

Captured from the production Notes E2E in both themes and desktop/mobile sizes. Three representative screenshots are attached here:

Notes explorer, desktop, Paper White

Daily gate, desktop, Paper White

Notes explorer, mobile, Tokyo Night

Known gaps

  • Clippy did not finish because sccache disconnected; no retry was made per the finish-now instruction.
  • The adversarial script reached the 15-minute cap; the valid Calendar timeout is tracked in #250.
  • Composer screenshots were not captured because its pre-existing mode-pill assertion stopped that E2E; tracked in #251.
  • The cargo fmt wrapper did not retain the command exit code, though it printed no formatter diagnostics.

Decisions

The design does not specify how an E2E fixture should create a nested Note while Notes creation is flat. The fixture creates it in Notes/, then moves it into the test folder through the Files API.

## #165 complete Branch `job/flat-layout` is pushed to `origin`. Head: `1dddb9f1c53d8e20c8d4f4c0bcc90d37bfbdd04e`. ### Built Implemented the flat Home layout through Notes, Photos, and Documents. Daily notes and task notes resolve from `Notes/`; photos use `Photos/YYYY/YYYY-MM-DD/`; document uploads and attachment routing use Documents locations. Updated note links, tags, search, calendar references, upload handling, generated API contracts, and settings to follow the layout. Updated the E2E fixtures for the new paths. The branch changes 79 files across `apps/web/src/lib/{notes,photos,composer,files}/`, web routes and E2E, `crates/calternal-notes-core/`, `crates/plugins/{notes,files,photos}/`, related calendar/search/tags/server integrations, `contracts/openapi.json`, `packages/api-client/src/generated.ts`, `tests/adversarial/`, `Cargo.lock`, and `CONTEXT.md`. Two commits: - `38c22c3657a52beac3bdb91c4686d279861f0d40` — `fix(web): resolve tagged Log entries from any folder` - `1dddb9f1c53d8e20c8d4f4c0bcc90d37bfbdd04e` — `test(web): align E2E fixtures with flat Home layout` ### Gates `cargo fmt --check` emitted no formatter diagnostics. The wrapper failed while capturing the exit code because zsh reserves its `status` variable: ``` run_gate:6: read-only variable: status ``` `cargo clippy --all-targets -- -D warnings`: ``` sccache: error: failed to execute compile sccache: caused by: Failed to send data to or receive data from server sccache: caused by: Failed to read response header sccache: caused by: Connection reset by peer (os error 104) error: could not compile `calternal-fs` (test "storage") EXIT_CODE=101 ``` This was an sccache connection failure in the unrelated `calternal-fs` storage test build. `cargo test`: ``` EXIT_CODE=0 ``` `bun run check`: ``` svelte-check found 0 errors and 0 warnings EXIT_CODE=0 ``` `bun run test`: ``` Test Files 80 passed (80) Tests 585 passed (585) EXIT_CODE=0 ``` Targeted notes E2E, against the production build: ``` notes e2e: ok NOTES_E2E_EXIT_CODE=0 ``` The Composer E2E stopped at its existing mode-pill border assertion before its attachment flows: ``` AssertionError [ERR_ASSERTION]: the shared mode pill has no outer border + actual - expected + 'color(srgb 0.890196 0.890196 0.882353 / 0.6)' - 'rgba(0, 0, 0, 0)' ``` Tracked separately as #251. One 15-minute adversarial round was run and timeboxed: ``` ADVERSARIAL_EXIT_CODE=124 ``` The round recorded a 30-second timeout for “Calendar Event from Log” under shared-host load; I filed it as #250 for reproduction. SLOW timings were treated as host load. Connection-refused messages after the timebox killed the server were teardown effects, not product findings. ### Production screenshots Captured from the production Notes E2E in both themes and desktop/mobile sizes. Three representative screenshots are attached here: ![Notes explorer, desktop, Paper White](https://git.kayg.org/attachments/07215d29-6280-4e2c-8d78-5890b22925bb) ![Daily gate, desktop, Paper White](https://git.kayg.org/attachments/6e54a5c4-f6ef-4707-a740-3e213611a80c) ![Notes explorer, mobile, Tokyo Night](https://git.kayg.org/attachments/dcb14e11-ecde-4ca4-9625-53e4286089e9) ### Known gaps - Clippy did not finish because sccache disconnected; no retry was made per the finish-now instruction. - The adversarial script reached the 15-minute cap; the valid Calendar timeout is tracked in #250. - Composer screenshots were not captured because its pre-existing mode-pill assertion stopped that E2E; tracked in #251. - The `cargo fmt` wrapper did not retain the command exit code, though it printed no formatter diagnostics. ### Decisions The design does not specify how an E2E fixture should create a nested Note while Notes creation is flat. The fixture creates it in `Notes/`, then moves it into the test folder through the Files API.
Author
Owner

Merged in 060094c3. Orchestrator re-ran clippy (clean; the job's run hit an sccache crash) and the generated-client check (OK).

Merged in 060094c3. Orchestrator re-ran clippy (clean; the job's run hit an sccache crash) and the generated-client check (OK).
kayg referenced this issue from a commit 2026-09-27 17:32:39 +00:00
kayg closed this issue 2026-09-27 17:32:40 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#165
No description provided.