.agentconf/skills/identity-notifications/SKILL.md
Create new identity notification types by adding enum values, type definitions, notifier methods, push handlers, and API integration. Use when adding new notification types, creating notifications, or extending the notification system.
npx skillsauth add 6529-collections/6529seize-backend identity-notificationsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill guides you through the complete process of creating a new identity notification type in the 6529 SEIZE Backend.
Identity notifications alert users about activities relevant to them (mentions, votes, replies, etc.). Creating a new notification type involves eight steps:
IdentityNotificationCauseUserNotifierBefore implementing, gather these details using AskUserQuestion:
DROP_PINNED, WAVE_ARCHIVED, PROFILE_UPDATEDidentity_id - WHO receives the notification (required)additional_identity_id - WHO triggered it (nullable)related_drop_id + related_drop_part_no - Primary drop reference (nullable)related_drop_2_id + related_drop_2_part_no - Secondary drop reference (nullable)wave_id - Associated wave (nullable)visibility_group_id - Group visibility restriction (nullable)additional_data - JSON column for custom fieldssrc/drops/create-or-update-drop.use-case.tssrc/drops/vote-for-drop.use-case.tssrc/reactions/reactions.service.tssrc/api-serverless/src/waves/wave.api.service.tssrc/api-serverless/src/identity-subscriptions/identity-subscriptions.api.service.tsFile: src/entities/IIdentityNotification.ts
Add the new cause to the enum:
export enum IdentityNotificationCause {
// ... existing values
YOUR_NEW_CAUSE = 'YOUR_NEW_CAUSE'
}
File: src/notifications/user-notification.types.ts
Add three things:
2a. Data interface (what gets stored):
export interface YourNewNotificationData {
actor_id: string; // Who triggered it
recipient_id: string; // Who receives it
drop_id?: string; // Related drop if applicable
wave_id?: string; // Related wave if applicable
// Add custom fields as needed
}
2b. Full notification interface:
export interface YourNewNotification extends UserNotificationBase {
cause: IdentityNotificationCause.YOUR_NEW_CAUSE;
data: YourNewNotificationData;
}
2c. Add to union type (at the end of file):
export type UserNotification =
| IdentitySubscriptionNotification
// ... existing types
| YourNewNotification; // Add here
File: src/notifications/user-notification.mapper.ts
3a. Add case to switch in entityToNotification():
case IdentityNotificationCause.YOUR_NEW_CAUSE:
return this.mapYourNewNotification(entity);
3b. Add mapping method:
private mapYourNewNotification(
entity: IdentityNotificationDeserialized
): YourNewNotification {
return {
id: entity.id,
created_at: entity.created_at,
read_at: entity.read_at,
cause: IdentityNotificationCause.YOUR_NEW_CAUSE,
data: {
actor_id: entity.additional_identity_id!,
recipient_id: entity.identity_id,
drop_id: entity.related_drop_id ?? undefined,
wave_id: entity.wave_id ?? undefined
// Map additional_data fields if needed:
// custom_field: entity.additional_data.custom_field
}
};
}
Important: Use ! only when the field is guaranteed to be populated for this notification type.
File: src/notifications/user.notifier.ts
4a. Import your new type at the top:
import {
// ... existing imports
YourNewNotificationData
} from './user-notification.types';
4b. Add notifier method:
public async notifyOfYourNewThing(
data: YourNewNotificationData,
visibility_group_id: string | null,
connection?: ConnectionWrapper<any>
) {
// Skip self-notifications
if (data.actor_id === data.recipient_id) {
return;
}
await this.identityNotificationsDb.insertNotification(
{
identity_id: data.recipient_id,
additional_identity_id: data.actor_id,
related_drop_id: data.drop_id ?? null,
related_drop_part_no: null,
related_drop_2_id: null,
related_drop_2_part_no: null,
wave_id: data.wave_id ?? null,
cause: IdentityNotificationCause.YOUR_NEW_CAUSE,
additional_data: {
// Custom JSON fields here
},
visibility_group_id
},
connection
);
}
In the identified service/use-case, call your notifier:
await userNotifier.notifyOfYourNewThing(
{
actor_id: actorIdentityId,
recipient_id: recipientIdentityId,
drop_id: dropId,
wave_id: waveId
},
visibilityGroupId,
connection // Pass if inside transaction
);
Important considerations:
connection if inside a database transactionFile: src/pushNotificationsHandler/identityPushNotifications.ts
6a. Add case to generateNotificationData():
case IdentityNotificationCause.YOUR_NEW_CAUSE:
return handleYourNewThing(notification, additionalEntity);
6b. Add handler function:
async function handleYourNewThing(
notification: IdentityNotificationEntity,
additionalEntity: ApiIdentity
) {
// Fetch related data if needed
const dropSerialNo = await getDropSerialNo(notification.related_drop_id);
const title = `${additionalEntity.handle} did something`;
const body = 'View details';
const imageUrl = additionalEntity.pfp;
const data = {
redirect: 'waves', // or 'profile'
wave_id: notification.wave_id,
drop_id: dropSerialNo
};
return { title, body, data, imageUrl };
}
Available helpers:
getDropSerialNo(dropId) - Get drop's serial number for deep linkinggetDropPart(notification, handle?) - Get drop content for body textgetDrop(notification) - Get full drop entitygetWaveEntityOrThrow(notificationId, waveId) - Get wave entityFile: src/api-serverless/src/notifications/notifications.api.service.ts
7a. Add case to getAllRelatedIds() (~line 154):
case IdentityNotificationCause.YOUR_NEW_CAUSE: {
const data = notification.data;
profileIds.push(data.actor_id);
if (data.drop_id) {
dropIds.push(data.drop_id);
}
break;
}
7b. Add case to mapToApiNotification() (~line 225):
case IdentityNotificationCause.YOUR_NEW_CAUSE: {
const data = notification.data;
return {
id: notification.id,
created_at: notification.created_at,
read_at: notification.read_at,
cause: enums.resolveOrThrow(ApiNotificationCause, notificationCause),
related_identity: profiles[data.actor_id],
related_drops: data.drop_id ? [drops[data.drop_id]] : [],
additional_context: {
wave_id: data.wave_id
// Add custom context for frontend
}
};
}
File: src/api-serverless/openapi.yaml
Find ApiNotificationCause enum (~line 7668) and add your value:
ApiNotificationCause:
type: string
enum:
- IDENTITY_SUBSCRIBED
- IDENTITY_MENTIONED
# ... existing values
- YOUR_NEW_CAUSE # Add here
Then regenerate types:
cd src/api-serverless && npm run restructure-openapi && npm run generate
After implementation, verify:
IdentityNotificationCause in src/entities/IIdentityNotification.tssrc/notifications/user-notification.types.tsUserNotification unionsrc/notifications/user-notification.mapper.tssrc/notifications/user.notifier.tssrc/pushNotificationsHandler/identityPushNotifications.tsgetAllRelatedIds() case added in notifications API servicemapToApiNotification() case added in notifications API servicecd src/api-serverless && npm run restructure-openapi && npm run generate)npm test)npm run build)identity_id (recipient), additional_identity_id (actor)related_drop_id, wave_idrelated_drop_id (new drop), related_drop_2_id (original drop)wave_id| File | Purpose |
|------|---------|
| src/entities/IIdentityNotification.ts | Entity + enum definition |
| src/notifications/user-notification.types.ts | TypeScript interfaces |
| src/notifications/user-notification.mapper.ts | DB → domain mapping |
| src/notifications/user.notifier.ts | Notification creation methods |
| src/pushNotificationsHandler/identityPushNotifications.ts | Push rendering |
| src/api-serverless/src/notifications/notifications.api.service.ts | API response mapping |
| src/api-serverless/openapi.yaml | API schema (line ~7668) |
For notifications to work:
USER_NOTIFIER_ACTIVATED=true - Enable notification creationPUSH_NOTIFICATIONS_ACTIVATED=true - Enable push sendingconnection if inside a database transactionassertUnreachable pattern ensures you don't miss switch casesAskUserQuestion to gather the four required pieces of information:
npm run build to verify TypeScript compilationnpm test to verify tests passUSER_NOTIFIER_ACTIVATED=truedata-ai
Implement database-related changes in this repository, including schema changes via entities, repository/query patterns, transactions, and data migrations. Use when working on migrations, DB schema updates, or app logic that touches the database.
development
Create new community metrics by adding enum values, recording functions, wiring, backfill migrations, and API integration. Use when adding new community metrics, creating metrics, or tracking community activity.
development
Build or modify API endpoints in this repository by driving changes from openapi.yaml, regenerating API models, implementing routes manually, and wiring validation/auth/timing correctly. Use when creating new APIs, changing existing APIs, or updating API request/response models.
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.