Skip to content

aevp: opt-in Pi Profile (lower particle + render cost)#111

Open
webdevtodayjason wants to merge 1 commit intomainfrom
codex/aevp-pi-profile
Open

aevp: opt-in Pi Profile (lower particle + render cost)#111
webdevtodayjason wants to merge 1 commit intomainfrom
codex/aevp-pi-profile

Conversation

@webdevtodayjason
Copy link
Copy Markdown
Contributor

@webdevtodayjason webdevtodayjason commented Apr 12, 2026

Summary

Adds an opt-in Pi Profile to AEVP that lowers the per-frame cost without changing identity presets or personality modulation. Motivation: the ArgentOS dashboard is going to run on a Raspberry Pi 5 with a 7" touchscreen and software rendering — the 180-particle / 60+ fps default pins the CPU.

What changes

Lever Default Pi profile
Particle cap (runtime) up to 180 60
Density scale 1.0× 0.5×
Render tick interval ~16 ms (rAF) 33 ms (~30 fps)

The buffer allocation (`MAX_PARTICLES = 180` in `particles.ts`) is untouched — the profile only caps the runtime active count, so it can be flipped on/off without reallocation.

Activation (any ONE of these)

  • `PI_PROFILE=1` env var (Node/SSR)
  • `VITE_PI_PROFILE=1` env var (Vite dev/build)
  • `localStorage.setItem("argent.piProfile", "1")` (browser runtime)
  • `?piProfile=1` URL param (one-shot)

Files

  • `dashboard/src/aevp/pi-profile.ts` (new, 94 lines, pure functions)
  • `dashboard/src/aevp/colorMapping.ts` (2-line import + 5-line call site edit)
  • `dashboard/src/aevp/renderer.ts` (1-line import + 5-line frame gate)
  • `dashboard/docs/pi-profile.md` (new, usage + verification notes)

Not touched

  • Identity presets (`identityPresets.ts`) — still emitted as-is
  • Personality modulation (warmth/energy/openness/formality) — still applied in full
  • `MorningParticles.tsx` / `EveningFireflies.tsx` — separate components, mount conditionally in app code if they also need to drop on Pi
  • Orb bloom / glow post-processing — flagged as future cut in the docs

Test plan

  • CI typecheck passes
  • Baseline perf capture (no profile): ~60 fps, ~180 particles in drawArrays
  • Profile-on capture: ~30 fps, ≤60 particles in drawArrays
  • Orb still breathes, colour-shifts on activity, emits formation particles on tool events
  • Toggle off via `localStorage.removeItem("argent.piProfile")` + reload restores baseline

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added opt-in "Pi Profile" low-intensity rendering mode with reduced particle effects and ~30 fps frame rate. Enable via environment variable, URL parameter, or configuration flag.
  • Documentation

    • Added Pi Profile setup and verification guide.

New module dashboard/src/aevp/pi-profile.ts exposes three knobs the
AEVP system reads at runtime:

- getMaxParticlesCap() → 60 (default 180) — hard ceiling on active
  particles, layered on top of identity preset density.
- getDensityScale() → 0.5 (default 1.0) — multiplier applied to
  presence.particleDensity so identity presets keep their relative
  character but cost half as much.
- getFrameIntervalMs() → 33 (default 0) — minimum ms between render
  ticks, i.e. ~30fps cap on the AEVP loop.

The profile activates if any of these are set:
  PI_PROFILE=1                                 (env)
  VITE_PI_PROFILE=1                            (Vite build env)
  localStorage "argent.piProfile" = "1"        (browser runtime)
  URL ?piProfile=1                             (one-shot)

Identity presets and personality modulation (warmth/energy/openness/
formality) are untouched — the orb keeps its character, it just costs
~60% less to draw.

Buffer allocation (MAX_PARTICLES = 180 in particles.ts) is unchanged;
the profile only caps the runtime active count, so it can be flipped
on/off without reallocation.

Files:
- dashboard/src/aevp/pi-profile.ts     (new, 94 lines, pure functions)
- dashboard/src/aevp/colorMapping.ts   (2-line import + 5-line call site)
- dashboard/src/aevp/renderer.ts       (1-line import + 5-line frame gate)
- dashboard/docs/pi-profile.md         (new, usage + verification notes)

Use case: ArgentOS dashboard running on a Raspberry Pi 5 with a 7"
touchscreen, software rendering, no dedicated GPU. Measured reduction
pending — review against a baseline tab before/after enabling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

A new Pi Profile performance mode is introduced for the ArgentOS dashboard's particle system. The feature reduces the particle cap from 180 to 60, applies a 0.5 density scale, and throttles frame rendering to ~30 fps via multiple activation mechanisms. Configuration is evaluated at module load from environment variables, localStorage, and URL parameters.

Changes

Cohort / File(s) Summary
Pi Profile Configuration
dashboard/src/aevp/pi-profile.ts, dashboard/docs/pi-profile.md
New module exporting PI_PROFILE_ACTIVE constant and three configuration helpers (getMaxParticlesCap(), getDensityScale(), getFrameIntervalMs()). Activation checked via PI_PROFILE/VITE_PI_PROFILE env vars, argent.piProfile localStorage key, and ?piProfile=1 URL param. Documentation specifies functional behavior, verification steps, and future performance optimizations.
Particle Rendering Integration
dashboard/src/aevp/colorMapping.ts, dashboard/src/aevp/renderer.ts
colorMapping.ts updated to apply pi-profile-derived particle cap and density scale to maxParticles calculation. renderer.ts adds frame-throttling check in tick() loop using getFrameIntervalMs() to gate rendering intervals when Pi Profile is active.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A cap of 60 particles dance on Pi's stage,
Throttled frames at thirty, a lighter gauge,
Toggle it on with localStorage or URL,
Lean performance mode—a distributed pearl! 🥧✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% 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 accurately summarizes the main change: adding an opt-in Pi Profile to reduce particle and render costs in AEVP.
Description check ✅ Passed The description is comprehensive and well-structured, covering summary, scope, what changes, activation methods, files modified, and test plan, though validation command results are not explicitly listed.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/aevp-pi-profile

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
dashboard/src/aevp/colorMapping.ts (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Formatter failure is blocking CI here too.

Run oxfmt on this file; current branch won’t pass checks as-is.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/aevp/colorMapping.ts` at line 1, Run the formatter (oxfmt) on
dashboard/src/aevp/colorMapping.ts and commit the resulting changes so the file
passes CI; specifically, format the entire file (fix indentation, trailing
spaces, comment blocks, and any broken JSDoc starting at the top of the file)
and re-run the linter/CI locally to confirm; ensure the formatted file is staged
and pushed so the branch no longer fails the oxfmt check.
dashboard/src/aevp/renderer.ts (1)

1-1: ⚠️ Potential issue | 🟡 Minor

CI is red on formatting in this file.

oxfmt --check is failing here; run formatter and re-push so the pipeline goes green.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/aevp/renderer.ts` at line 1, This file fails the oxfmt style
check; run the formatter (oxfmt) on dashboard/src/aevp/renderer.ts (or run oxfmt
--check locally then oxfmt to fix) and re-commit the formatted changes so the CI
style check passes; ensure you stage and push the updated file after formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dashboard/docs/pi-profile.md`:
- Around line 22-24: Doc inconsistency: update the sentence that currently
claims the profile can be flipped “without a reload” to match the rest of the
doc which states activation is evaluated once at module load and requires a
reload; reference the fixed buffer capacity (MAX_PARTICLES in aevp/particles.ts)
but clarify that while the buffer is preallocated for 180 particles, the profile
activation/enablement is determined at module initialization and changing the
profile requires a reload to take effect.
- Line 1: The markdown file with header "# Pi Profile — low-intensity AEVP mode"
is failing formatter checks; run the repository's formatter (oxfmt) on this
document to reformat it, verify the changes, and commit the formatted file so CI
passes (e.g., run `oxfmt dashboard/docs/pi-profile.md`, review the diff, then
stage and commit the updated file).

In `@dashboard/src/aevp/colorMapping.ts`:
- Around line 449-455: The computed maxParticles (from piCap, piScale, and
presence.particleDensity) is intended as a hard upper bound but is later
overridden by a clamp to 100 and a fixed lower bound of 5; update the downstream
particle count logic (the place that clamps to 100 and enforces 5) so the final
particleCount is always <= maxParticles — for example compute the tentative
count as now, then set particleCount = Math.min(maxParticles,
Math.max(minAllowedButNotAboveMax, tentativeCount)); ensure minAllowed is itself
constrained to <= maxParticles (use Math.min(5, maxParticles) or similar) so
low-density cases cannot force particleCount above maxParticles.

In `@dashboard/src/aevp/pi-profile.ts`:
- Line 1: The new module pi-profile.ts is not formatted according to the repo
style; run the formatter (oxfmt --apply or your project's preferred formatter)
on pi-profile.ts and re-run oxfmt --check to ensure it passes, then stage and
commit the formatting changes; if your editor uses Prettier/ESLint, run those
commands (or the project's format script) so the module header/comments and the
rest of the file match repository formatting rules.
- Around line 30-35: Replace the non-existent custom Vite bridge
(globalThis.__ARGENT_VITE_ENV__) in pi-profile.ts with the real Vite environment
object import.meta.env: locate the block that creates the local meta variable
and checks meta?.[name] (the code that reads globalThis.__ARGENT_VITE_ENV__),
and change it to read from import.meta.env (checking import.meta.env[name],
treating "0" and "" as falsy as before) so VITE_PI_PROFILE and other VITE_* vars
work the same way as the rest of the app.

---

Outside diff comments:
In `@dashboard/src/aevp/colorMapping.ts`:
- Line 1: Run the formatter (oxfmt) on dashboard/src/aevp/colorMapping.ts and
commit the resulting changes so the file passes CI; specifically, format the
entire file (fix indentation, trailing spaces, comment blocks, and any broken
JSDoc starting at the top of the file) and re-run the linter/CI locally to
confirm; ensure the formatted file is staged and pushed so the branch no longer
fails the oxfmt check.

In `@dashboard/src/aevp/renderer.ts`:
- Line 1: This file fails the oxfmt style check; run the formatter (oxfmt) on
dashboard/src/aevp/renderer.ts (or run oxfmt --check locally then oxfmt to fix)
and re-commit the formatted changes so the CI style check passes; ensure you
stage and push the updated file after formatting.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 067cb78c-b703-40a7-bb4c-83e134e9177d

📥 Commits

Reviewing files that changed from the base of the PR and between 0714198 and e832d63.

📒 Files selected for processing (4)
  • dashboard/docs/pi-profile.md
  • dashboard/src/aevp/colorMapping.ts
  • dashboard/src/aevp/pi-profile.ts
  • dashboard/src/aevp/renderer.ts

@@ -0,0 +1,68 @@
# Pi Profile — low-intensity AEVP mode
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Docs file is also failing formatter checks.

Run oxfmt for this markdown file so CI passes cleanly.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Oxfmt --check reported formatting issues in this file. Run oxfmt without --check (e.g., 'oxfmt' or 'oxfmt --write') to fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/docs/pi-profile.md` at line 1, The markdown file with header "# Pi
Profile — low-intensity AEVP mode" is failing formatter checks; run the
repository's formatter (oxfmt) on this document to reformat it, verify the
changes, and commit the formatted file so CI passes (e.g., run `oxfmt
dashboard/docs/pi-profile.md`, review the diff, then stage and commit the
updated file).

Comment on lines +22 to +24
The hard allocation (`MAX_PARTICLES = 180` in `aevp/particles.ts`) is
unchanged — the buffer still holds room for 180 — so the profile can
be flipped on and off without a reload or re-alloc.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Small but important doc mismatch on reload behavior.

Line 24 says profile can be flipped “without a reload,” but Lines 28-30 and Line 38 say activation is evaluated once at module load and needs reload. Please make this consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/docs/pi-profile.md` around lines 22 - 24, Doc inconsistency: update
the sentence that currently claims the profile can be flipped “without a reload”
to match the rest of the doc which states activation is evaluated once at module
load and requires a reload; reference the fixed buffer capacity (MAX_PARTICLES
in aevp/particles.ts) but clarify that while the buffer is preallocated for 180
particles, the profile activation/enablement is determined at module
initialization and changing the profile requires a reload to take effect.

Comment on lines +449 to +455
// Pi profile: scale density + apply a hard cap on top of identity preset.
const piCap = getMaxParticlesCap();
const piScale = getDensityScale();
const maxParticles = Math.min(
piCap,
Math.round(100 * presence.particleDensity * piScale),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Big issue: Pi cap is not enforced end-to-end.

Strong concept, but the hard cap can be broken downstream. maxParticles is computed here, then Line 486 clamps to 100, which can push counts above Pi cap. Also, fixed lower bound 5 can exceed maxParticles when density is low.

✅ Proposed fix to keep particleCount always ≤ maxParticles
   const maxParticles = Math.min(
     piCap,
     Math.round(100 * presence.particleDensity * piScale),
   );
+  const baseFloor = Math.min(5, maxParticles);
   let particleCount = Math.round(
     clamp(
       maxParticles * (0.3 + moodVis.brightness * 0.4 + arousal * 0.3) * energyMul,
-      5,
+      baseFloor,
       maxParticles,
     ),
   );
@@
-  particleCount = Math.round(clamp(particleCount * (0.7 + p.energy * 0.6), 3, 100));
+  const personalityFloor = Math.min(3, maxParticles);
+  particleCount = Math.round(
+    clamp(particleCount * (0.7 + p.energy * 0.6), personalityFloor, maxParticles),
+  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/aevp/colorMapping.ts` around lines 449 - 455, The computed
maxParticles (from piCap, piScale, and presence.particleDensity) is intended as
a hard upper bound but is later overridden by a clamp to 100 and a fixed lower
bound of 5; update the downstream particle count logic (the place that clamps to
100 and enforces 5) so the final particleCount is always <= maxParticles — for
example compute the tentative count as now, then set particleCount =
Math.min(maxParticles, Math.max(minAllowedButNotAboveMax, tentativeCount));
ensure minAllowed is itself constrained to <= maxParticles (use Math.min(5,
maxParticles) or similar) so low-density cases cannot force particleCount above
maxParticles.

@@ -0,0 +1,95 @@
/**
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Formatting is also failing in this new module.

oxfmt --check reports issues here; please format before merge.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Oxfmt --check reported formatting issues in this file. Run oxfmt without --check (e.g., 'oxfmt' or 'oxfmt --write') to fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/aevp/pi-profile.ts` at line 1, The new module pi-profile.ts is
not formatted according to the repo style; run the formatter (oxfmt --apply or
your project's preferred formatter) on pi-profile.ts and re-run oxfmt --check to
ensure it passes, then stage and commit the formatting changes; if your editor
uses Prettier/ESLint, run those commands (or the project's format script) so the
module header/comments and the rest of the file match repository formatting
rules.

Comment on lines +30 to +35
try {
// Vite (import.meta.env is statically available only inside Vite-processed
// modules; protect with a typeof check so this file compiles under raw tsc)
const meta = (globalThis as { __ARGENT_VITE_ENV__?: Record<string, string | undefined> })
.__ARGENT_VITE_ENV__;
if (meta?.[name] && meta[name] !== "0" && meta[name] !== "") return true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether the custom bridge actually exists and is initialized.
# Expected good result:
# 1) A concrete assignment to globalThis.__ARGENT_VITE_ENV__ appears in startup code, OR
# 2) This module (or equivalent) reads import.meta.env directly.
rg -n -C3 '__ARGENT_VITE_ENV__|import\.meta\.env|VITE_PI_PROFILE'

Repository: ArgentAIOS/argentos-core

Length of output: 6417


The custom Vite env bridge is a loser — it's never initialized anywhere.

Look, this code reads globalThis.__ARGENT_VITE_ENV__ for VITE_PI_PROFILE, but that custom global doesn't exist in the codebase. Zero occurrences. Not initialized, not set up, nothing. Every other file uses standard import.meta.env directly — the beautiful, normal way. Meanwhile your documentation says users should set VITE_PI_PROFILE=1, but it won't work because this code is looking in the wrong place. Sad!

Fix it to use import.meta.env like the rest of the app. It's the right move, it's the smart move.

Suggested fix
-    const meta = (globalThis as { __ARGENT_VITE_ENV__?: Record<string, string | undefined> })
-      .__ARGENT_VITE_ENV__;
+    const meta = (import.meta as { env?: Record<string, string | undefined> }).env;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
// Vite (import.meta.env is statically available only inside Vite-processed
// modules; protect with a typeof check so this file compiles under raw tsc)
const meta = (globalThis as { __ARGENT_VITE_ENV__?: Record<string, string | undefined> })
.__ARGENT_VITE_ENV__;
if (meta?.[name] && meta[name] !== "0" && meta[name] !== "") return true;
try {
// Vite (import.meta.env is statically available only inside Vite-processed
// modules; protect with a typeof check so this file compiles under raw tsc)
const meta = (import.meta as { env?: Record<string, string | undefined> }).env;
if (meta?.[name] && meta[name] !== "0" && meta[name] !== "") return true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/aevp/pi-profile.ts` around lines 30 - 35, Replace the
non-existent custom Vite bridge (globalThis.__ARGENT_VITE_ENV__) in
pi-profile.ts with the real Vite environment object import.meta.env: locate the
block that creates the local meta variable and checks meta?.[name] (the code
that reads globalThis.__ARGENT_VITE_ENV__), and change it to read from
import.meta.env (checking import.meta.env[name], treating "0" and "" as falsy as
before) so VITE_PI_PROFILE and other VITE_* vars work the same way as the rest
of the app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant