Skip to content

PR2: Role-aware digest insight assembler — Same Brain, different lens per role - #104

Merged
venkateshsakamuri-lab merged 5 commits into
mainfrom
cursor/digest-insight-assembler-cc18
Sep 9, 2026
Merged

PR2: Role-aware digest insight assembler — Same Brain, different lens per role#104
venkateshsakamuri-lab merged 5 commits into
mainfrom
cursor/digest-insight-assembler-cc18

Conversation

@venkateshsakamuri-lab

@venkateshsakamuri-lab venkateshsakamuri-lab commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Role-aware insight assembler that ranks Brain insights for specific users/personas. Same Brain, different lens per role — not another generic Slack dump.

Persona → Real Brain Signal Mapping

The assembler surfaces different signals per persona for the same window:

Persona Primary Signals Examples
DBA LOCK_CONCURRENCY, QUERY_PERFORMANCE, CONFIG_TUNING idle-in-txn, lock waits, blocking chains, autovacuum/bloat, join_collapse_limit cliffs, plan regressions
APP_ENG SCHEMA_CHANGES, QUERY_PERFORMANCE, LOCK_CONCURRENCY DDL blocking risk (ACCESS EXCLUSIVE during deploys), migration impact, ORM query patterns
DATA_ENG DOCUMENTATION_GAPS, SCHEMA_CHANGES, GROWTH_ANOMALIES semantic drift, ETL/pipeline breaks, schema/comment gaps, load patterns
EXEC COST_CAPACITY, SYSTEM_ALERTS cost/risk summaries (3 bullets max), top regression headline, one decision ask

Implementation

New Models

  • DigestInsight — ranked insight with category, severity, freshness, actionability, signature key for deduplication
  • InsightCategory — enum with persona-aware multipliers (0.4–2.0x)
  • DigestAssemblyResult — assembled digest with executive summary for EXEC persona

DigestInsightAssemblerService

Mines insights from existing Brain stores:

  • BrainV2AlertRepository — workload changes, config drift, plan regressions
  • BrainScoreRepository — overall health scores
  • BrainLearningProgressRepository — tuning readiness
  • IndexRecommendationRepository — missing/unused index candidates
  • SchemaChangeRepository — breaking changes, DDL events
  • GrowthAnomalyRepository — size spikes, bloat
  • PlaybookAlertRepository — system alerts
  • SlowQueryHistoryRepository — slow query analysis
  • LockContentionRepository — lock waits, blocking chains, ACCESS EXCLUSIVE

Ranking Formula

score = categoryWeight × severityMultiplier × freshnessMultiplier
      × personaRelevance × roleMultiplier × actionabilityBonus

Suppression Logic

  • Acknowledged insights filtered out
  • Duplicate signatures from last digest suppressed (unless severity ≥80 or changed in last 6h)
  • EXEC persona capped at 5 insights with executive summary

Testing

Comprehensive unit tests covering:

  • Different personas get different ranked results
  • DBA ranks lock contention higher than DATA_ENG
  • APP_ENG ranks schema changes higher for DDL blocking risk
  • EXEC gets limited insights with executive summary
  • Duplicate suppression across digests
  • Acknowledged insight filtering
  • Signal mining from all Brain stores

Notes

  • EMAIL enum exists but is not implemented — this PR does not pretend email delivery works
  • WHATSAPP not added — Hermes spike is later
  • Clear service API for PR3 to consume (assembleDigest(username, connectionId, role, personaTag, sinceLast))

Stacked on

PR #103 (cursor/per-user-digest-prefs-3784) — per-user digest preferences

Open in Web Open in Cursor 

@cursor cursor Bot changed the title feat: role-aware insight assembler from Brain stores (PR2) PR2: Role-aware digest insight assembler — Same Brain, different lens per role Sep 8, 2026
@venkateshsakamuri-lab

Copy link
Copy Markdown
Contributor Author

Stack review pointer

Reviewed together with #103 and #105 (no #106). Assembler unit suite (26) green; live tick on demo_shop produced persona-specific titles/formatting (EXEC summary vs engineer readouts).

Full write-up + blocking findings on tip #105: #105 (comment)

This PR (#104): assembler ranking/persona lens is directionally good; sparse Brain data on demo_shop mostly shared the same index insight — titles/format differed more than ranked content. Merge only as middle of stack after tip delivery fixes.

venkateshsakamuri-lab added a commit that referenced this pull request Sep 9, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

**Same Brain, different lens per role.**

This PR implements per-recipient Slack delivery for digests, consuming
the `DigestAssemblyResult` API from PR2. Two users with different
personas on the same connection now get different Slack digests for the
same time window.

### Honesty-gap fix (per-user cron)

UI schedule presets and `UserDigestPreference.cronExpression` (+
timezone) were stored but ignored: `SlackDailyDigestTaskConfig` only ran
the global `slack.daily-digest.cron` and then delivered to every
preference. Product Evangelist blocked marketing “your own schedule”
until this was fixed.

The scheduler now ticks every minute (`slack.daily-digest.tick-cron`,
default `0 * * * * *`) via `processDigestTick()`:

- **No enabled prefs** → legacy channel broadcast only when the global
cron is due (UTC)
- **Prefs exist** → each enabled `SLACK_DM` preference is evaluated with
Spring `CronExpression` in that user’s timezone; already-logged fire
windows are skipped (`SlackDigestLog` idempotency)
- Blank preference cron still falls back to `slack.daily-digest.cron`
- Manual/admin trigger still uses `sendDailyDigestHybrid()` (force all
recipients)

## Changes

### Backend

- **`SlackDailyDigestService`**: `sendPersonalizedDigests()` /
`sendDailyDigestHybrid()` / **`processDigestTick()`** — per-recipient
DMs, EXEC formatting, personalized logs, and per-user cron on the minute
tick
- **`DigestCronMatcher`**: Spring `CronExpression` due-window helper
(timezone-aware)
- **`DigestPreferenceSeedService`**: seed from singleton; persona
inference; skips inactive users
- **`SlackDailyDigestTaskConfig`**: minute tick → `processDigestTick()`
- **`DigestPreferenceController`**: seed/status; refuses EMAIL

### Frontend

- Digest preferences API + panel (persona, schedule presets, seed) +
DigestSection bell icon

### Documentation

- `docs/DIGEST_PREFERENCES.md` updated for tick vs global cron and
accurate “your own schedule”

## Acceptance Criteria

- [x] Two users, different roles/personas, same connection → different
Slack digests same window
- [x] Cron/config not singleton-only (**per-user cron honored on tick**)
- [x] Tests for per-recipient delivery + seed/legacy + due-cron /
timezone / idempotency
- [x] UI prefs for role/schedule/persona
- [x] Docs updated — “your own schedule” is accurate
- [x] No fake EMAIL / WhatsApp delivery advertised

## Out of Scope (Follow-ups)

1. Idle-in-transaction insight when Brain has data
2. `join_collapse_limit` cliffs under CONFIG_TUNING
3. DOCUMENTATION_GAPS miner
4. COST_CAPACITY dedicated cost miner for EXEC
5. Hermes/WhatsApp = PR4

## Dependencies

- PR1 #103: per-user digest prefs
- PR2 #104: role-aware insight assembler

## Testing

```bash
cd backend
./mvnw test -Dtest=PerRecipientDigestDeliveryTest,PerRecipientDigestCronSchedulingTest,DigestCronMatcherTest,DigestPreferenceSeedServiceTest,SlackDailyDigestServiceTest
```

Key scenarios: different crons → only due user; timezone
America/New_York 9AM = 13:00 UTC; idempotent window skip; EXEC 3
bullets; EMAIL skipped; seed idempotent; legacy when no enabled prefs.

<!-- CURSOR_AGENT_PR_BODY_END -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@venkateshsakamuri-lab
venkateshsakamuri-lab added this pull request to stack #106 September 9, 2026 06:04
@venkateshsakamuri-lab
venkateshsakamuri-lab marked this pull request as ready for review September 9, 2026 06:05
@venkateshsakamuri-lab
venkateshsakamuri-lab requested a review from a team as a code owner September 9, 2026 06:05
cursoragent and others added 4 commits September 9, 2026 12:23
PR1 for role-aware digests theme. This change migrates from singleton
SlackDigestConfig to per-user UserDigestPreference with:

Data Model:
- PersonaTag enum: DBA, APP_ENG, DATA_ENG, EXEC for content prioritization
- DigestDeliveryMethod enum: SLACK_DM, SLACK_CHANNEL, EMAIL
- UserDigestPreference entity with per-user/per-connection preferences
- Extended SlackDigestLog with recipient tracking fields

Services:
- UserDigestPreferenceService for CRUD and preference resolution
- Minimal hooks in SlackDailyDigestService (isPerUserModeEnabled, getDigestRecipients)

API:
- /digest/preferences/* for user self-service management
- /admin/slack/digest/preferences/* for admin management
- Backward compatible: singleton still works when no prefs exist

Migration:
- V120 creates user_digest_preference table
- Extends slack_digest_log with recipient/persona/delivery fields
- Adds is_global_default to slack_digest_config

Tests:
- PersonaTagTest, DigestDeliveryMethodTest for enum parsing
- UserDigestPreferenceTest for entity behavior
- UserDigestPreferenceServiceTest for service logic

Docs:
- docs/DIGEST_PREFERENCES.md with API usage examples

Full role-aware ranking/delivery is PR2 (this is data model + migration only).

Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
Implements PR2 of the thinking database feature: role-aware, personalized
digest content assembly.

New components:
- DigestInsight: ranked insight model with severity, freshness, actionability
- InsightCategory: enum mapping insight types to persona relevance weights
- DigestAssemblyResult: assembled digest with metadata and executive summary
- DigestInsightAssemblerService: mines and ranks insights from Brain stores

Signal mining from existing stores:
- BrainV2Alert: workload changes, config drift, plan regressions
- BrainScore: low health score alerts
- BrainLearningProgress: tuning milestones
- IndexRecommendation: pending index recommendations by priority
- SchemaChange: breaking/critical schema changes
- GrowthAnomaly: table growth spikes
- PlaybookAlert: system alerts
- SlowQueryHistory: critical/high slow query counts

Ranking formula:
  score = categoryWeight × severityMultiplier × freshnessMultiplier
        × personaRelevance × roleMultiplier × actionabilityBonus

Persona-aware weighting (examples):
- DBA: 2x on QUERY_PERFORMANCE, INDEX_RECOMMENDATIONS, CONFIG_TUNING
- DATA_ENG: 2x on DOCUMENTATION_GAPS, SCHEMA_CHANGES
- APP_ENG: 2x on SCHEMA_CHANGES, 1.8x on QUERY_PERFORMANCE
- EXEC: 2x on COST_CAPACITY, limited to 5 insights with summary

Suppression logic:
- Filters acknowledged insights
- Suppresses duplicates from last digest (via signature key)
- High-severity insights (>=80) bypass suppression

Tests cover:
- Different personas get differently ranked digests
- Duplicate suppression across digests
- Acknowledged insight filtering
- Severity/freshness/actionability ranking
- Signal mining from all Brain stores

Depends on PR #103 (cursor/per-user-digest-prefs-3784) for UserDigestPreference,
PersonaTag, and SlackDigestLog models.

Part of: Thinking database — continuous-learning Brain + role-aware digests

Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
PR2 enhancement per Product Evangelist guidance:

Concrete signal mapping (same Brain, different lens per role):
- DBA: idle-in-txn, lock waits, blocking chains, autovacuum/bloat
- APP_ENG: ACCESS EXCLUSIVE risk (DDL blocking), schema changes
- DATA_ENG: documentation gaps (semantic drift), ETL/load patterns
- EXEC: cost/capacity summaries (3 bullets max)

Changes:
- Add LOCK_CONCURRENCY InsightCategory for DBA operational signals
- Update persona multipliers based on concrete use cases
- Add mineLockContention() mining from LockContentionRepository
- Add tests for lock contention mining and persona-based ranking
- Inject LockContentionRepository into DigestInsightAssemblerService

GTM: 'Same Brain, different lens per role' — not another generic dump.

Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary

**Same Brain, different lens per role.**

This PR implements per-recipient Slack delivery for digests, consuming
the `DigestAssemblyResult` API from PR2. Two users with different
personas on the same connection now get different Slack digests for the
same time window.

### Honesty-gap fix (per-user cron)

UI schedule presets and `UserDigestPreference.cronExpression` (+
timezone) were stored but ignored: `SlackDailyDigestTaskConfig` only ran
the global `slack.daily-digest.cron` and then delivered to every
preference. Product Evangelist blocked marketing “your own schedule”
until this was fixed.

The scheduler now ticks every minute (`slack.daily-digest.tick-cron`,
default `0 * * * * *`) via `processDigestTick()`:

- **No enabled prefs** → legacy channel broadcast only when the global
cron is due (UTC)
- **Prefs exist** → each enabled `SLACK_DM` preference is evaluated with
Spring `CronExpression` in that user’s timezone; already-logged fire
windows are skipped (`SlackDigestLog` idempotency)
- Blank preference cron still falls back to `slack.daily-digest.cron`
- Manual/admin trigger still uses `sendDailyDigestHybrid()` (force all
recipients)

## Changes

### Backend

- **`SlackDailyDigestService`**: `sendPersonalizedDigests()` /
`sendDailyDigestHybrid()` / **`processDigestTick()`** — per-recipient
DMs, EXEC formatting, personalized logs, and per-user cron on the minute
tick
- **`DigestCronMatcher`**: Spring `CronExpression` due-window helper
(timezone-aware)
- **`DigestPreferenceSeedService`**: seed from singleton; persona
inference; skips inactive users
- **`SlackDailyDigestTaskConfig`**: minute tick → `processDigestTick()`
- **`DigestPreferenceController`**: seed/status; refuses EMAIL

### Frontend

- Digest preferences API + panel (persona, schedule presets, seed) +
DigestSection bell icon

### Documentation

- `docs/DIGEST_PREFERENCES.md` updated for tick vs global cron and
accurate “your own schedule”

## Acceptance Criteria

- [x] Two users, different roles/personas, same connection → different
Slack digests same window
- [x] Cron/config not singleton-only (**per-user cron honored on tick**)
- [x] Tests for per-recipient delivery + seed/legacy + due-cron /
timezone / idempotency
- [x] UI prefs for role/schedule/persona
- [x] Docs updated — “your own schedule” is accurate
- [x] No fake EMAIL / WhatsApp delivery advertised

## Out of Scope (Follow-ups)

1. Idle-in-transaction insight when Brain has data
2. `join_collapse_limit` cliffs under CONFIG_TUNING
3. DOCUMENTATION_GAPS miner
4. COST_CAPACITY dedicated cost miner for EXEC
5. Hermes/WhatsApp = PR4

## Dependencies

- PR1 #103: per-user digest prefs
- PR2 #104: role-aware insight assembler

## Testing

```bash
cd backend
./mvnw test -Dtest=PerRecipientDigestDeliveryTest,PerRecipientDigestCronSchedulingTest,DigestCronMatcherTest,DigestPreferenceSeedServiceTest,SlackDailyDigestServiceTest
```

Key scenarios: different crons → only due user; timezone
America/New_York 9AM = 13:00 UTC; idempotent window skip; EXEC 3
bullets; EMAIL skipped; seed idempotent; legacy when no enabled prefs.

<!-- CURSOR_AGENT_PR_BODY_END -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@venkateshsakamuri-lab
venkateshsakamuri-lab force-pushed the cursor/per-user-digest-prefs-3784 branch from 800d213 to 7b4dea1 Compare September 9, 2026 06:53
@venkateshsakamuri-lab
venkateshsakamuri-lab force-pushed the cursor/digest-insight-assembler-cc18 branch from c6585bb to 3d292d1 Compare September 9, 2026 06:53
Base automatically changed from cursor/per-user-digest-prefs-3784 to main September 9, 2026 07:03
Resolve add/add and content conflicts from PR1 (#103) squash on main
against PR1+PR2+PR3 on this branch. Prefer branch tip (assembler,
per-recipient delivery, per-user cron, seed/admin endpoints, EMAIL
guard) since #103 tree matched 7b4dea1 and PR2/PR3 supersede it.
@venkateshsakamuri-lab
venkateshsakamuri-lab removed this pull request from stack #106 September 9, 2026 07:09
@venkateshsakamuri-lab
venkateshsakamuri-lab merged commit e358131 into main Sep 9, 2026
9 checks passed
@venkateshsakamuri-lab
venkateshsakamuri-lab deleted the cursor/digest-insight-assembler-cc18 branch September 9, 2026 07:12
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.

2 participants