Skip to content

feat(analytics): read-only SQL views layer with analytics schema - #12367

Merged
majdyz merged 12 commits into
devfrom
feat/analytics-views
Mar 13, 2026
Merged

feat(analytics): read-only SQL views layer with analytics schema#12367
majdyz merged 12 commits into
devfrom
feat/analytics-views

Conversation

@majdyz

@majdyz majdyz commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Changes 🏗️

Adds autogpt_platform/analytics/ — 14 SQL view definitions that expose production data safely through a locked-down analytics schema.

Security model:

  • Views use security_invoker = false (PostgreSQL 15+), so they execute as their owner (postgres), not the caller
  • analytics_readonly role only has access to analytics.* — cannot touch platform or auth tables directly

Files:

  • backend/generate_views.py — does everything; auto-reads credentials from backend/.env
  • analytics/queries/*.sql — 14 documented view definitions (auth, user activity, executions, onboarding funnel, cohort retention)

Running locally (dev)

cd autogpt_platform/backend

# First time only — creates analytics schema, role, grants
poetry run analytics-setup

# Create / refresh views (auto-reads backend/.env)
poetry run analytics-views

Running in production (Supabase)

cd autogpt_platform/backend

# Step 1 — first time only (run in Supabase SQL Editor as postgres superuser)
poetry run analytics-setup --dry-run
# Paste the output into Supabase SQL Editor and run

# Step 2 — apply views (use direct connection host, not pooler)
poetry run analytics-views --db-url "postgresql://postgres:PASSWORD@db.<ref>.supabase.co:5432/postgres"

# Step 3 — set password for analytics_readonly so external tools can connect
# Run in Supabase SQL Editor:
# ALTER ROLE analytics_readonly WITH PASSWORD 'your-password';

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • Setup + views applied cleanly on local Postgres 15
    • analytics_readonly can SELECT from all 14 analytics.* views
    • analytics_readonly gets permission denied on platform.* and auth.* directly

Introduces an analytics/ layer that wraps production Postgres data in
safe, read-only views exposed under the analytics schema.

- 14 documented query files in queries/ (one per Looker data source)
  covering auth activities, user activity, execution metrics, onboarding
  funnel, and cohort retention (login + execution, weekly + daily)
- setup.sql — one-time schema creation and role/grant setup for the
  analytics_readonly role (auth, platform, analytics schemas)
- generate_views.py — reads queries/*.sql and applies
  CREATE OR REPLACE VIEW analytics.<name> to the database;
  supports --dry-run, --only, and --db-url flags
- views.sql — pre-generated combined reference output
- README.md — full setup, deployment, and integration guide

Looker, PostHog Data Warehouse, and Supabase MCP (for Otto) all
connect to the same analytics.* views instead of raw tables.
@majdyz
majdyz requested a review from a team as a code owner March 11, 2026 09:00
@majdyz
majdyz requested review from Bentlybro and Pwuts and removed request for a team March 11, 2026 09:00
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Mar 11, 2026
@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds 14 new Postgres analytics views covering auth, execution, node/block, retention, onboarding, spending, and user activity, plus a new Python CLI utility to create/apply those views and related schema/grants.

Changes

Cohort / File(s) Summary
Authentication & User Activity
autogpt_platform/analytics/queries/auth_activities.sql, autogpt_platform/analytics/queries/users_activities.sql
New views: analytics.auth_activities (90-day audit log events) and analytics.users_activities (per-user lifetime activity aggregates, status counts, and 7-day active flag).
Execution & Node/Block Metrics
autogpt_platform/analytics/queries/graph_execution.sql, autogpt_platform/analytics/queries/node_block_execution.sql
New views unpacking JSONB stats into numeric metrics, normalizing execution status (including NO_CREDITS mapping), deriving agent/graph/block names, scrubbing IDs/URLs from error messages, and joining LibraryAgent metadata (90-day window).
Agent Retention Cohorts
autogpt_platform/analytics/queries/retention_agent.sql
New view computing weekly cohort retention per agent over 180 days with multi-CTE cohort sizing, caps, and wide output columns for cohort/agent labels and retention rates.
Execution Retention (Daily/Weekly)
autogpt_platform/analytics/queries/retention_execution_daily.sql, autogpt_platform/analytics/queries/retention_execution_weekly.sql
Daily and weekly execution-retention views (first-exec cohorts) with bounded/unbounded retention counts, cohort caps, grid generation, and 90–180 day windows.
Login Retention Variants
autogpt_platform/analytics/queries/retention_login_daily.sql, autogpt_platform/analytics/queries/retention_login_weekly.sql, autogpt_platform/analytics/queries/retention_login_onboarded_weekly.sql
Daily/weekly login-based cohort retention views plus an onboarded-weekly variant (onboarded within 365 days), computing bounded/unbounded retention and cohort metrics with caps and safeguards.
Onboarding & Funnel
autogpt_platform/analytics/queries/user_onboarding.sql, autogpt_platform/analytics/queries/user_onboarding_funnel.sql, autogpt_platform/analytics/queries/user_onboarding_integration.sql
Views exposing onboarding records, a funnel view that expands completedSteps and computes pct_from_prev, and an integrations view counting distinct users per selected integration (90-day window).
Spending / Credit Usage
autogpt_platform/analytics/queries/user_block_spending.sql
New view aggregating credit transactions joined to node executions with token counts, provider/model info, negativeAmount, and 90-day filter.
View Deployment Tooling
autogpt_platform/backend/generate_views.py, autogpt_platform/backend/pyproject.toml
New CLI utility to discover .sql files and create/replace analytics views (includes SETUP_SQL for schema/role/grants), with poetry scripts analytics-setup and analytics-views added.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer/CI
    participant FS as File system (analytics/queries/*.sql)
    participant CLI as generate_views.py
    participant DB as Postgres (analytics schema)

    Dev->>FS: add/modify SQL view files
    Dev->>CLI: run `analytics-views` (or call script)
    CLI->>FS: discover .sql files (load_views)
    CLI->>DB: connect(db_url)
    CLI->>DB: execute SETUP_SQL (create schema/role/grants)
    CLI->>DB: for each view: CREATE OR REPLACE VIEW ... (run_sql)
    DB-->>CLI: success / error per statement
    CLI->>DB: grant refresh to analytics_readonly
    CLI->>Dev: print summary / exit
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Suggested labels

Possible security concern, Review effort 4/5

Suggested reviewers

  • Pwuts
  • Bentlybro

Poem

🐇 I hopped through SQL fields, nibbling lines neat,

Views sprouted like carrots—metrics to eat.
Cohorts and costs in tidy parade,
I masked all the secrets and warmly stayed.
🥕🔍

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing a read-only SQL views layer with an analytics schema.
Description check ✅ Passed The description thoroughly explains the changes, security model, implementation details, and includes clear instructions for both local and production environments.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/analytics-views
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Comment thread autogpt_platform/analytics/queries/user_block_spending.sql Outdated
@majdyz majdyz changed the title feat(analytics): add documented SQL views with generation script feat(analytics): safe read-only SQL views layer for analytics access Mar 11, 2026
@majdyz majdyz changed the title feat(analytics): safe read-only SQL views layer for analytics access feat(analytics): read-only SQL views layer with analytics schema Mar 11, 2026
Comment thread autogpt_platform/analytics/queries/users_activities.sql Outdated
@github-actions github-actions Bot added the platform/backend AutoGPT Platform - Back end label Mar 11, 2026
@github-actions

github-actions Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 0 conflict(s), 0 medium risk, 11 low risk (out of 11 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

@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: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/analytics/generate_views.py`:
- Around line 75-80: The SQL that creates the role analytics_readonly should not
provision a LOGIN with a hardcoded password; update the CREATE ROLE statement in
the block that checks for 'analytics_readonly' so it creates the role with
NOLOGIN (remove LOGIN and PASSWORD 'CHANGE_ME') and leave enabling LOGIN and
setting a real password to the later setup step that performs credential
provisioning.
- Around line 109-116: The DB URI builder currently interpolates raw
user/password into the connection string (variables host, port, user, password,
dbname) which breaks for reserved characters; percent-encode credentials before
composing the URI—use urllib.parse.quote_plus (or urllib.parse.quote) to encode
user and password (and optionally dbname) and then return
f"postgresql://{encoded_user}:{encoded_password}@{host}:{port}/{encoded_dbname}";
after making the change run `poetry run format`.

In `@autogpt_platform/analytics/queries/graph_execution.sql`:
- Around line 96-105: The current subquery filters LibraryAgent rows with WHERE
... = TRUE before applying DISTINCT ON, causing older true flags to persist; fix
it by selecting the latest row per ("userId","agentGraphId") first (keep
DISTINCT ON ("userId","agentGraphId") with ORDER BY
"userId","agentGraphId","agentGraphVersion" DESC) and remove the WHERE that
pre-filters by sensitive_action_safe_mode so that possibly_ai is derived only
from the most recent row's
("settings"::jsonb->>'sensitive_action_safe_mode')::boolean; update the la
subquery (alias la, table "LibraryAgent", field agentGraphVersion and the
possibly_ai projection) accordingly.
- Around line 84-91: The current groupedErrorMessage expression only strips URLs
and digit-bearing tokens but still leaks emails, API keys, UUIDs and long
alphanumeric identifiers from ge."stats"::jsonb->>'error'; update the expression
to apply additional REGEXP_REPLACE passes (after the URL and digit replacements)
to: redact email addresses (e.g. \S+@\S+\.\S+), redact UUIDs (standard
8-4-4-4-12 hex pattern), redact long hexadecimal or alphanumeric tokens (e.g.
contiguous [A-Fa-f0-9]{16,} or [A-Za-z0-9_-]{12,}), and collapse repeated
redaction markers and trim length to a safe maximum; keep the final alias
groupedErrorMessage and ensure order is URL -> email -> UUID -> long hex/alnum
-> collapse/trim.

In `@autogpt_platform/analytics/queries/retention_agent.sql`:
- Line 79: The agent_names CTE currently uses MAX(g."name") which picks the
lexicographically largest historical name; instead select the name from the
deterministic latest graph version for each agent by replacing the aggregation
with a lookup of the row with the newest version (or timestamp) for that agent.
Concretely, change the agent_names CTE (referencing AgentGraph, agent_id,
agent_name) to select g."name" from the AgentGraph row with the maximum
g."version" (or max g."updated_at") per g."id"—for example via a subquery that
finds MAX(version) per id or via DISTINCT ON (g."id") ORDER BY g."version"
DESC—so agent_name always reflects the latest graph version.

In `@autogpt_platform/analytics/queries/retention_login_daily.sql`:
- Around line 39-49: The query never applies the documented 90-day cohort
window: update the params CTE to reflect 90 days (set max_days to 90) and filter
the first_login cohorts to only include cohort_day_start within that window by
joining or WHERE cohort_day_start >= (CURRENT_DATE - (params.max_days || '
days')::interval) (or equivalent using params.max_days) so the first_login CTE
only contains cohorts from the last 90 days; reference params and first_login
when making the changes.

In `@autogpt_platform/analytics/queries/user_block_spending.sql`:
- Around line 64-67: The JSONB extractions for ne."stats" used with direct
casting should use ->> (text) before casting to int; update the expressions that
produce llm_call_count, llm_retry_count, llm_input_token_count, and
llm_output_token_count to use (ne."stats"->>'llm_call_count')::int,
(ne."stats"->>'llm_retry_count')::int, (ne."stats"->>'input_token_count')::int,
and (ne."stats"->>'output_token_count')::int respectively so PostgreSQL casts
from text not JSONB.

In `@autogpt_platform/analytics/queries/user_onboarding_funnel.sql`:
- Around line 45-60: The CASE mapping for step_txt -> step_order (used in the
user_onboarding_funnel query building step_counts/funnel) misses 8
OnboardingStep enum values causing NULL step_order; either extend the CASE in
the SELECT that defines step_order to include the missing enum members
(VISIT_COPILOT, RE_RUN_AGENT, SCHEDULE_AGENT, RUN_AGENTS, RUN_3_DAYS,
TRIGGER_WEBHOOK, RUN_14_DAYS, RUN_AGENTS_100) with appropriate numeric ordering,
or add an ELSE clause (e.g., ELSE 0 or ELSE -1) to assign a default step_order
for unmapped completedSteps so they are not silently excluded from
step_counts/funnel; update any downstream logic that depends on ordering
(step_order) to handle the chosen default.

In `@autogpt_platform/analytics/queries/users_activities.sql`:
- Around line 89-101: In the user_node_runs CTE the query uses COUNT(*) which
counts the user row even when no node exists and also counts execution rows
without node records; change all counts to count the node identifier instead
(e.g., replace COUNT(*) and the filtered COUNT(*) FILTER (...) with
COUNT(n."id") and COUNT(n."id") FILTER (WHERE n."executionStatus" = '...')) so
only actual AgentNodeExecution rows are tallied (refer to user_node_runs,
n."id", and the node_execution_* columns).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4cb8f4b2-8425-4fcb-91b5-5d593f695910

📥 Commits

Reviewing files that changed from the base of the PR and between 0876d22 and 856f0d9.

📒 Files selected for processing (16)
  • autogpt_platform/analytics/.gitignore
  • autogpt_platform/analytics/generate_views.py
  • autogpt_platform/analytics/queries/auth_activities.sql
  • autogpt_platform/analytics/queries/graph_execution.sql
  • autogpt_platform/analytics/queries/node_block_execution.sql
  • autogpt_platform/analytics/queries/retention_agent.sql
  • autogpt_platform/analytics/queries/retention_execution_daily.sql
  • autogpt_platform/analytics/queries/retention_execution_weekly.sql
  • autogpt_platform/analytics/queries/retention_login_daily.sql
  • autogpt_platform/analytics/queries/retention_login_onboarded_weekly.sql
  • autogpt_platform/analytics/queries/retention_login_weekly.sql
  • autogpt_platform/analytics/queries/user_block_spending.sql
  • autogpt_platform/analytics/queries/user_onboarding.sql
  • autogpt_platform/analytics/queries/user_onboarding_funnel.sql
  • autogpt_platform/analytics/queries/user_onboarding_integration.sql
  • autogpt_platform/analytics/queries/users_activities.sql
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/analytics/generate_views.py
🧠 Learnings (2)
📚 Learning: 2026-02-26T21:29:27.619Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/CLAUDE.md:0-0
Timestamp: 2026-02-26T21:29:27.619Z
Learning: Applies to autogpt_platform/**/.env* : Platform-level configuration should use `.env.default` (Supabase/shared defaults, tracked in git) and `.env` for user overrides (gitignored)

Applied to files:

  • autogpt_platform/analytics/.gitignore
📚 Learning: 2026-02-04T16:50:20.508Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:20.508Z
Learning: Applies to autogpt_platform/backend/**/schema.prisma : Define key database models (User, AgentGraph, AgentGraphExecution, AgentNode, StoreListing) in `schema.prisma`

Applied to files:

  • autogpt_platform/analytics/queries/graph_execution.sql
🪛 Ruff (0.15.5)
autogpt_platform/analytics/generate_views.py

[warning] 174-174: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (6)
autogpt_platform/analytics/.gitignore (1)

1-2: LGTM!

Ignoring generated SQL artifacts (views.sql, setup.sql) is appropriate since these are produced by generate_views.py and should not be committed.

autogpt_platform/analytics/queries/user_onboarding_integration.sql (1)

28-41: LGTM!

The query correctly uses double-quoted identifiers (platform."UserOnboarding", u."userId", etc.) matching the Prisma-generated table naming. The logic to explode the integrations array, filter nulls/empty strings, and aggregate distinct users per integration is sound.

autogpt_platform/analytics/queries/auth_activities.sql (1)

34-40: LGTM!

The query correctly extracts authentication event data from Supabase's internal audit log. The JSON field extraction using ->> and the 90-day rolling window are implemented properly.

autogpt_platform/analytics/queries/user_onboarding.sql (1)

36-45: LGTM!

The view correctly exposes onboarding record fields without a date filter, which is appropriate since onboarding occurs once per user. The mixed quoting style (quoted for camelCase, unquoted for lowercase) is valid PostgreSQL.

autogpt_platform/analytics/queries/retention_login_onboarded_weekly.sql (1)

41-50: LGTM!

The onboarders CTE correctly identifies users who executed an agent within the 365-day onboarding window after their first login. The EXISTS subquery efficiently checks for qualifying AgentGraphExecution records.

autogpt_platform/analytics/queries/node_block_execution.sql (1)

64-101: LGTM!

The query correctly:

  • Uses ->> for JSONB field extraction with appropriate type casts
  • Joins through AgentNode to AgentBlock for block metadata
  • Applies aggressive error scrubbing via regex to anonymize IDs/URLs for safe grouping
  • Conditionally joins error output only for failed executions

Comment thread autogpt_platform/analytics/generate_views.py Outdated
Comment thread autogpt_platform/analytics/generate_views.py Outdated
Comment thread autogpt_platform/analytics/queries/graph_execution.sql
Comment thread autogpt_platform/analytics/queries/graph_execution.sql
Comment thread autogpt_platform/analytics/queries/retention_agent.sql Outdated
Comment thread autogpt_platform/analytics/queries/retention_login_daily.sql Outdated
Comment thread autogpt_platform/analytics/queries/user_block_spending.sql Outdated
Comment thread autogpt_platform/analytics/queries/user_onboarding_funnel.sql Outdated
Comment thread autogpt_platform/analytics/queries/users_activities.sql Outdated

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/generate_views.py`:
- Around line 97-104: The connection URL construction currently interpolates
DB_USER/DB_PASS raw into the URI, which breaks when they contain reserved
characters; import urllib.parse.quote and apply quote(...) to user and password
(e.g., encoded_user = quote(user, safe='') and encoded_password =
quote(password, safe='')) before building the f"postgresql://{...}" string while
keeping the existing empty-password check and variables (host, port, user,
password, dbname) the same.
- Around line 67-71: The SQL block creates a role analytics_readonly with a
hardcoded password 'CHANGE_ME'; change the creation to avoid enabling a public
default password by either creating the role as NOLOGIN (CREATE ROLE
analytics_readonly NOLOGIN) and only enabling LOGIN with a provided password
later, or require an explicit password parameter during setup instead of
'CHANGE_ME' (remove the hardcoded PASSWORD 'CHANGE_ME' from the CREATE ROLE
statement and add a step to set a secure password via ALTER ROLE
analytics_readonly WITH LOGIN PASSWORD '<secure_password>' at setup time).
- Around line 141-145: The current build_view_sql function returns views with
"security_invoker = false", which grants definer privileges and allows
analytics_readonly to access sensitive tables; update build_view_sql to use
"security_invoker = true" in the generated SQL so views run with the caller's
privileges, and then ensure the deployment/migration that creates these views
also issues explicit "GRANT SELECT" only on the specific auth/platform tables
required (or, as an alternative remediation, modify the query_body before
building the view to remove sensitive columns like actor_id, actor_via_sso, and
credential provider). Locate the build_view_sql function and change the WITH
clause to "security_invoker = true" in the returned string, and coordinate
adding targeted GRANT SELECT statements (or scrubbing) for the affected views
(e.g., analytics.auth_activities, analytics.user_block_spending) so
analytics_readonly only receives the minimal allowed access.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 139d048b-6875-45c7-871f-c9403cf368ee

📥 Commits

Reviewing files that changed from the base of the PR and between 856f0d9 and 8aad333.

📒 Files selected for processing (2)
  • autogpt_platform/backend/generate_views.py
  • autogpt_platform/backend/pyproject.toml
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/generate_views.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/generate_views.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/generate_views.py
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:20.508Z
Learning: Applies to autogpt_platform/backend/backend/**/*.py : Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/generate_views.py
  • autogpt_platform/backend/pyproject.toml
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-02-04T16:50:20.508Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:20.508Z
Learning: Applies to autogpt_platform/backend/**/*.{py,txt} : Use `poetry run` prefix for all Python commands, including testing, linting, formatting, and migrations

Applied to files:

  • autogpt_platform/backend/pyproject.toml
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/schema.prisma : Run database migrations with 'poetry run prisma migrate dev' and 'poetry run prisma generate' after schema changes in backend

Applied to files:

  • autogpt_platform/backend/pyproject.toml
📚 Learning: 2026-02-04T16:50:20.508Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:20.508Z
Learning: Applies to autogpt_platform/backend/**/*_test.py : Always review snapshot changes with `git diff` before committing when updating snapshots with `poetry run pytest --snapshot-update`

Applied to files:

  • autogpt_platform/backend/pyproject.toml
📚 Learning: 2026-01-23T19:58:10.520Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11796
File: autogpt_platform/backend/backend/blocks/video/loop.py:80-87
Timestamp: 2026-01-23T19:58:10.520Z
Learning: Ensure MoviePy is constrained to version ^2.1.2 (2.x) in pyproject.toml files where MoviePy is declared, so the backend video processing relies on a compatible API. This should cover all relevant pyproject.toml files (e.g., autogpt_platform/backend/pyproject.toml) to maintain consistency.

Applied to files:

  • autogpt_platform/backend/pyproject.toml
🔇 Additional comments (1)
autogpt_platform/backend/pyproject.toml (1)

123-124: Nice CLI surfacing.

These entry points match the documented analytics workflow and make the setup / refresh steps discoverable from Poetry.

Comment thread autogpt_platform/backend/generate_views.py
Comment thread autogpt_platform/backend/generate_views.py Outdated
Comment thread autogpt_platform/backend/generate_views.py
The original CTEs drove all joins from user_logins, causing a
O(users × executions × node_executions) fan-out that made the view
too heavy for Supabase to serve. Rewrote each CTE to aggregate its
own source table directly by userId, then LEFT JOIN the aggregates
in the final SELECT.

@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: 2

🧹 Nitpick comments (1)
autogpt_platform/analytics/queries/users_activities.sql (1)

117-122: Consider consistent NULL handling across all count columns.

The node_execution_* columns use COALESCE(..., 0) (lines 123-128), but agent_count, unique_agent_runs, and agent_runs do not. This inconsistency may confuse analytics users — a user with no agents will have agent_count = NULL while a user with no node executions will have node_execution_count = 0.

For consistency, consider applying COALESCE to the agent-related counts as well, or document the intentional distinction.

   ua.last_agent_save_time,
-  ua.agent_count,
+  COALESCE(ua.agent_count, 0)        AS agent_count,
   gr.first_agent_run_time,
   gr.last_agent_run_time,
-  gr.unique_agent_runs,
-  gr.agent_runs,
+  COALESCE(gr.unique_agent_runs, 0)  AS unique_agent_runs,
+  COALESCE(gr.agent_runs, 0)         AS agent_runs,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/analytics/queries/users_activities.sql` around lines 117 -
122, The agent-related count columns (agent_count, unique_agent_runs, agent_runs
in the ua and gr SELECTs) are currently nullable while node_execution_* columns
use COALESCE(..., 0); update those columns to use COALESCE(column, 0) so they
return 0 instead of NULL for users with no agents (e.g., replace ua.agent_count
with COALESCE(ua.agent_count, 0) and gr.unique_agent_runs/gr.agent_runs with
COALESCE(..., 0)), ensuring consistent NULL handling across counts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/analytics/queries/users_activities.sql`:
- Around line 101-106: The per-status breakdown in the query is missing counts
for the INCOMPLETE and REVIEW statuses, causing node_execution_count to not
equal the sum of node_execution_* columns; update the SELECT to add COUNT(*)
FILTER (WHERE n."executionStatus" = 'INCOMPLETE') AS node_execution_incomplete
and COUNT(*) FILTER (WHERE n."executionStatus" = 'REVIEW') AS
node_execution_review (or similarly named columns) so the AgentExecutionStatus
enum values are represented alongside the existing
node_execution_failed/completed/terminated/queued/running columns.
- Around line 74-83: In the user_agents CTE that aggregates
platform."AgentGraph", change the timestamp used for last_agent_save_time from
MAX("createdAt") to MAX("updatedAt") so the alias last_agent_save_time reflects
the graph's last modification time; update the SELECT expression in the
user_agents CTE (referencing "AgentGraph", "createdAt", "updatedAt", and the
alias last_agent_save_time) accordingly.

---

Nitpick comments:
In `@autogpt_platform/analytics/queries/users_activities.sql`:
- Around line 117-122: The agent-related count columns (agent_count,
unique_agent_runs, agent_runs in the ua and gr SELECTs) are currently nullable
while node_execution_* columns use COALESCE(..., 0); update those columns to use
COALESCE(column, 0) so they return 0 instead of NULL for users with no agents
(e.g., replace ua.agent_count with COALESCE(ua.agent_count, 0) and
gr.unique_agent_runs/gr.agent_runs with COALESCE(..., 0)), ensuring consistent
NULL handling across counts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60a01786-1253-4aff-b6a0-1721a0564e6b

📥 Commits

Reviewing files that changed from the base of the PR and between 8aad333 and 6e9d4c4.

📒 Files selected for processing (1)
  • autogpt_platform/analytics/queries/users_activities.sql
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (python)
🔇 Additional comments (2)
autogpt_platform/analytics/queries/users_activities.sql (2)

1-60: Well-documented view header.

The documentation is comprehensive with clear descriptions of source tables, output columns, performance considerations, and example queries. This will be very helpful for analytics users.


62-73: LGTM!

The user_logins CTE correctly aggregates session data per user, using GREATEST to capture the most recent activity regardless of whether it was a new login or a token refresh.

Comment thread autogpt_platform/analytics/queries/users_activities.sql
Comment thread autogpt_platform/analytics/queries/users_activities.sql Outdated
- user_block_spending: use ->> instead of -> for JSONB field extraction
  before casting to int (avoids runtime cast errors)
- generate_views: create analytics_readonly as NOLOGIN to avoid a
  usable role with a known default password
- generate_views: percent-encode DB credentials in the URI builder so
  passwords with reserved chars (@, :, /) connect correctly
- graph_execution: remove WHERE filter on sensitive_action_safe_mode
  before DISTINCT ON so the latest LibraryAgent version always wins
  (fixes possibly_ai being sticky once any version had the flag set)
- retention_agent: use DISTINCT ON ordered by version DESC instead of
  MAX(name) so renamed agents resolve to their latest name
- retention_login_daily: add 90-day cohort_start filter to first_login
  CTE so the view matches its documented window
- user_onboarding_funnel: map the 8 missing OnboardingStep enum values
  (VISIT_COPILOT, RE_RUN_AGENT, SCHEDULE_AGENT, RUN_AGENTS, RUN_3_DAYS,
  TRIGGER_WEBHOOK, RUN_14_DAYS, RUN_AGENTS_100) to step_order 15-22
- users_activities: use updatedAt instead of createdAt for
  last_agent_save_time; add node_execution_incomplete and
  node_execution_review status columns

@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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/analytics/queries/user_onboarding_funnel.sql`:
- Around line 6-8: The pct_from_prev calculation is using LAG(users_completed)
over the sparse result from raw, which skips steps with zero completions; fix
this by creating a fixed steps table or CTE enumerating steps 1..22 (e.g.,
steps(step_num)) and LEFT JOINing the aggregated counts from raw onto that full
step list so missing steps yield users_completed = 0, then compute
LAG(users_completed) and pct_from_prev over the full step sequence; apply the
same change to the other similar block referenced (lines with pct_from_prev
around 73-84) so both funnel outputs keep one row per onboarding step.
- Around line 15-16: The view's inline documentation still describes a 14-step
funnel while the CASE that computes step_order in the user_onboarding_funnel
view now assigns values up to 22; update the comment blocks above the view (the
header that mentions "step_order INT" and the step-list comment sections) to say
"22=last" and enumerate the current 22 step names to match the CASE logic
(ensure the descriptive step names and numeric mapping match the CASE in the
step_order computation so readers aren’t misled).

In `@autogpt_platform/backend/generate_views.py`:
- Around line 153-165: load_views currently silently ignores unknown names in
the only parameter and returns exit code 0 when nothing matches; update
load_views to validate the provided only list against available SQL filenames
(use files and f.stem/name) and if any requested name is missing or if the
filtered result would be empty, print a clear error to stderr and exit non‑zero
(sys.exit(1)) so the process fails fast; ensure the same validation logic is
applied to the analogous function that handles materialized views (the code
around build_view_sql and any counterpart at the later block) so typos don't
silently drop views.
- Around line 21-25: The docstring and setup instructions reference altering the
analytics_readonly role but only set a password; update the ALTER ROLE
statements shown in the examples to include WITH LOGIN so the role can accept
connections (e.g., change "ALTER ROLE analytics_readonly WITH PASSWORD
'your-password';" to include "WITH LOGIN" in the same statement). Edit the
occurrences tied to SETUP_SQL / analytics_readonly examples so both the one near
the top (Step 3) and the second instance (lines ~67-72) are updated to "ALTER
ROLE analytics_readonly WITH LOGIN PASSWORD 'your-password';" ensuring the
examples match the NOLOGIN creation in SETUP_SQL.
- Around line 103-109: The current logic returns None when password is empty;
instead construct and return a valid libpq URI without the password component.
Update the code that builds the connection string (using the same symbols user,
password, host, port, dbname and quote) so that if password is falsy you return
"postgresql://" + f"{quote(user, safe='')}@{host}:{port}/{quote(dbname,
safe='')}", and otherwise keep the existing behavior that includes the quoted
password; ensure user and dbname remain percent-encoded via quote in both
branches.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 64fbdfa8-8a9b-4fe3-a709-c9f0f6f27359

📥 Commits

Reviewing files that changed from the base of the PR and between 6e9d4c4 and 7d39234.

📒 Files selected for processing (7)
  • autogpt_platform/analytics/queries/graph_execution.sql
  • autogpt_platform/analytics/queries/retention_agent.sql
  • autogpt_platform/analytics/queries/retention_login_daily.sql
  • autogpt_platform/analytics/queries/user_block_spending.sql
  • autogpt_platform/analytics/queries/user_onboarding_funnel.sql
  • autogpt_platform/analytics/queries/users_activities.sql
  • autogpt_platform/backend/generate_views.py
✅ Files skipped from review due to trivial changes (1)
  • autogpt_platform/analytics/queries/user_block_spending.sql
🚧 Files skipped from review as they are similar to previous changes (3)
  • autogpt_platform/analytics/queries/retention_login_daily.sql
  • autogpt_platform/analytics/queries/graph_execution.sql
  • autogpt_platform/analytics/queries/users_activities.sql
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/generate_views.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/generate_views.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/generate_views.py
🧠 Learnings (10)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12367
File: autogpt_platform/backend/generate_views.py:146-150
Timestamp: 2026-03-11T16:50:37.459Z
Learning: In Significant-Gravitas/AutoGPT PR `#12367` (`autogpt_platform/backend/generate_views.py`), the `security_invoker = false` setting on analytics views is intentional. `analytics_readonly` is granted access only to the `analytics` schema. The views execute as their owner (postgres) to read `auth.*` and `platform.*` tables, but expose only curated, scrubbed columns via their SELECT lists. This is the correct PostgreSQL "view as security boundary" pattern — equivalent to a stored-procedure security definer. Do not flag this as a privilege escalation; switching to `security_invoker = true` would require granting `analytics_readonly` direct SELECT on `auth.sessions`, `auth.audit_log_entries`, `platform.AgentGraphExecution`, etc., which is a much wider blast radius than the current design.
📚 Learning: 2026-03-11T16:50:37.459Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12367
File: autogpt_platform/backend/generate_views.py:146-150
Timestamp: 2026-03-11T16:50:37.459Z
Learning: In Significant-Gravitas/AutoGPT PR `#12367` (`autogpt_platform/backend/generate_views.py`), the `security_invoker = false` setting on analytics views is intentional. `analytics_readonly` is granted access only to the `analytics` schema. The views execute as their owner (postgres) to read `auth.*` and `platform.*` tables, but expose only curated, scrubbed columns via their SELECT lists. This is the correct PostgreSQL "view as security boundary" pattern — equivalent to a stored-procedure security definer. Do not flag this as a privilege escalation; switching to `security_invoker = true` would require granting `analytics_readonly` direct SELECT on `auth.sessions`, `auth.audit_log_entries`, `platform.AgentGraphExecution`, etc., which is a much wider blast radius than the current design.

Applied to files:

  • autogpt_platform/analytics/queries/user_onboarding_funnel.sql
  • autogpt_platform/analytics/queries/retention_agent.sql
  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-02-04T16:50:20.508Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:20.508Z
Learning: Applies to autogpt_platform/backend/**/schema.prisma : Define key database models (User, AgentGraph, AgentGraphExecution, AgentNode, StoreListing) in `schema.prisma`

Applied to files:

  • autogpt_platform/analytics/queries/retention_agent.sql
📚 Learning: 2026-03-05T00:13:52.412Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/backend/schema.prisma:284-284
Timestamp: 2026-03-05T00:13:52.412Z
Learning: In `autogpt_platform/backend/schema.prisma`, the `AgentGraph` ↔ `StoreListing` relation uses the pattern: `AgentGraph` declares `StoreListing? relation(fields: [id], references: [agentGraphId], onDelete: NoAction)` and `StoreListing` declares `AgentGraph AgentGraph[]` with `agentGraphId String unique`. This is intentional and valid because `AgentGraph` has a composite PK `@id([id, version])` (multiple rows per graph id, one per version), while `StoreListing.agentGraphId` is `unique` (one listing per graph id). The `fields: [id], references: [agentGraphId]` on the `AgentGraph` side joins `AgentGraph.id` against `StoreListing.agentGraphId`. Do NOT flag this as a cardinality mismatch or malformed relation — `prisma validate` passes cleanly.

Applied to files:

  • autogpt_platform/analytics/queries/retention_agent.sql
📚 Learning: 2026-03-10T08:39:13.707Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.

Applied to files:

  • autogpt_platform/analytics/queries/retention_agent.sql
  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-03-10T11:22:14.861Z
Learnt from: Swiftyos
Repo: Significant-Gravitas/AutoGPT PR: 12347
File: autogpt_platform/backend/backend/data/invited_user.py:193-193
Timestamp: 2026-03-10T11:22:14.861Z
Learning: In Significant-Gravitas/AutoGPT, the admin data-layer functions in `autogpt_platform/backend/backend/data/invited_user.py` (`list_invited_users`, `create_invited_user`, `revoke_invited_user`, `retry_invited_user_tally`, `bulk_create_invited_users_from_file`) intentionally omit an acting-user/admin ID parameter. Authorization for these functions is enforced entirely at the FastAPI router layer via `Security(requires_admin_user)` in `user_admin_routes.py`. Do not flag the absence of a user_id/actor_id parameter in these functions as a missing data-access guardrail violation.

Applied to files:

  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/backend/data/**/*.py : All data access in backend requires user ID checks; verify this for any 'data/*.py' changes

Applied to files:

  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/**/data/*.py : For changes touching `data/*.py`, validate user ID checks or explain why not needed

Applied to files:

  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/generate_views.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/generate_views.py
🔇 Additional comments (1)
autogpt_platform/analytics/queries/retention_agent.sql (1)

79-80: Deterministic latest-version name lookup looks good.

DISTINCT ON (g."id") ... ORDER BY g."version" DESC avoids the old MAX(name) drift and keeps labels aligned with the newest graph version.

Comment thread autogpt_platform/analytics/queries/user_onboarding_funnel.sql Outdated
Comment thread autogpt_platform/analytics/queries/user_onboarding_funnel.sql Outdated
Comment thread autogpt_platform/backend/generate_views.py Outdated
Comment thread autogpt_platform/backend/generate_views.py
Comment thread autogpt_platform/backend/generate_views.py
…LECT

CREATE OR REPLACE VIEW requires existing columns to stay in position.
Moving node_execution_incomplete and node_execution_review after
is_active_after_7d so the replacement doesn't shift existing columns.

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/analytics/queries/users_activities.sql`:
- Around line 25-42: The documented output column header is missing the final
two columns returned by the SELECT; update the header block so the declared
column list and order match the view by appending entries for
node_execution_incomplete and node_execution_review (with their appropriate
types, e.g., BIGINT or INT depending on the SELECT) and brief descriptions;
ensure the new lines appear after node_execution_running in the same comment
block so the header order exactly mirrors the SELECT output.
- Around line 77-80: The query currently uses COUNT("id") AS agent_count which
counts versioned rows rather than distinct agents; change the aggregation to
COUNT(DISTINCT "id") AS agent_count in the SELECT so each graph id is counted
once (keep the "userId"::text AS user_id and MAX("updatedAt") AS
last_agent_save_time unchanged and reference the AgentGraph table/column names
exactly as used).
- Around line 119-124: Normalize the count columns by converting NULLs to 0:
update the SELECT expressions that return ua.agent_count, gr.unique_agent_runs,
and gr.agent_runs to wrap them with COALESCE (or equivalent) so they return 0
instead of NULL for users with logins but no agents or executions, matching the
existing normalization applied to the node counters; ensure you modify the
SELECT projection where ua.agent_count, gr.first_agent_run_time,
gr.last_agent_run_time, gr.unique_agent_runs, gr.agent_runs are listed so
downstream aggregations no longer need special-casing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2c035eeb-ee0a-45bf-a966-1b895a5b9936

📥 Commits

Reviewing files that changed from the base of the PR and between 7d39234 and f585d97.

📒 Files selected for processing (1)
  • autogpt_platform/analytics/queries/users_activities.sql
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (python)
  • GitHub Check: Check PR Status
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12367
File: autogpt_platform/backend/generate_views.py:146-150
Timestamp: 2026-03-11T16:50:37.459Z
Learning: In Significant-Gravitas/AutoGPT PR `#12367` (`autogpt_platform/backend/generate_views.py`), the `security_invoker = false` setting on analytics views is intentional. `analytics_readonly` is granted access only to the `analytics` schema. The views execute as their owner (postgres) to read `auth.*` and `platform.*` tables, but expose only curated, scrubbed columns via their SELECT lists. This is the correct PostgreSQL "view as security boundary" pattern — equivalent to a stored-procedure security definer. Do not flag this as a privilege escalation; switching to `security_invoker = true` would require granting `analytics_readonly` direct SELECT on `auth.sessions`, `auth.audit_log_entries`, `platform.AgentGraphExecution`, etc., which is a much wider blast radius than the current design.
📚 Learning: 2026-03-11T16:50:37.459Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12367
File: autogpt_platform/backend/generate_views.py:146-150
Timestamp: 2026-03-11T16:50:37.459Z
Learning: In Significant-Gravitas/AutoGPT PR `#12367` (`autogpt_platform/backend/generate_views.py`), the `security_invoker = false` setting on analytics views is intentional. `analytics_readonly` is granted access only to the `analytics` schema. The views execute as their owner (postgres) to read `auth.*` and `platform.*` tables, but expose only curated, scrubbed columns via their SELECT lists. This is the correct PostgreSQL "view as security boundary" pattern — equivalent to a stored-procedure security definer. Do not flag this as a privilege escalation; switching to `security_invoker = true` would require granting `analytics_readonly` direct SELECT on `auth.sessions`, `auth.audit_log_entries`, `platform.AgentGraphExecution`, etc., which is a much wider blast radius than the current design.

Applied to files:

  • autogpt_platform/analytics/queries/users_activities.sql
📚 Learning: 2026-03-04T23:58:18.476Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.

Applied to files:

  • autogpt_platform/analytics/queries/users_activities.sql
📚 Learning: 2026-02-04T16:50:20.508Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:20.508Z
Learning: Applies to autogpt_platform/backend/**/schema.prisma : Define key database models (User, AgentGraph, AgentGraphExecution, AgentNode, StoreListing) in `schema.prisma`

Applied to files:

  • autogpt_platform/analytics/queries/users_activities.sql
📚 Learning: 2026-03-05T00:13:52.412Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/backend/schema.prisma:284-284
Timestamp: 2026-03-05T00:13:52.412Z
Learning: In `autogpt_platform/backend/schema.prisma`, the `AgentGraph` ↔ `StoreListing` relation uses the pattern: `AgentGraph` declares `StoreListing? relation(fields: [id], references: [agentGraphId], onDelete: NoAction)` and `StoreListing` declares `AgentGraph AgentGraph[]` with `agentGraphId String unique`. This is intentional and valid because `AgentGraph` has a composite PK `@id([id, version])` (multiple rows per graph id, one per version), while `StoreListing.agentGraphId` is `unique` (one listing per graph id). The `fields: [id], references: [agentGraphId]` on the `AgentGraph` side joins `AgentGraph.id` against `StoreListing.agentGraphId`. Do NOT flag this as a cardinality mismatch or malformed relation — `prisma validate` passes cleanly.

Applied to files:

  • autogpt_platform/analytics/queries/users_activities.sql
📚 Learning: 2026-03-10T08:39:13.707Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.

Applied to files:

  • autogpt_platform/analytics/queries/users_activities.sql

Comment thread autogpt_platform/analytics/queries/users_activities.sql
Comment thread autogpt_platform/analytics/queries/users_activities.sql
Comment thread autogpt_platform/analytics/queries/users_activities.sql Outdated
@majdyz
majdyz enabled auto-merge March 11, 2026 17:42
- user_onboarding_funnel: build complete 22-step grid with VALUES CTE
  so zero-completion steps are always present, fixing LAG comparisons
  against wrong predecessors; update docs to reflect all 22 steps
- users_activities: use COUNT(DISTINCT "id") for agent_count to avoid
  counting multiple version rows per graph; add COALESCE(..., 0) for
  agent_count, unique_agent_runs, agent_runs; update docs column list
  to include node_execution_incomplete and node_execution_review
- generate_views: update Step 3 comment to clarify NOLOGIN role needs
  WITH LOGIN PASSWORD not just WITH PASSWORD; add fail-fast validation
  for unknown --only view names with helpful error message
The completedSteps column is a platform."OnboardingStep" enum array.
UNNEST produces enum values that can't be compared directly to text
from the VALUES clause. Adding ::text cast fixes the type mismatch.
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Mar 13, 2026
@majdyz
majdyz added this pull request to the merge queue Mar 13, 2026
Merged via the queue into dev with commit a8259ca Mar 13, 2026
20 checks passed
@majdyz
majdyz deleted the feat/analytics-views branch March 13, 2026 12:24
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Mar 13, 2026
@Bentlybro
Bentlybro restored the feat/analytics-views branch March 16, 2026 13:46
Bentlybro pushed a commit that referenced this pull request Apr 4, 2026
)

### Changes 🏗️

Adds `autogpt_platform/analytics/` — 14 SQL view definitions that expose
production data safely through a locked-down `analytics` schema.

**Security model:**
- Views use `security_invoker = false` (PostgreSQL 15+), so they execute
as their owner (`postgres`), not the caller
- `analytics_readonly` role only has access to `analytics.*` — cannot
touch `platform` or `auth` tables directly

**Files:**
- `backend/generate_views.py` — does everything; auto-reads credentials
from `backend/.env`
- `analytics/queries/*.sql` — 14 documented view definitions (auth, user
activity, executions, onboarding funnel, cohort retention)

---

### Running locally (dev)

```bash
cd autogpt_platform/backend

# First time only — creates analytics schema, role, grants
poetry run analytics-setup

# Create / refresh views (auto-reads backend/.env)
poetry run analytics-views
```

### Running in production (Supabase)

```bash
cd autogpt_platform/backend

# Step 1 — first time only (run in Supabase SQL Editor as postgres superuser)
poetry run analytics-setup --dry-run
# Paste the output into Supabase SQL Editor and run

# Step 2 — apply views (use direct connection host, not pooler)
poetry run analytics-views --db-url "postgresql://postgres:PASSWORD@db.<ref>.supabase.co:5432/postgres"

# Step 3 — set password for analytics_readonly so external tools can connect
# Run in Supabase SQL Editor:
# ALTER ROLE analytics_readonly WITH PASSWORD 'your-password';
```

---

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Setup + views applied cleanly on local Postgres 15
- [x] `analytics_readonly` can `SELECT` from all 14 `analytics.*` views
- [x] `analytics_readonly` gets `permission denied` on `platform.*` and
`auth.*` directly

---------

Co-authored-by: Otto (AGPT) <otto@agpt.co>
Bentlybro pushed a commit to Bentlybro/AutoGPT that referenced this pull request Apr 4, 2026
…nificant-Gravitas#12367)

### Changes 🏗️

Adds `autogpt_platform/analytics/` — 14 SQL view definitions that expose
production data safely through a locked-down `analytics` schema.

**Security model:**
- Views use `security_invoker = false` (PostgreSQL 15+), so they execute
as their owner (`postgres`), not the caller
- `analytics_readonly` role only has access to `analytics.*` — cannot
touch `platform` or `auth` tables directly

**Files:**
- `backend/generate_views.py` — does everything; auto-reads credentials
from `backend/.env`
- `analytics/queries/*.sql` — 14 documented view definitions (auth, user
activity, executions, onboarding funnel, cohort retention)

---

### Running locally (dev)

```bash
cd autogpt_platform/backend

# First time only — creates analytics schema, role, grants
poetry run analytics-setup

# Create / refresh views (auto-reads backend/.env)
poetry run analytics-views
```

### Running in production (Supabase)

```bash
cd autogpt_platform/backend

# Step 1 — first time only (run in Supabase SQL Editor as postgres superuser)
poetry run analytics-setup --dry-run
# Paste the output into Supabase SQL Editor and run

# Step 2 — apply views (use direct connection host, not pooler)
poetry run analytics-views --db-url "postgresql://postgres:PASSWORD@db.<ref>.supabase.co:5432/postgres"

# Step 3 — set password for analytics_readonly so external tools can connect
# Run in Supabase SQL Editor:
# ALTER ROLE analytics_readonly WITH PASSWORD 'your-password';
```

---

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Setup + views applied cleanly on local Postgres 15
- [x] `analytics_readonly` can `SELECT` from all 14 `analytics.*` views
- [x] `analytics_readonly` gets `permission denied` on `platform.*` and
`auth.*` directly

---------

Co-authored-by: Otto (AGPT) <otto@agpt.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end size/xl

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

3 participants