From 5c3f9f22ec73e0ac6a9253810944dbc093966186 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Wed, 20 May 2026 19:08:57 +0000 Subject: [PATCH] docs(constructive-sdk-events): add EventReferral max_depth documentation - Add EventReferral section to SKILL.md with config reference table - Create references/event-referral.md with multi-level chain walk docs, MLM blueprint example, attenuation design, and toggle reference - Update invite-virality.md with multi-level referral chain section - Update description to include max_depth/MLM trigger phrases - Repackage zip Documents the max_depth feature added in: constructive-io/constructive#1201 (upstream node-type-registry) constructive-io/constructive-db#1261 (generator + tests) --- .agents/skills/constructive-sdk-events.zip | Bin 15680 -> 19529 bytes .../skills/constructive-sdk-events/SKILL.md | 63 ++++++- .../references/event-referral.md | 176 ++++++++++++++++++ .../references/invite-virality.md | 15 ++ 4 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 .agents/skills/constructive-sdk-events/references/event-referral.md diff --git a/.agents/skills/constructive-sdk-events.zip b/.agents/skills/constructive-sdk-events.zip index d60a73fd79a0c243819440ae0f4525a786950b87..9edda0e9d22ef8c6d9bdf8227fd3af2314a37dff 100644 GIT binary patch delta 10031 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 delta 6251 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 diff --git a/.agents/skills/constructive-sdk-events/SKILL.md b/.agents/skills/constructive-sdk-events/SKILL.md index f1ea10e..820e16b 100644 --- a/.agents/skills/constructive-sdk-events/SKILL.md +++ b/.agents/skills/constructive-sdk-events/SKILL.md @@ -1,6 +1,6 @@ --- name: constructive-sdk-events -description: "Events, achievements, and gamification — EventTracker blueprint node for recording events on row changes, blueprint achievements[] for defining levels with requirements and credit rewards (limit_credit + meter_credit with expires_interval), invite-based achievements (has_invite_achievements), period-aware event_aggregates (lazy count reset), re-triggerable achievements (per-period re-qualification), EventReferral for attributing events to inviters, and the full virality chain. Use when asked to 'add analytics', 'track events', 'add achievements', 'gamification', 'record events', 'EventTracker', 'level requirements', 'achievement rewards', 'invite achievements', 'invite virality', 'credit grants for achievements', 'meter_credit', 'expires_interval', 'period_interval', 'recurring credits', 'referral credits', 'EventReferral', or when working with events_module in blueprints." +description: "Events, achievements, and gamification — EventTracker blueprint node for recording events on row changes, blueprint achievements[] for defining levels with requirements and credit rewards (limit_credit + meter_credit with expires_interval), invite-based achievements (has_invite_achievements), period-aware event_aggregates (lazy count reset), re-triggerable achievements (per-period re-qualification), EventReferral for attributing events to inviters (with multi-level max_depth for MLM referral chains), and the full virality chain. Use when asked to 'add analytics', 'track events', 'add achievements', 'gamification', 'record events', 'EventTracker', 'level requirements', 'achievement rewards', 'invite achievements', 'invite virality', 'credit grants for achievements', 'meter_credit', 'expires_interval', 'period_interval', 'recurring credits', 'referral credits', 'EventReferral', 'max_depth', 'multi-level referral', 'MLM', 'referral chain', or when working with events_module in blueprints." metadata: author: constructive-io version: "1.0.0" @@ -14,7 +14,7 @@ Three capabilities compose together: - **`EventTracker`** — table-level node. Attach to any table to declaratively record events when rows change. Same compound condition system as JobTrigger. - **`achievements[]`** — top-level blueprint section. Define levels with requirements (event counts) and optional rewards (`limit_credit` or `meter_credit` grants, with optional `expires_interval`). - **`has_invite_achievements`** — entity type flag. Auto-attaches EventTracker to `claimed_invites` and wires the invitee achievement virality chain. -- **`EventReferral`** — entity type node. Wires referral attribution so that when an invitee's event is recorded, the inviter also gets an attributed event. +- **`EventReferral`** — table-level node. Wires referral attribution so that when an invitee performs an action, the inviter gets an attributed event. Supports `max_depth` (1–10) for multi-level referral chains — walks up the `claimed_invites` chain N levels, crediting each ancestor. - **Period-aware counting** — event types with `period_interval` auto-reset aggregate counts each period (lazy reset). Enables re-triggerable achievements for recurring credit grants. Related skills: @@ -116,6 +116,64 @@ See [references/event-tracker.md](references/event-tracker.md) for compound cond --- +## EventReferral Blueprint Node + +Add `EventReferral` to a table's `nodes[]` to credit the actor's inviter (and optionally their inviter's inviter, etc.) when a row changes: + +```json +{ + "tables": [{ + "table_name": "user_uploads", + "nodes": [ + { "$type": "EventReferral", "data": { + "event_name": "invitee_uploaded", + "events": ["INSERT"], + "actor_field": "owner_id", + "max_depth": 5 + }} + ] + }] +} +``` + +### Configuration Reference + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `event_name` | string | **(required)** | Event type name to record for each ancestor in the invite chain | +| `events` | `("INSERT" \| "UPDATE" \| "DELETE")[]` | `["INSERT"]` | Which DML events fire the trigger | +| `actor_field` | string (column-ref) | `"owner_id"` | Column containing the invitee (actor) ID — used to look up the referrer via `claimed_invites.receiver_id` | +| `entity_field` | string (column-ref) | — | Column containing the entity ID for entity-scoped referral events. **Cannot be combined with `max_depth > 1`.** | +| `max_depth` | integer | `1` | How many levels up the invite chain to walk. `1` = direct inviter only (default, backward compatible). `2`–`10` = multi-level referral chain. Hard cap at 10. | +| `auto_register_type` | boolean | `true` | Automatically register the `event_name` in the `event_types` catalog during provisioning | +| `conditions` | object \| array | — | Compound conditions for WHEN clause (same syntax as EventTracker) | + +### Toggles & Controls + +- **Build-time:** `max_depth` is the toggle. Default `1` = today's single-hop behavior. Set `2`–`10` to opt in to multi-level. Omitting or setting to `1` produces the exact same trigger as before. +- **Runtime:** `event_types.is_active` — flip to `false` to pause referral rewards without redeploying. +- **Scope constraint:** `max_depth > 1` requires app-level scope only (`entity_field` must be omitted). The generator raises an exception if both are set. + +### How Multi-Level Works + +When `max_depth > 1`, the trigger builds a FOR loop that walks `claimed_invites`: + +``` +User action (INSERT on user_uploads) + → trigger resolves NEW.owner_id + → FOR depth IN 1..max_depth: + SELECT sender_id FROM claimed_invites WHERE receiver_id = current + EXIT WHEN no sender found + record_event(event_name, sender_id) + current := sender_id +``` + +Each ancestor in the chain receives the same event. Combined with tiered achievement thresholds, this creates natural attenuation — direct inviters accumulate events quickly (low threshold, high reward), while deeper ancestors accumulate slowly (high threshold, low reward). + +See [references/event-referral.md](references/event-referral.md) for multi-level blueprint examples, the viral loop pattern, and attenuation design. + +--- + ## Blueprint Achievements The top-level `achievements[]` section defines levels with requirements and optional rewards. Processed in **Phase 7** of `constructBlueprint()` — after tables, relations, and entity types. @@ -265,6 +323,7 @@ See [references/invite-virality.md](references/invite-virality.md) for detailed | File | Contents | |------|----------| | [references/event-tracker.md](references/event-tracker.md) | Full EventTracker parameter reference, compound conditions, toggle mode, entity-scoped examples | +| [references/event-referral.md](references/event-referral.md) | EventReferral parameter reference, multi-level `max_depth` chain walk, MLM blueprint examples, attenuation design | | [references/achievements.md](references/achievements.md) | Achievement definitions, requirements, rewards (limit_credit + meter_credit), expires_interval, period-aware aggregates, re-triggerable achievements | | [references/invite-virality.md](references/invite-virality.md) | Simple + meta invite tiers, full virality chain, cross-entity examples | | [references/triggers.md](references/triggers.md) | Internal trigger reference: tg_check_achievements, tg_achievement_reward, tg_invitee_achievement | diff --git a/.agents/skills/constructive-sdk-events/references/event-referral.md b/.agents/skills/constructive-sdk-events/references/event-referral.md new file mode 100644 index 0000000..018a620 --- /dev/null +++ b/.agents/skills/constructive-sdk-events/references/event-referral.md @@ -0,0 +1,176 @@ +# EventReferral Reference + +EventReferral is a table-level blueprint node that attributes events to the actor's inviter(s) when a row changes. It resolves the referrer via the invites module's `claimed_invites` table. + +## Single-Level Referral (Default) + +With `max_depth: 1` (or omitted), EventReferral credits only the direct inviter: + +```json +{ + "tables": [{ + "table_name": "user_profiles", + "nodes": [ + { "$type": "EventReferral", "data": { + "event_name": "invitee_completed_profile", + "events": ["UPDATE"], + "actor_field": "owner_id" + }} + ] + }] +} +``` + +When User B (invited by User A) updates their profile: +1. Trigger fires → resolves `NEW.owner_id` (User B) +2. Looks up `claimed_invites WHERE receiver_id = B` → finds User A +3. Calls `record_event('invitee_completed_profile', A)` + +## Multi-Level Referral (max_depth > 1) + +Set `max_depth` to walk up the invite chain multiple levels: + +```json +{ + "tables": [{ + "table_name": "user_uploads", + "nodes": [ + { "$type": "EventReferral", "data": { + "event_name": "invitee_uploaded", + "events": ["INSERT"], + "actor_field": "owner_id", + "max_depth": 5 + }} + ] + }] +} +``` + +When User D (chain: A invited B invited C invited D) uploads a file: +1. Depth 1: `claimed_invites WHERE receiver_id = D` → C. Records event for C. +2. Depth 2: `claimed_invites WHERE receiver_id = C` → B. Records event for B. +3. Depth 3: `claimed_invites WHERE receiver_id = B` → A. Records event for A. +4. Depth 4: `claimed_invites WHERE receiver_id = A` → NULL. Loop exits. + +All three ancestors (C, B, A) receive the same `invitee_uploaded` event. The loop stops early if the chain is shorter than `max_depth`. + +## Configuration Reference + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `event_name` | string | **(required)** | Event type name to record for each ancestor | +| `events` | `("INSERT" \| "UPDATE" \| "DELETE")[]` | `["INSERT"]` | Which DML events fire the trigger | +| `actor_field` | string (column-ref) | `"owner_id"` | Column containing the invitee (actor) ID | +| `entity_field` | string (column-ref) | — | Entity ID column for entity-scoped events. **Cannot be combined with `max_depth > 1`.** | +| `max_depth` | integer | `1` | Levels to walk up the invite chain. Range: 1–10. | +| `auto_register_type` | boolean | `true` | Auto-register `event_name` in `event_types` during provisioning | +| `conditions` | object \| array | — | Compound conditions for WHEN clause | + +## Constraints + +- **`max_depth` range:** 1–10. The generator raises an exception for values outside this range. +- **App-level scope only:** When `max_depth > 1`, `entity_field` must be omitted. The chain walk uses `claimed_invites` which is scoped by membership_type at the app level. Entity-scoped actions still credit the chain — the trigger resolves the *user* who performed the action, not the entity. +- **Backward compatible:** `max_depth: 1` (or omitted) produces the exact same single-lookup trigger as before the feature existed. + +## Toggles + +| Toggle | Type | How | +|--------|------|-----| +| Build-time on/off | `max_depth` parameter | `1` = off (single hop), `2`–`10` = on (multi-level) | +| Runtime on/off | `event_types.is_active` | Set to `false` to pause referral event recording without redeploying | + +## Multi-Level MLM Blueprint Example + +A complete blueprint showing 5-level referral rewards with tiered achievements: + +```json +{ + "entity_types": [ + { + "name": "App Members", + "prefix": "app", + "has_invites": true, + "has_levels": true, + "has_limits": true + } + ], + + "tables": [ + { + "table_name": "databases", + "fields": [ + { "name": "name", "type": "text", "is_required": true }, + { "name": "owner_id", "type": "uuid", "is_required": true } + ], + "nodes": [ + { "$type": "EventReferral", "data": { + "event_name": "invitee_created_db", + "events": ["INSERT"], + "actor_field": "owner_id", + "max_depth": 5 + }}, + { "$type": "LimitCounter", "data": { "limit_name": "databases" } } + ] + } + ], + + "achievements": [ + { + "name": "referral_bronze", + "description": "3 people in your network created a database", + "priority": 10, + "requirements": [ + { "event_name": "invitee_created_db", "count": 3 } + ], + "rewards": [ + { "reward_type": "limit_credit", "target_name": "databases", "amount": 5 } + ] + }, + { + "name": "referral_silver", + "description": "10 people in your network created a database", + "priority": 20, + "requirements": [ + { "event_name": "invitee_created_db", "count": 10 } + ], + "rewards": [ + { "reward_type": "limit_credit", "target_name": "databases", "amount": 3 } + ] + }, + { + "name": "referral_gold", + "description": "25 people in your network created a database", + "priority": 30, + "requirements": [ + { "event_name": "invitee_created_db", "count": 25 } + ], + "rewards": [ + { "reward_type": "limit_credit", "target_name": "databases", "amount": 2 } + ] + } + ] +} +``` + +## Attenuation Design + +Multi-level referrals create natural attenuation without any per-depth tracking: + +- **Direct inviters** (depth 1) see events frequently — their invitees' actions directly generate events. They hit achievement thresholds quickly. +- **2nd-degree ancestors** see events less often — only when their invitees' invitees act. +- **5th-degree ancestors** accumulate events very slowly. + +The tiered achievement thresholds (3 → 10 → 25) create decreasing rewards at each tier. Combined with the natural event decay at deeper levels, this produces an MLM-style attenuation curve without any schema changes or depth-tracking infrastructure. + +### Performance + +Each depth level is one indexed lookup on `claimed_invites(receiver_id)`. With `max_depth=10`, that's at most 10 index scans per trigger fire — negligible overhead. + +## Composing with Invite Virality + +EventReferral composes with `has_invite_achievements`. They serve different purposes: + +- **`has_invite_achievements`**: Credits the inviter when an invite is *claimed* (`invite_claimed` event) and when an invitee *earns an achievement* (`invitee_achieved_*` events). Always single-level. +- **`EventReferral`**: Credits the inviter(s) when an invitee performs a *table action*. Supports multi-level via `max_depth`. + +Both can be used simultaneously. A common pattern is `has_invite_achievements` for the social/gamification loop and `EventReferral` with `max_depth > 1` for the MLM referral reward chain. diff --git a/.agents/skills/constructive-sdk-events/references/invite-virality.md b/.agents/skills/constructive-sdk-events/references/invite-virality.md index 94347d6..aa093c4 100644 --- a/.agents/skills/constructive-sdk-events/references/invite-virality.md +++ b/.agents/skills/constructive-sdk-events/references/invite-virality.md @@ -141,6 +141,21 @@ User A invites User B → A invites more people (viral loop) ``` +## Multi-Level Referral Chains (EventReferral + max_depth) + +For MLM-style referral rewards that go beyond direct inviters, use `EventReferral` with `max_depth > 1` on table nodes. This walks the `claimed_invites` chain up to N levels: + +``` +User A invites B, B invites C, C invites D + +D creates a database (EventReferral with max_depth=5): + → C gets 'invitee_created_db' event (depth 1) + → B gets 'invitee_created_db' event (depth 2) + → A gets 'invitee_created_db' event (depth 3) +``` + +This is separate from `has_invite_achievements` (which only tracks invite claims and achievement completions). See [event-referral.md](event-referral.md) for the full reference, blueprint examples, and attenuation design. + ## Entity-Scoped Invites Each entity type gets its own independent invite achievement tracking. If both `app` and `org` have `has_invite_achievements: true`: