Skip to content

Feat/activity log backend - #367

Merged
Abhash-Chakraborty merged 13 commits into
Abhash-Chakraborty:canaryfrom
payalrvs3:feat/activity-log-backend
Aug 1, 2026
Merged

Feat/activity log backend#367
Abhash-Chakraborty merged 13 commits into
Abhash-Chakraborty:canaryfrom
payalrvs3:feat/activity-log-backend

Conversation

@payalrvs3

@payalrvs3 payalrvs3 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the backend half of the local activity log: an append-only Activity model, a best-effort recorder wired into upload, archive/trash/restore, vault lock/unlock/restore, and settings-change flows, and a paginated/ filterable read + retention API. UI route is a follow-up PR.

Part of #354 - this covers the data model, recording, and API; the timeline UI and accessibility pass are tracked separately in that issue, so I've used "Part of" rather than "Fixes" to avoid auto-closing it.

Type of change

  • Feature

Release impact

  • No user-visible release note needed
  • Minor (backward-compatible feature)
  • Patch (backward-compatible fix)
  • Major (breaking change)
  • Critical/security fix (keep sensitive details private)

What changed

  • New Activity model + migration (single head, full downgrade()).
  • record_activity() - best-effort writer, never raises, wired at upload completion/failure, archive/trash/restore, vault lock/unlock/restore, and settings changes.
  • GET/POST /activity, /activity/clear, /activity/purge - scoped via new scope_activity_query (mirrors scope_media_query).
  • ACTIVITY_RETENTION_DAYS (default 90), mirrors TRASH_RETENTION_DAYS.
  • Payloads are a hand-picked allow-list per site - never image bytes, embeddings, OCR text, captions, secrets, or session tokens.

How to test

cd backend && uv run ruff check . && uv run ruff format --check . && uv run pytest tests/ -v
→ 545 passed, 6 skipped, 0 regressions (19 new tests in test_activity.py).

Checklist

  • I linked the related issue
  • I ran required checks from CONTRIBUTING.md
  • I updated docs/env notes if needed
  • My PR is scoped to a single issue (backend half; UI is a follow-up)
  • I followed commit message conventions
  • I am not committing secrets or local artifacts
  • This PR targets canary unless it is the maintainer promotion PR

GSSoC'26 checklist

  • I requested issue assignment before starting
  • I have meaningful commits (no spam commits)
  • I am ready to explain my implementation in review comments

Summary by CodeRabbit

  • New Features
    • Added a local activity log covering uploads, media actions, vault events, and settings changes.
    • Added activity listing with filtering and pagination.
    • Added controls to clear activity and purge entries based on a configurable retention period.
    • Added privacy-conscious activity details and user-based visibility controls.
  • Documentation
    • Documented activity tracking, retention, privacy, and shared-mode access behavior.
  • Bug Fixes
    • Improved vault lock attribution and shared-mode behavior.

Signed-off-by: Payalrvs0310@gmail.com <Payalrvs0310@gmail.com>
@github-actions github-actions Bot added the needs linked issue Pull request needs to link a valid issue before review. label Jul 19, 2026
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

PR Context Summary

Suggested issue links

Use Fixes #123 or Closes #123 in the PR body when one of the suggestions is the intended issue.
Manual rerun: Actions > PR Context Triage > Run workflow > set pr_number and force_review=true.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Abhash-Chakraborty, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e3493574-68da-45ed-a2dc-0923953c96c6

📥 Commits

Reviewing files that changed from the base of the PR and between 47e8f9c and 1148520.

📒 Files selected for processing (6)
  • backend/src/find_api/core/config.py
  • backend/src/find_api/main.py
  • backend/src/find_api/routers/activity.py
  • backend/src/find_api/services/activity_log.py
  • backend/src/find_api/workers/jobs.py
  • backend/tests/test_activity.py
📝 Walkthrough

Walkthrough

Adds a local append-only activity log with SQLAlchemy persistence, scoped listing and deletion APIs, configurable retention, event recording across media, vault, settings, and image processing flows, comprehensive tests, and feature documentation.

Changes

Activity Log

Layer / File(s) Summary
Activity persistence and contracts
backend/alembic/versions/..., backend/src/find_api/models/*, backend/src/find_api/core/...
Defines the activity table and Activity model, registers and exports it, and adds 90-day retention configuration.
Scoped activity management API
backend/src/find_api/routers/activity.py, backend/src/find_api/core/dependencies.py, backend/src/find_api/main.py
Adds scoped listing, filtering, pagination, clearing, retention purge, serialization, and router registration.
Activity recording integrations
backend/src/find_api/services/activity_log.py, backend/src/find_api/routers/{config,gallery,vault}.py, backend/src/find_api/workers/jobs.py
Adds best-effort recording and emits events for settings, media lifecycle, vault, and image-processing outcomes.
Validation and documentation
backend/tests/test_activity.py, backend/tests/test_vault.py, docs/guides/features.md
Tests event recording, failure handling, API behavior, retention, access scoping, and vault attribution, and documents the activity feature.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EventSource
  participant record_activity
  participant ActivityDatabase
  EventSource->>record_activity: record completed state change
  record_activity->>ActivityDatabase: insert and commit Activity
  ActivityDatabase-->>record_activity: commit result
Loading

Possibly related issues

  • Issue 354 — Covers the privacy-safe, user-scoped activity model, retention, event logging, APIs, tests, and documentation implemented here.

Possibly related PRs

  • Abhash-Chakraborty/Find#376 — Contains corresponding backend activity migration, model, scoping, APIs, purge/clear logic, and event instrumentation.

Suggested reviewers: abhash-chakraborty

Poem

A rabbit logs each hop and swipe,
In tidy rows both day and night.
Vaults unlock, old files restore,
Settings change, then logs record.
With ninety carrots keeping time,
The activity trail grows fine! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: the backend activity log feature.
Description check ✅ Passed The description largely matches the template with summary, type, release impact, changes, testing, and checklist details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread backend/src/find_api/routers/vault.py
Comment thread backend/src/find_api/routers/gallery.py
Comment thread backend/src/find_api/core/config.py
@macroscopeapp

macroscopeapp Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Unable to check for correctness in 1121670. This PR introduces a new activity logging feature with database schema changes, new API endpoints, and integration across multiple existing routers and workers. All 16 changed files are owned by a different developer (Abhash-Chakraborty), warranting review by the designated code owner.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/find_api/routers/activity.py`:
- Around line 42-111: Move the database query construction and execution from
list_activity, clear_activity, and purge_expired_activity into
services/activity_log.py, reusing or adding service functions for scoped
filtering, ordering, pagination, counting, and bulk deletion. Keep the router
handlers as thin HTTP adapters that validate inputs, call the activity-log
service, and return the existing response shapes and messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7eeb9362-cd78-4af7-bba7-b91a7b4cbdc3

📥 Commits

Reviewing files that changed from the base of the PR and between 162cb44 and b5e18a0.

📒 Files selected for processing (15)
  • backend/alembic/versions/20260715_activity_log.py
  • backend/src/find_api/core/config.py
  • backend/src/find_api/core/database.py
  • backend/src/find_api/core/dependencies.py
  • backend/src/find_api/main.py
  • backend/src/find_api/models/__init__.py
  • backend/src/find_api/models/activity.py
  • backend/src/find_api/routers/activity.py
  • backend/src/find_api/routers/config.py
  • backend/src/find_api/routers/gallery.py
  • backend/src/find_api/routers/vault.py
  • backend/src/find_api/services/activity_log.py
  • backend/src/find_api/workers/jobs.py
  • backend/tests/test_activity.py
  • docs/guides/features.md

Comment thread backend/src/find_api/routers/activity.py
Signed-off-by: Payalrvs0310@gmail.com <Payalrvs0310@gmail.com>
@payalrvs3 payalrvs3 mentioned this pull request Jul 28, 2026
16 tasks
@Abhash-Chakraborty Abhash-Chakraborty linked an issue Jul 29, 2026 that may be closed by this pull request
7 tasks
@Abhash-Chakraborty Abhash-Chakraborty removed the needs linked issue Pull request needs to link a valid issue before review. label Jul 29, 2026
@github-actions github-actions Bot added the needs linked issue Pull request needs to link a valid issue before review. label Jul 29, 2026
@Abhash-Chakraborty Abhash-Chakraborty added enhancement New feature, improvement, or enhancement request. gssoc26 Related to GirlScript Summer of Code 2026. backend FastAPI, database, storage, and API work type:feature Feature PR. GSSoC type bonus. under-review Maintainer needs to verify and removed needs linked issue Pull request needs to link a valid issue before review. labels Jul 29, 2026
@github-actions github-actions Bot added the needs linked issue Pull request needs to link a valid issue before review. label Jul 29, 2026
@github-actions

Copy link
Copy Markdown

@macroscope-app review

Please review this PR against its linked issue, local-first privacy rules, and the current Find repo instructions.
Linked issue(s): none linked yet.
Trigger source: label-gated review (under-review).

@Abhash-Chakraborty Abhash-Chakraborty removed the needs linked issue Pull request needs to link a valid issue before review. label Jul 29, 2026
@github-actions github-actions Bot added the needs linked issue Pull request needs to link a valid issue before review. label Jul 29, 2026
@Abhash-Chakraborty

Copy link
Copy Markdown
Owner

This is excellent work. The things I'd normally have to ask for are already here: record_activity() commits in its own transaction and swallows its own failures so a missing diagnostic row can never roll back real work, SET NULL on both FKs so history survives a delete, the payload allow-list is hand-picked per call site rather than dumping objects, and scope_activity_query mirrors scope_media_query instead of inventing a second scoping rule. The migration has a real downgrade() and a single head. 310 lines of tests including the IDOR case. Thank you.

One thing to fix before I merge:

lock_vault records without a user_id. In routers/vault.py, unlock_vault now attributes the row to user.id, but the lock path calls record_activity(db, "vault", "locked") with no user. In shared mode scope_activity_query filters on Activity.user_id == user.id, so a regular member sees "Vault unlocked" in their feed and then never sees the matching "Vault locked" — the pair is asymmetric. lock_vault doesn't currently take a user dependency, so it needs user: Optional[User] = Depends(get_required_user) added alongside the db you already introduced, then pass user_id=user.id if user else None.

One note, not a change request: in gallery.py you attribute archive/trash/restore to media.uploader_user_id rather than the acting user, so in shared mode an admin acting on someone else's photo files the entry under the owner. I think that's the right default for an "what happened to my photos" feed, but please add a one-line comment saying it's deliberate so nobody 'fixes' it later.

Fix the lock attribution and I'll merge this.

@Abhash-Chakraborty Abhash-Chakraborty added quality:clean Clean and maintainable PR. GSSoC contributor multiplier: 1.2x. gssoc:approved Valid GSSoC contribution approved for scoring. privacy Data privacy, security boundaries, and user trust level:advanced GSSoC difficulty level: advanced. Base contributor points: 55. and removed needs linked issue Pull request needs to link a valid issue before review. labels Jul 29, 2026
Signed-off-by: Payalrvs0310@gmail.com <Payalrvs0310@gmail.com>
@payalrvs3

Copy link
Copy Markdown
Contributor Author

Hey @Abhash-Chakraborty, Branch also picked up #377/#379 from your side along the way, let me know if you'd rather I do anything about that, otherwise this is ready on the vault fix.

Abhash-Chakraborty and others added 2 commits July 30, 2026 15:38
Attributing the lock to the acting user via get_required_user broke the
route once an admin exists. The client sends the *vault* session token in
the Authorization header, and get_required_user reads that same header:
get_current_user cannot resolve a vault token to a user, so shared mode
took the 401 branch and the vault never locked server-side.

Resolve the actor from the find_session login cookie instead, using the
non-raising get_current_user. The frontend already sends the cookie
(withCredentials), so real sessions still get attribution, and a
cookie-less API client locks successfully with an unattributed row.

The existing lock test only ran in local mode, where get_required_user
returns None, so nothing caught this. Added shared-mode coverage for both
the status code and the attribution.

Claude-Session: https://claude.ai/code/session_01UBTPUoE8ZKveSPEuhAWkVT

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@Abhash-Chakraborty

Copy link
Copy Markdown
Owner

Pushed a fix to your branch rather than sending you round again for it — the lock attribution was right in principle but it 401s in shared mode, and that's my fault for asking for it without flagging the header collision.

get_required_user reads the Authorization header, and on this one route that header carries the vault session token, not a login session. get_current_user can't resolve a vault token to a user, so once an admin exists the dependency takes its 401 branch and the vault never locks server-side. The existing lock test only ran in local mode, where the dependency returns None, which is why nothing caught it.

The actor now comes from the find_session cookie via the non-raising get_current_user. The frontend already sends it (withCredentials: true), so real sessions still get attributed, and a cookie-less API client locks fine with an unattributed row. Added shared-mode coverage for both the status code and the attribution — they fail on your commit and pass on mine.

Nothing else outstanding from me. Once CI is green this is going in.

On #377/#379 riding along: leave them, they're mine and they'll disappear when this squashes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
backend/tests/test_vault.py (1)

532-552: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the cookie-less attribution branch.

This verifies that locking succeeds, but not that its vault/locked activity row is unattributed. Query the latest locked row and assert user_id is None to protect the intended cookie-less behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_vault.py` around lines 532 - 552, Update
test_lock_succeeds_with_vault_token_in_header to query the latest vault/locked
activity row after the lock request and assert its user_id is None, preserving
the existing response assertions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/tests/test_vault.py`:
- Around line 532-552: Update test_lock_succeeds_with_vault_token_in_header to
query the latest vault/locked activity row after the lock request and assert its
user_id is None, preserving the existing response assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e80f3780-97c0-4f70-ac67-beca42058914

📥 Commits

Reviewing files that changed from the base of the PR and between 274aed4 and 47e8f9c.

📒 Files selected for processing (2)
  • backend/src/find_api/routers/vault.py
  • backend/tests/test_vault.py

@Abhash-Chakraborty Abhash-Chakraborty added ready-to-merge Fully approved, tested, and cleared for immediate merging. and removed needs linked issue Pull request needs to link a valid issue before review. under-review Maintainer needs to verify labels Jul 30, 2026

@Abhash-Chakraborty Abhash-Chakraborty left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The vault-lock regression is fixed and covered, everything else here was already in good shape — best-effort recording that can't roll back real work, SET NULL on both FKs, a hand-picked payload allow-list, and scope_activity_query mirroring the media scoping. Marked ready-to-merge.

Also cleared the needs linked issue label: you did link #354, the triage bot just doesn't recognise "Part of" as a link. Using it deliberately so #354 stays open for the UI half was the right call — that's on my tooling, not you.

@github-actions github-actions Bot added the needs linked issue Pull request needs to link a valid issue before review. label Jul 30, 2026
Abhash-Chakraborty added a commit that referenced this pull request Jul 30, 2026
Every pip-authored PR against /backend has been dead on arrival. The pip
ecosystem edits pyproject.toml and stops there -- it does not know uv.lock
exists. backend-check runs `uv sync --locked`, so each PR failed with:

    error: The lockfile at `uv.lock` needs to be updated, but `--locked`
    was provided.

That killed #387, #388, #389, #390 and #391 in the same batch, and it is
not a new problem: the backend pins have been frozen behind it long enough
that pgvector is still on 0.2.4 (current 0.5.0) and minio on 7.2.3
(current 7.2.20). The major/minor split added in #384 could not help,
because the failure has nothing to do with which versions were proposed.

The uv ecosystem updates the manifest and the lockfile in the same commit,
which is the only shape that can pass this repo's CI. The torch/torchvision
ignore and the group split carry over unchanged.

Also fixes the linked-issue check in pr-context-triage, which only counted
closing keywords. A PR implementing half of a tracking issue has to write
"Part of #N" instead of "Closes #N" or merging it auto-closes work that is
still open -- so the rule punished contributors for splitting work
correctly, and mislabelled #367 as `needs linked issue` when it linked #354
on purpose. Non-closing links now count, matched off explicit keywords
rather than any bare #N so a passing mention of an unrelated PR does not
qualify. The Macroscope trigger comment reports both kinds too, since a
"Part of" issue still holds the acceptance criteria the review is judged on.

Claude-Session: https://claude.ai/code/session_01UBTPUoE8ZKveSPEuhAWkVT

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@Abhash-Chakraborty Abhash-Chakraborty removed the needs linked issue Pull request needs to link a valid issue before review. label Jul 30, 2026

@Abhash-Chakraborty Abhash-Chakraborty left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — migration present, reads owner-scoped via scope_activity_query, payload contract documented and privacy-safe.

@Abhash-Chakraborty Abhash-Chakraborty left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — migration present, reads owner-scoped via scope_activity_query, payload contract documented and privacy-safe.

Two review findings.

ACTIVITY_RETENTION_DAYS accepted negative values. 0 is a documented "keep
forever" switch, but -1 has no meaning and silently disabled cleanup rather
than failing, so an environment typo looked like it worked. Now validated as
non-negative, along with TRASH_RETENTION_DAYS, which had the same gap.

list_activity/clear_activity/purge_expired_activity built and ran their
SQLAlchemy filtering, ordering, pagination and bulk deletes inline in the
router. services/activity_log.py already existed for record_activity and is
the natural home for the rest, per the thin-router guideline. The endpoints
now do request/response handling only; owner scoping moves with the queries
so it cannot be forgotten at one call site.

@Abhash-Chakraborty Abhash-Chakraborty left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — the retention validator and router/service split are fixed in code; migration, owner scoping, and privacy-safe payload contract all check out.

@Abhash-Chakraborty Abhash-Chakraborty left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — retention validator and router/service split fixed in code; migration, owner scoping and payload contract all verified.

@Abhash-Chakraborty
Abhash-Chakraborty merged commit 2c17c82 into Abhash-Chakraborty:canary Aug 1, 2026
17 of 18 checks passed
Abhash-Chakraborty added a commit to payalrvs3/Find that referenced this pull request Aug 1, 2026
Abhash-Chakraborty#367 landed as a squash commit, so the backend half of the activity log arrives
in canary as new content rather than as this branch's own commits. That turns
the three files this branch carried from Abhash-Chakraborty#367 into add/add conflicts:
routers/activity.py, services/activity_log.py, and tests/test_activity.py.

Resolved by taking canary wholesale for backend/ and docs/, since everything
this branch held there was an older copy of Abhash-Chakraborty#367's work -- including the
pre-review router, before the query logic moved into services/activity_log.py.
The diff against canary is now frontend only.
Abhash-Chakraborty pushed a commit that referenced this pull request Aug 1, 2026
Frontend half of the local activity log, over the /api/activity endpoints from
#367.

- /activity route: day-grouped feed, category filters (upload/media/vault/
  settings), load-more pagination, and a Clear control.
- lib/activity.ts holds pure describeActivity/activityTone/groupActivityByDay,
  unit-tested without rendering.
- lib/api.ts gains getActivity/clearActivity/purgeActivity.
- Nav link added under Utilities.
- Retention purge fires on page view, mirroring how Trash auto-purges on open.

States and accessibility: skeleton with role=status/aria-busy, role=alert with a
retry on the error path, empty state, aria-pressed filter chips, sr-only
fieldset legend, aria-hidden icons. All colours resolve through the CSS custom
properties, so light and dark both stay readable.

Pagination compensates for offset drift: the feed is newest-first and
append-only, so rows recorded mid-pagination would otherwise make Load more
re-serve visible rows and skip the same number of older ones. The next offset is
pushed past the drift between the live total and the first page's snapshot, and
the flatten step drops ids already rendered so page seams cannot duplicate a
row. The retention purge invalidates the activity query once it has actually
deleted something, so it can no longer lose the race against the initial load.

Fixes #354

Co-authored-by: payalrvs3 <Payalrvs0310@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend FastAPI, database, storage, and API work enhancement New feature, improvement, or enhancement request. gssoc:approved Valid GSSoC contribution approved for scoring. gssoc26 Related to GirlScript Summer of Code 2026. level:advanced GSSoC difficulty level: advanced. Base contributor points: 55. privacy Data privacy, security boundaries, and user trust quality:clean Clean and maintainable PR. GSSoC contributor multiplier: 1.2x. ready-to-merge Fully approved, tested, and cleared for immediate merging. type:feature Feature PR. GSSoC type bonus.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add a privacy-safe local activity and processing history

2 participants