ACTIVITY: internal and hidden files (.calternal/settings.json, dot-files) show as 'files saved' in Calendar, Search and Recent #351

Closed
opened 2026-09-28 15:12:40 +00:00 by kayg · 10 comments
Owner

Owner (2026-09-28, screenshot of the Calendar day: '6:00 PM – 7:00 PM · 1 file saved · settings.json 6:59 PM'): the file is .calternal/settings.json, calternal's own per-user settings (written when Appearance changes). #305 hid write temporaries only; internal files and dot-files still flow into user-facing activity.
Decision (from #305's proposal, orchestrator; confirm if the owner objects): hidden and internal files are never user activity.

  • One predicate in calternal-path (extend the existing reserved/internal-temp check): internal = anything under .calternal/ (settings, versions, collection metadata) and other calternal-owned names; hidden = any path segment starting with '.'.
  • Excluded everywhere user-facing by default: Calendar day activity and counts (files saved, heat map), Search results and suggestions, Recent, the Files listing (the 'Show hidden files' toggle, ⌘⇧., shows hidden but never internal), the notifications inbox, Analytics, Photos.
  • Still synced and versioned as normal files (file over app: e.g. .obsidian/ keeps working); only presentation and indexing change.
  • A migration/backfill removes existing activity/index rows for internal paths.
  • Tests: write .calternal/settings.json via the appearance API and assert: no Calendar activity, no search hit, not in Recent; a user dot-file shows only with the hidden toggle.
    Also from the screenshot: the preview popover covers the 'Show earlier days' button; the anchoring fix in #303 should keep previews clear of other controls where possible.
Owner (2026-09-28, screenshot of the Calendar day: '6:00 PM – 7:00 PM · 1 file saved · settings.json 6:59 PM'): the file is `.calternal/settings.json`, calternal's own per-user settings (written when Appearance changes). #305 hid write temporaries only; internal files and dot-files still flow into user-facing activity. Decision (from #305's proposal, orchestrator; confirm if the owner objects): **hidden and internal files are never user activity.** - One predicate in calternal-path (extend the existing reserved/internal-temp check): **internal** = anything under `.calternal/` (settings, versions, collection metadata) and other calternal-owned names; **hidden** = any path segment starting with '.'. - Excluded everywhere user-facing by default: Calendar day activity and counts (files saved, heat map), Search results and suggestions, Recent, the Files listing (the 'Show hidden files' toggle, ⌘⇧., shows hidden but never internal), the notifications inbox, Analytics, Photos. - Still synced and versioned as normal files (file over app: e.g. `.obsidian/` keeps working); only presentation and indexing change. - A migration/backfill removes existing activity/index rows for internal paths. - Tests: write `.calternal/settings.json` via the appearance API and assert: no Calendar activity, no search hit, not in Recent; a user dot-file shows only with the hidden toggle. Also from the screenshot: the preview popover covers the 'Show earlier days' button; the anchoring fix in #303 should keep previews clear of other controls where possible.
Author
Owner

Starting issue #351 on branch job/hidden-activity. Base SHA: eb4ff20a98.

Starting issue #351 on branch job/hidden-activity. Base SHA: eb4ff20a9862a627f6d0aea1ca617ca9a9491bce.
Author
Owner

Trace evidence: Calendar path_is_visible returns true for every own path before applying its hidden-path check (crates/plugins/calendar/src/view.rs); Search's is_searchable_path only rejects .Trash and .calternal, so ordinary dot-files can be indexed (crates/calternal-search/src/indexer.rs); and Files' show_hidden=true disables the only hidden-path check in the listing route (crates/plugins/files/src/lib.rs). Recent already excludes dot path segments in SQL. Analytics caches finished-day summaries, so its cache needs invalidation when the projection policy changes.

Trace evidence: Calendar `path_is_visible` returns true for every own path before applying its hidden-path check (`crates/plugins/calendar/src/view.rs`); Search's `is_searchable_path` only rejects `.Trash` and `.calternal`, so ordinary dot-files can be indexed (`crates/calternal-search/src/indexer.rs`); and Files' `show_hidden=true` disables the only hidden-path check in the listing route (`crates/plugins/files/src/lib.rs`). Recent already excludes dot path segments in SQL. Analytics caches finished-day summaries, so its cache needs invalidation when the projection policy changes.
Author
Owner

Search suggestion review found that /api/v1/tags aggregates every indexed source path, and the Search field consumes that endpoint for tag suggestions. Hidden Markdown was already skipped during rebuild, but stale/internal tag rows could still appear. I added the shared user-activity predicate at the aggregate response entry point and a regression test for hidden, internal, and visible sources; the tag rows remain available for Files and tag editing.

Search suggestion review found that `/api/v1/tags` aggregates every indexed source path, and the Search field consumes that endpoint for tag suggestions. Hidden Markdown was already skipped during rebuild, but stale/internal tag rows could still appear. I added the shared user-activity predicate at the aggregate response entry point and a regression test for hidden, internal, and visible sources; the tag rows remain available for Files and tag editing.
Author
Owner

Photos event review found a stale-row case: hidden paths were ignored by the invalidation handler, so moving an indexed photo into a dot path could leave its former visible Photos row and CLIP vector. Hidden path events now trigger a targeted lookup by stable Files item ID; the refresh removes the old projection, while visible-item loading still excludes hidden paths. Startup also purges stale semantic and CLIP vector rows before model loading. Regression coverage exercises the hidden move and both index cleanups.

Photos event review found a stale-row case: hidden paths were ignored by the invalidation handler, so moving an indexed photo into a dot path could leave its former visible Photos row and CLIP vector. Hidden path events now trigger a targeted lookup by stable Files item ID; the refresh removes the old projection, while visible-item loading still excludes hidden paths. Startup also purges stale semantic and CLIP vector rows before model loading. Regression coverage exercises the hidden move and both index cleanups.
Author
Owner

Finding during Notifications validation: cargo test -p calternal-plugin-notifications initially had 7 existing store tests fail because the new backfill migration reads Files, Notes, and Calendar source tables that those isolated fixtures did not create (no such table: note_items). The migration succeeds with the production migration ordering; I updated the test fixtures with minimal source-schema stubs and reran the full Notifications crate suite: 18 passed, 0 failed.

Finding during Notifications validation: `cargo test -p calternal-plugin-notifications` initially had 7 existing store tests fail because the new backfill migration reads Files, Notes, and Calendar source tables that those isolated fixtures did not create (`no such table: note_items`). The migration succeeds with the production migration ordering; I updated the test fixtures with minimal source-schema stubs and reran the full Notifications crate suite: 18 passed, 0 failed.
Author
Owner

The first real-server hidden-activity browser run reached Search successfully. Its visible control hit had path: "Inbox/visible-proof.txt", so the e2e assertion incorrectly expected an absolute path suffix and timed out despite a 200 response and the expected visible hit. I corrected the assertion to compare the actual Files-relative API path and am rerunning the browser flow.

The first real-server hidden-activity browser run reached Search successfully. Its visible control hit had `path: "Inbox/visible-proof.txt"`, so the e2e assertion incorrectly expected an absolute path suffix and timed out despite a 200 response and the expected visible hit. I corrected the assertion to compare the actual Files-relative API path and am rerunning the browser flow.
Author
Owner

Adversarial round (real local server): sections feed,notifications,search_hidden,analytics.

Verbatim summary:

server alive at end: True

==== ROUND 2 FINDINGS 0

==== ROUND 2 SLOW 3
 - analytics storm :: 20.4s status 201
 - analytics storm :: 21.8s status 201
 - analytics storm :: 22.3s status 201

All responses were 201; the slow entries are load observations.

Adversarial round (real local server): sections `feed,notifications,search_hidden,analytics`. Verbatim summary: ``` server alive at end: True ==== ROUND 2 FINDINGS 0 ==== ROUND 2 SLOW 3 - analytics storm :: 20.4s status 201 - analytics storm :: 21.8s status 201 - analytics storm :: 22.3s status 201 ``` All responses were 201; the slow entries are load observations.
Author
Owner

Post-merge web gate evidence: bun run check passed (svelte-check found 0 errors and 0 warnings). bun run test ran 747 tests across 113 files and reported 746 passed, 1 failed: src/lib/themes.test.ts > rendered theme contrast contract > keeps text inside every glass surface at 4.5:1 over theme meshes and photo extremes exceeded its 30,000 ms timeout at 31,648 ms. The timeout occurred under shared-host load; I did not change its expectation or timeout. The hidden-activity UI tests passed in this run.

Post-merge web gate evidence: `bun run check` passed (`svelte-check found 0 errors and 0 warnings`). `bun run test` ran 747 tests across 113 files and reported 746 passed, 1 failed: `src/lib/themes.test.ts > rendered theme contrast contract > keeps text inside every glass surface at 4.5:1 over theme meshes and photo extremes` exceeded its 30,000 ms timeout at 31,648 ms. The timeout occurred under shared-host load; I did not change its expectation or timeout. The hidden-activity UI tests passed in this run.
Author
Owner

Issue #351 implementation report

Branch: job/hidden-activity
Head SHA: f7dac3320c371fe63366be3b7298fbed8c1fbdd7

Built

  • Added a shared calternal-path predicate for paths that are not User activity. Calendar, Search and tag suggestions, Recent, Notifications, Analytics, Photos, Files, and derived embedding projections now omit internal paths and hidden paths by default.
  • Added the Files “Show hidden files” toggle (⌘⇧.). It reveals hidden paths, but never internal paths.
  • Added migration/backfill cleanup for existing activity and index rows, plus targeted cleanup for hidden Photos and semantic/CLIP vectors.
  • Added regression coverage and a real-server browser flow for internal and hidden files.

Files

  • Web: apps/web/e2e/hidden-activity.mjs, apps/web/package.json, apps/web/src/lib/files/FilesBrowser.svelte, apps/web/src/lib/files/api.ts, apps/web/src/lib/files/api.test.ts, apps/web/src/lib/search/server.ts, apps/web/src/lib/search/providers.test.ts, apps/web/src/lib/shortcuts/registry.ts, apps/web/src/lib/shortcuts/registry.test.ts.
  • Shared path, Search, tags, server, and derived data: crates/calternal-path/src/lib.rs, crates/calternal-fs/src/lib.rs, crates/calternal-search/src/indexer.rs, crates/calternal-search/src/lib.rs, crates/calternal-search/migrations/0003_hidden_activity.sql, crates/calternal-tags/src/index.rs, crates/calternal-tags/src/lib.rs, crates/calternal-server/src/appearance.rs, crates/calternal-server/src/main.rs, crates/calternal-embed/src/store.rs, crates/calternal-embed/src/clip_store.rs.
  • Plugins: crates/plugins/analytics/migrations/0002_hidden_activity.sql, crates/plugins/analytics/src/lib.rs, crates/plugins/analytics/src/tests.rs, crates/plugins/calendar/src/view.rs, crates/plugins/files/src/lib.rs, crates/plugins/files/src/listing.rs, crates/plugins/files/src/lookup.rs, crates/plugins/files/src/shares.rs, crates/plugins/notifications/migrations/0004_hidden_activity.sql, crates/plugins/notifications/src/block_reminders.rs, crates/plugins/notifications/src/reminders.rs, crates/plugins/notifications/src/store.rs, crates/plugins/photos/migrations/0006_hidden_activity.sql, crates/plugins/photos/src/index.rs, crates/plugins/photos/src/lib.rs, crates/plugins/photos/src/migration_tests.rs.
  • Adversarial probe: tests/adversarial/attack2.py.

Visual evidence

Calendar day and Files with the hidden toggle, at 390, 820, and 1440 px in light and dark themes:

Gates

cargo fmt --check exited 0 and printed no output.

cargo clippy --all-targets -- -D warnings did not complete. The original invocation remained in native dependency builds and was stopped at the job time cap. A duplicate invocation was cancelled while waiting for Cargo locks; its output was:

Blocking waiting for file lock on package cache
Blocking waiting for file lock on package cache
Blocking waiting for file lock on build directory

cargo test did not complete. It was interrupted with exit code 130 while the vendored OpenSSL make build_libs step was still compiling. No test summary was produced.

bun run check exited 0. Output:

$ svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/hidden-activity/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test exited 1. Output summary:

Test Files  8 failed | 105 passed (113)
Tests  8 failed | 739 passed (747)
Duration  468.11s (transform 57%, environment 18%, import 12%, tests 9%, setup 3%)
error: script "test" exited with code 1

The eight failures were timeout errors in the theme contrast, font menu, shared menu focus, Calendar TimeGrid, keyboard shortcuts, Composer chips, Analytics RangeBar, and Analytics StatRow tests. I did not change their expectations or timeouts.

Adversarial round and known gaps

The prior adversarial round recorded in this issue, before the final dev merge, reported 0 findings and 3 slow Analytics storm responses (20.4s, 21.8s, and 22.3s). I did not complete a new post-merge adversarial round after the final merge of dev, which updated auth storage and server wire code.

Decisions

DESIGN.md does not define the hidden-activity rule. I followed issue #351’s proposal: every dot-prefixed path segment is hidden; calternal-owned and internal paths are never User activity and stay hidden even when the Files toggle is on. I applied the shared rule at existing surface entry points and used migrations to remove stale projections. Tag suggestions are filtered at their aggregate API entry point because Search consumes that endpoint; tag editing remains available in Files.

Build output was cleaned with cargo clean (6.2 GiB removed), and apps/web/build plus apps/web/.svelte-kit were deleted. The worktree was clean at report time. No issue was closed.

# Issue #351 implementation report Branch: `job/hidden-activity` Head SHA: `f7dac3320c371fe63366be3b7298fbed8c1fbdd7` ## Built - Added a shared `calternal-path` predicate for paths that are not User activity. Calendar, Search and tag suggestions, Recent, Notifications, Analytics, Photos, Files, and derived embedding projections now omit internal paths and hidden paths by default. - Added the Files “Show hidden files” toggle (`⌘⇧.`). It reveals hidden paths, but never internal paths. - Added migration/backfill cleanup for existing activity and index rows, plus targeted cleanup for hidden Photos and semantic/CLIP vectors. - Added regression coverage and a real-server browser flow for internal and hidden files. ## Files - Web: `apps/web/e2e/hidden-activity.mjs`, `apps/web/package.json`, `apps/web/src/lib/files/FilesBrowser.svelte`, `apps/web/src/lib/files/api.ts`, `apps/web/src/lib/files/api.test.ts`, `apps/web/src/lib/search/server.ts`, `apps/web/src/lib/search/providers.test.ts`, `apps/web/src/lib/shortcuts/registry.ts`, `apps/web/src/lib/shortcuts/registry.test.ts`. - Shared path, Search, tags, server, and derived data: `crates/calternal-path/src/lib.rs`, `crates/calternal-fs/src/lib.rs`, `crates/calternal-search/src/indexer.rs`, `crates/calternal-search/src/lib.rs`, `crates/calternal-search/migrations/0003_hidden_activity.sql`, `crates/calternal-tags/src/index.rs`, `crates/calternal-tags/src/lib.rs`, `crates/calternal-server/src/appearance.rs`, `crates/calternal-server/src/main.rs`, `crates/calternal-embed/src/store.rs`, `crates/calternal-embed/src/clip_store.rs`. - Plugins: `crates/plugins/analytics/migrations/0002_hidden_activity.sql`, `crates/plugins/analytics/src/lib.rs`, `crates/plugins/analytics/src/tests.rs`, `crates/plugins/calendar/src/view.rs`, `crates/plugins/files/src/lib.rs`, `crates/plugins/files/src/listing.rs`, `crates/plugins/files/src/lookup.rs`, `crates/plugins/files/src/shares.rs`, `crates/plugins/notifications/migrations/0004_hidden_activity.sql`, `crates/plugins/notifications/src/block_reminders.rs`, `crates/plugins/notifications/src/reminders.rs`, `crates/plugins/notifications/src/store.rs`, `crates/plugins/photos/migrations/0006_hidden_activity.sql`, `crates/plugins/photos/src/index.rs`, `crates/plugins/photos/src/lib.rs`, `crates/plugins/photos/src/migration_tests.rs`. - Adversarial probe: `tests/adversarial/attack2.py`. ## Visual evidence Calendar day and Files with the hidden toggle, at 390, 820, and 1440 px in light and dark themes: - Calendar 390: [light](https://git.kayg.org/attachments/facc1da8-1d9e-46d0-a7d2-ef926000e01f) · [dark](https://git.kayg.org/attachments/32f35c4e-cb70-4f37-aa0a-4190171dd9ec) - Calendar 820: [light](https://git.kayg.org/attachments/42dc2a28-3b76-4151-81ba-49dc6234f04c) · [dark](https://git.kayg.org/attachments/72e7e167-03f1-4d82-8adb-983ce8bb55db) - Calendar 1440: [light](https://git.kayg.org/attachments/7085e843-47ac-4e88-b79a-9fd4aea8673a) · [dark](https://git.kayg.org/attachments/078d87b0-fe90-4877-9aad-f38639ca127e) - Files 390: [light](https://git.kayg.org/attachments/4c6ae884-35ea-4b09-88d1-534861428af4) · [dark](https://git.kayg.org/attachments/3e807dd1-b3d7-4144-b346-9e7f6fdc2bad) - Files 820: [light](https://git.kayg.org/attachments/c993e20a-60bc-49f1-a06a-d21ed045713e) · [dark](https://git.kayg.org/attachments/444c7799-bc27-44b0-93b4-e6041b05e019) - Files 1440: [light](https://git.kayg.org/attachments/de1f2d16-c429-4674-a3cb-8080c702ed9d) · [dark](https://git.kayg.org/attachments/48b901ca-fbc6-4dbb-9905-08dafc019a4e) ## Gates `cargo fmt --check` exited 0 and printed no output. `cargo clippy --all-targets -- -D warnings` did not complete. The original invocation remained in native dependency builds and was stopped at the job time cap. A duplicate invocation was cancelled while waiting for Cargo locks; its output was: ``` Blocking waiting for file lock on package cache Blocking waiting for file lock on package cache Blocking waiting for file lock on build directory ``` `cargo test` did not complete. It was interrupted with exit code 130 while the vendored OpenSSL `make build_libs` step was still compiling. No test summary was produced. `bun run check` exited 0. Output: ``` $ svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/hidden-activity/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test` exited 1. Output summary: ``` Test Files 8 failed | 105 passed (113) Tests 8 failed | 739 passed (747) Duration 468.11s (transform 57%, environment 18%, import 12%, tests 9%, setup 3%) error: script "test" exited with code 1 ``` The eight failures were timeout errors in the theme contrast, font menu, shared menu focus, Calendar TimeGrid, keyboard shortcuts, Composer chips, Analytics RangeBar, and Analytics StatRow tests. I did not change their expectations or timeouts. ## Adversarial round and known gaps The prior adversarial round recorded in this issue, before the final `dev` merge, reported 0 findings and 3 slow Analytics storm responses (20.4s, 21.8s, and 22.3s). I did not complete a new post-merge adversarial round after the final merge of `dev`, which updated auth storage and server wire code. ## Decisions `DESIGN.md` does not define the hidden-activity rule. I followed issue #351’s proposal: every dot-prefixed path segment is hidden; calternal-owned and internal paths are never User activity and stay hidden even when the Files toggle is on. I applied the shared rule at existing surface entry points and used migrations to remove stale projections. Tag suggestions are filtered at their aggregate API entry point because Search consumes that endpoint; tag editing remains available in Files. Build output was cleaned with `cargo clean` (6.2 GiB removed), and `apps/web/build` plus `apps/web/.svelte-kit` were deleted. The worktree was clean at report time. No issue was closed.
Author
Owner

Merged into dev by Claude after visual review (a9a16e90) and deployed to calternal.cloud. Closing.

Merged into dev by Claude after visual review (a9a16e90) and deployed to calternal.cloud. Closing.
kayg closed this issue 2026-09-28 22:31:05 +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#351
No description provided.