@dzhechkov/keysarium 1.1.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +7 -2
  2. package/package.json +10 -2
  3. package/templates/.claude/commands/brain-export.md +97 -20
  4. package/templates/.claude/commands/brain-import.md +75 -4
  5. package/templates/.claude/commands/dream.md +101 -0
  6. package/templates/.claude/commands/feature-adr.md +92 -0
  7. package/templates/.claude/commands/init-platform.md +161 -0
  8. package/templates/.claude/commands/learning-stats.md +130 -0
  9. package/templates/.claude/commands/workers.md +140 -0
  10. package/templates/.claude/rules/background-workers.md +72 -0
  11. package/templates/.claude/rules/dream-cycles.md +90 -0
  12. package/templates/.claude/rules/feature-adr-conventions.md +82 -0
  13. package/templates/.claude/rules/reward-learning.md +124 -0
  14. package/templates/.claude/shards/feature-adr.shard.md +98 -0
  15. package/templates/.claude/skills/feature-adr/SKILL.md +252 -0
  16. package/templates/.claude/skills/feature-adr/examples/sample-feature-output.md +257 -0
  17. package/templates/.claude/skills/feature-adr/modules/00-complexity-router.md +114 -0
  18. package/templates/.claude/skills/feature-adr/modules/01-requirements.md +113 -0
  19. package/templates/.claude/skills/feature-adr/modules/02-research.md +92 -0
  20. package/templates/.claude/skills/feature-adr/modules/03-adr.md +135 -0
  21. package/templates/.claude/skills/feature-adr/modules/04-ddd.md +114 -0
  22. package/templates/.claude/skills/feature-adr/modules/05-architecture.md +149 -0
  23. package/templates/.claude/skills/feature-adr/modules/06-implementation-plan.md +129 -0
  24. package/templates/.claude/skills/feature-adr/modules/07-code.md +126 -0
  25. package/templates/.claude/skills/feature-adr/modules/08-qe.md +167 -0
  26. package/templates/.claude/skills/feature-adr/references/adr-template.md +102 -0
  27. package/templates/.claude/skills/feature-adr/references/c4-template.md +120 -0
  28. package/templates/.claude/skills/feature-adr/references/complexity-matrix.md +139 -0
  29. package/templates/.claude/skills/feature-adr/references/qe-checklist.md +86 -0
  30. package/templates/CLAUDE.md +135 -8
  31. package/templates/docs/feature-adr/01-architecture.md +750 -0
  32. package/templates/docs/feature-adr/02-functionality.md +712 -0
  33. package/templates/docs/feature-adr/03-deployment-guide.md +614 -0
  34. package/templates/docs/feature-adr/04-admin-guide.md +564 -0
  35. package/templates/docs/feature-adr/05-user-guide.md +508 -0
  36. package/templates/docs/feature-adr/06-user-flows.md +1101 -0
  37. package/templates/docs/improvement-directions.md +16 -16
  38. package/templates/docs/keysarium/01-deployment-guide.md +21 -12
  39. package/templates/docs/keysarium/02-admin-guide.md +204 -3
  40. package/templates/docs/keysarium/03-user-guide.md +224 -0
  41. package/templates/docs/keysarium/05-architecture.md +181 -0
  42. package/templates/docs/keysarium/06-user-flows.md +235 -0
  43. package/templates/docs/keysarium-core/01-architecture.md +559 -0
  44. package/templates/docs/keysarium-core/02-functionality.md +953 -0
  45. package/templates/docs/keysarium-core/03-deployment-guide.md +625 -0
  46. package/templates/docs/keysarium-core/04-admin-guide.md +529 -0
  47. package/templates/docs/keysarium-core/05-user-guide.md +599 -0
  48. package/templates/docs/keysarium-core/06-user-flows.md +804 -0
  49. package/templates/lib/background-workers.md +278 -0
  50. package/templates/lib/dream-engine.md +460 -0
  51. package/templates/lib/judge-attestation.md +322 -0
  52. package/templates/lib/memory-protocol.md +344 -0
  53. package/templates/lib/platform-adapters.md +100 -0
  54. package/templates/lib/platform-templates/copilot.md +134 -0
  55. package/templates/lib/platform-templates/cursor.md +95 -0
  56. package/templates/lib/platform-templates/opencode.md +155 -0
  57. package/templates/lib/reward-tracker.md +256 -0
  58. package/templates/lib/witness-chain.md +372 -0
  59. package/templates/lib/worker-templates/brain-export.md +187 -0
  60. package/templates/lib/worker-templates/consolidate.md +180 -0
  61. package/templates/lib/worker-templates/dream-cycle.md +154 -0
  62. package/templates/lib/worker-templates/health-check.md +255 -0
@@ -0,0 +1,460 @@
1
+ # DreamEngine Protocol -- Background Pattern Consolidation
2
+
3
+ > Core protocol for the Dream Cycles system. Implements trigger evaluation, concept graph construction,
4
+ > cross-domain association detection, and insight generation from accumulated reward records.
5
+ >
6
+ > Builds on: `lib/memory-protocol.md` (reward records), `lib/reward-tracker.md` (aggregates & patterns)
7
+ > Runs via: `lib/worker-templates/dream-cycle.md` (background worker)
8
+
9
+ ## Overview
10
+
11
+ The DreamEngine analyzes accumulated reward data to produce higher-order insights that go beyond
12
+ what the reward tracker provides. While the tracker computes per-domain aggregates and detects
13
+ simple patterns, the DreamEngine builds a concept graph and discovers cross-domain associations,
14
+ temporal correlations, and phase interdependencies.
15
+
16
+ ## Directory Structure
17
+
18
+ ```
19
+ .keysarium/insights/
20
+ ├── trigger-state.json ← Trigger evaluation state (persistent)
21
+ ├── dream-20260301-120000.json ← Dream result (newest)
22
+ ├── dream-20260228-180000.json ← Dream result
23
+ ├── ... ← Max 10 files retained
24
+ └── dream-20260215-090000.json ← Dream result (oldest kept)
25
+ ```
26
+
27
+ ## Trigger Evaluation Protocol
28
+
29
+ ### Trigger State File
30
+
31
+ Location: `.keysarium/insights/trigger-state.json`
32
+
33
+ ```json
34
+ {
35
+ "version": "1.0",
36
+ "last_dream_completed_at": "2026-03-01T12:00:00Z",
37
+ "last_dream_id": "dream-20260301-120000",
38
+ "records_since_last_dream": 0,
39
+ "pending_events": [],
40
+ "config": {
41
+ "time_threshold_minutes": 60,
42
+ "volume_threshold": 20,
43
+ "event_triggers_enabled": true
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Default Trigger State (created if missing)
49
+
50
+ ```json
51
+ {
52
+ "version": "1.0",
53
+ "last_dream_completed_at": null,
54
+ "last_dream_id": null,
55
+ "records_since_last_dream": 0,
56
+ "pending_events": [],
57
+ "config": {
58
+ "time_threshold_minutes": 60,
59
+ "volume_threshold": 20,
60
+ "event_triggers_enabled": true
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### Trigger Evaluation Algorithm
66
+
67
+ 1. **Read state:** Read `.keysarium/insights/trigger-state.json`. If missing or malformed, create with defaults.
68
+ 2. **Check time trigger:** If `last_dream_completed_at` is null OR `now - last_dream_completed_at > config.time_threshold_minutes`, set `time_triggered = true`.
69
+ 3. **Check volume trigger:** If `records_since_last_dream >= config.volume_threshold`, set `volume_triggered = true`.
70
+ 4. **Check event trigger:** If `config.event_triggers_enabled` AND `pending_events` is non-empty, set `event_triggered = true`.
71
+ 5. **Result:** Return `{ should_dream: time_triggered OR volume_triggered OR event_triggered, reason: "time" | "volume" | "event" | "none" }`. If multiple triggers are true, use priority: event > volume > time.
72
+
73
+ ### Trigger State Update Protocol
74
+
75
+ #### On reward record store (called by memory_store integration):
76
+
77
+ ```
78
+ 1. Read trigger-state.json (create with defaults if missing)
79
+ 2. Increment records_since_last_dream by 1
80
+ 3. Write trigger-state.json
81
+ ```
82
+
83
+ #### On event (quality gate failure, case completion):
84
+
85
+ ```
86
+ 1. Read trigger-state.json (create with defaults if missing)
87
+ 2. Append to pending_events:
88
+ {
89
+ "event_type": "quality_gate_failure" | "case_completion",
90
+ "timestamp": "{current ISO8601}",
91
+ "details": "{description of event}"
92
+ }
93
+ 3. Write trigger-state.json
94
+ ```
95
+
96
+ #### On dream completion (called at end of dream cycle):
97
+
98
+ ```
99
+ 1. Read trigger-state.json
100
+ 2. Set last_dream_completed_at = current timestamp
101
+ 3. Set last_dream_id = current dream ID
102
+ 4. Set records_since_last_dream = 0
103
+ 5. Set pending_events = []
104
+ 6. Write trigger-state.json
105
+ ```
106
+
107
+ ## Dream Execution Protocol
108
+
109
+ ### Step 1: Load Data
110
+
111
+ **Input:** `.keysarium/memory/` directory tree
112
+
113
+ **Algorithm:**
114
+
115
+ 1. Check if `.keysarium/memory/` exists. If not, exit with status `no_data`.
116
+ 2. Read `.keysarium/memory/_stats/reward-summary.json` if it exists.
117
+ 3. Read `.keysarium/memory/_patterns/domain-patterns.json` if it exists.
118
+ 4. Scan all `*.json` files recursively under `.keysarium/memory/` (excluding `config.json`, `reward-summary.json`, `domain-patterns.json`).
119
+ 5. Parse each as a RewardRecord (schema defined in `lib/memory-protocol.md`).
120
+ 6. Exclude records where `expires_at < current_date`.
121
+ 7. Sort by `reward` DESC, then `timestamp` DESC.
122
+ 8. Take top 200 records.
123
+ 9. If fewer than 5 valid records, exit with status `insufficient_data`.
124
+
125
+ **Output:** `loaded_records` (list), `reward_summary` (object or null), `domain_patterns` (object or null)
126
+
127
+ ### Step 2: Build Concept Graph
128
+
129
+ **Input:** `loaded_records` from Step 1
130
+
131
+ **Algorithm:**
132
+
133
+ 1. Initialize empty graph: `{ nodes: [], edges: [] }`
134
+
135
+ 2. For each record in `loaded_records`:
136
+ a. Ensure domain node exists: `{ id: record.domain, type: "domain" }`
137
+ b. Ensure phase node exists: `{ id: record.phase, type: "phase" }`
138
+ c. Ensure skill node exists: `{ id: record.skill_used, type: "skill" }`
139
+ d. Ensure outcome node exists: `{ id: "{domain}_{phase}_{skill}_outcome", type: "outcome" }`
140
+ e. Create/update edges:
141
+ - domain -> phase (weight = record.reward)
142
+ - phase -> skill (weight = record.reward)
143
+ - skill -> outcome (weight = record.reward)
144
+
145
+ 3. For each node, compute aggregates:
146
+ ```
147
+ node.attributes = {
148
+ avg_reward: mean(all record rewards touching this node),
149
+ record_count: count(records touching this node),
150
+ trend: compute_trend(records touching this node)
151
+ }
152
+ ```
153
+
154
+ 4. Trend computation (same algorithm as `lib/reward-tracker.md`):
155
+ - Split records for node into two halves by timestamp
156
+ - If newer_avg - older_avg > 0.15 -> "improving"
157
+ - If older_avg - newer_avg > 0.15 -> "degrading"
158
+ - Otherwise -> "stable"
159
+ - Minimum 4 records required; otherwise "insufficient_data"
160
+
161
+ 5. For each edge, compute aggregates:
162
+ ```
163
+ edge.weight = mean(all record rewards traversing this edge)
164
+ edge.record_count = count(records traversing this edge)
165
+ ```
166
+
167
+ **Output:** `concept_graph` object
168
+
169
+ ### Step 3: Detect Cross-Domain Associations
170
+
171
+ **Input:** `concept_graph` from Step 2
172
+
173
+ **Algorithm:**
174
+
175
+ Scan for four types of associations:
176
+
177
+ #### 3a. Cross-Domain Phase Comparison
178
+
179
+ For each phase node, compare its performance across different domains:
180
+
181
+ ```
182
+ For each pair (domain_A, domain_B) where both have records for phase P:
183
+ gap = abs(avg_reward_A - avg_reward_B)
184
+ if gap > 0.15:
185
+ Create association:
186
+ type: "cross_domain"
187
+ description: "Phase {P} performs differently in {domain_A} (avg {A}) vs {domain_B} (avg {B})"
188
+ reward_gap: gap
189
+ evidence_count: min(records_A, records_B)
190
+ ```
191
+
192
+ #### 3b. Skill-Domain Mismatch
193
+
194
+ For each skill node, compare its performance across different domains:
195
+
196
+ ```
197
+ For each pair (domain_A, domain_B) where both use skill S:
198
+ gap = abs(avg_reward_A - avg_reward_B)
199
+ if gap > 0.15:
200
+ Create association:
201
+ type: "skill_mismatch"
202
+ description: "{skill} performs differently in {domain_A} vs {domain_B}"
203
+ reward_gap: gap
204
+ evidence_count: min(records_A, records_B)
205
+ ```
206
+
207
+ #### 3c. Phase Correlation
208
+
209
+ Find phases where low rewards in one correlate with low rewards in another:
210
+
211
+ ```
212
+ For each case_slug:
213
+ Collect phase rewards for this case
214
+ For each pair (phase_X, phase_Y):
215
+ If both reward_X < 0.5 AND reward_Y < 0.5:
216
+ Increment correlation counter for (phase_X, phase_Y)
217
+
218
+ For each (phase_X, phase_Y) pair with counter >= 3:
219
+ Create association:
220
+ type: "phase_correlation"
221
+ description: "Low performance in {phase_X} correlates with low performance in {phase_Y}"
222
+ evidence_count: counter
223
+ ```
224
+
225
+ #### 3d. Temporal Trends
226
+
227
+ Check for systematic reward changes over time across all data:
228
+
229
+ ```
230
+ Split all records into two halves by timestamp
231
+ If newer_avg - older_avg > 0.15:
232
+ Create association: type: "temporal", description: "Overall system performance is improving"
233
+ If older_avg - newer_avg > 0.15:
234
+ Create association: type: "temporal", description: "Overall system performance is degrading -- investigate"
235
+ ```
236
+
237
+ **Output:** `associations` (list of association objects)
238
+
239
+ ### Step 4: Generate Insights
240
+
241
+ **Input:** `associations` from Step 3, `domain_patterns` from Step 1
242
+
243
+ **Algorithm:**
244
+
245
+ 1. For each association, generate an Insight:
246
+
247
+ ```json
248
+ {
249
+ "insight_id": "{dream_id}-{sequential_number:03d}",
250
+ "type": "{map association type to insight type}",
251
+ "description": "{association.description + actionable advice}",
252
+ "confidence": "min(1.0, association.evidence_count / 10)",
253
+ "impact": "{high if gap > 0.3, medium if 0.15-0.3, low if < 0.15}",
254
+ "rank_score": "confidence * impact_weight",
255
+ "evidence_count": "association.evidence_count",
256
+ "domains": ["list of domains involved"],
257
+ "phases": ["list of phases involved"],
258
+ "skills": ["list of skills involved, if relevant"],
259
+ "created_at": "{current ISO8601}",
260
+ "dream_id": "{current dream cycle ID}"
261
+ }
262
+ ```
263
+
264
+ 2. Type mapping:
265
+ - `cross_domain` -> `performance`
266
+ - `skill_mismatch` -> `effectiveness`
267
+ - `phase_correlation` -> `anti_pattern`
268
+ - `temporal` -> `performance`
269
+
270
+ 3. Impact weight:
271
+ - `high` = 3
272
+ - `medium` = 2
273
+ - `low` = 1
274
+
275
+ 4. Actionable advice generation (appended to description):
276
+ - For `performance` (cross-domain): "Consider adjusting time budget for {phase} in {weaker_domain}"
277
+ - For `effectiveness` (skill mismatch): "Consider using alternative skill/approach for {skill} in {weaker_domain}"
278
+ - For `anti_pattern` (phase correlation): "Improving {earlier_phase} may improve {later_phase} outcomes"
279
+ - For `performance` (temporal improving): "Current approach is working well"
280
+ - For `performance` (temporal degrading): "Review recent changes to pipeline configuration"
281
+
282
+ 5. Additionally, promote existing domain patterns from `domain-patterns.json` to insights if they have confidence >= 0.5 and are not already captured by associations. These inherit `type` from their `category` field.
283
+
284
+ 6. Deduplicate: If an insight has the same `type` + normalized `description` as an insight from a recent dream (last 3 files in `.keysarium/insights/`), merge by summing evidence counts and recalculating confidence.
285
+
286
+ 7. Sort by `rank_score` DESC.
287
+
288
+ 8. Take top 20 insights.
289
+
290
+ **Output:** `insights` (list of Insight objects, max 20)
291
+
292
+ ### Step 5: Store and Clean
293
+
294
+ **Input:** `insights` from Step 4, `dream_id`, `trigger_reason`
295
+
296
+ **Algorithm:**
297
+
298
+ 1. **Ensure directory:** Create `.keysarium/insights/` if it does not exist.
299
+
300
+ 2. **Write dream result file:**
301
+
302
+ ```json
303
+ // .keysarium/insights/dream-{YYYYMMDD}-{HHmmss}.json
304
+ {
305
+ "version": "1.0",
306
+ "dream_id": "dream-{YYYYMMDD}-{HHmmss}",
307
+ "status": "completed",
308
+ "trigger_reason": "time | volume | event | manual",
309
+ "started_at": "{ISO8601}",
310
+ "completed_at": "{ISO8601}",
311
+ "records_analyzed": 200,
312
+ "concept_graph_nodes": 24,
313
+ "concept_graph_edges": 36,
314
+ "associations_found": 8,
315
+ "insights": [
316
+ {
317
+ "insight_id": "dream-20260301-143022-001",
318
+ "type": "performance",
319
+ "description": "Phase 2 takes significantly longer for banking cases (avg reward 0.55) compared to retail (avg reward 0.82). Consider allocating extra time budget for Phase 2 in banking domain.",
320
+ "confidence": 0.80,
321
+ "impact": "high",
322
+ "rank_score": 2.4,
323
+ "evidence_count": 8,
324
+ "domains": ["banking", "retail"],
325
+ "phases": ["phase-2"],
326
+ "skills": ["goap-research-ed25519"],
327
+ "created_at": "2026-03-01T14:30:22Z",
328
+ "dream_id": "dream-20260301-143022"
329
+ }
330
+ ],
331
+ "metadata": {
332
+ "domains_covered": ["banking", "retail", "enterprise"],
333
+ "phases_covered": ["phase-0", "phase-1", "phase-2", "phase-2.5", "phase-3", "phase-5"],
334
+ "total_reward_records_in_memory": 42
335
+ }
336
+ }
337
+ ```
338
+
339
+ 3. **Apply retention policy:**
340
+ - List all `dream-*.json` files in `.keysarium/insights/`
341
+ - Sort by filename (timestamp) DESC
342
+ - If count > 10, delete the oldest files until count = 10
343
+
344
+ 4. **Reset trigger state:** Call the trigger state reset protocol (see above).
345
+
346
+ 5. **Copy to worker output:** If running as a background worker, also write the dream result to `{WORKER_OUTPUT_DIR}/output/dream-{timestamp}.json`.
347
+
348
+ ## Insight Display Format
349
+
350
+ ### For `/dream insights`:
351
+
352
+ ```
353
+ Dream Insights (from dream-20260301-143022)
354
+ Trigger: volume (23 new records)
355
+ Generated: 2026-03-01 14:30:22
356
+
357
+ Top Insights:
358
+ ───────────────────────────────────────────────────────────────
359
+ 1. [0.80 HIGH] Phase 2 takes significantly longer for banking cases
360
+ (avg reward 0.55) compared to retail (avg reward 0.82).
361
+ Advice: Allocate extra time budget for Phase 2 in banking domain.
362
+ Domains: banking, retail | Phases: phase-2 | Evidence: 8 records
363
+
364
+ 2. [0.60 MEDIUM] TRIZ methodology produces better results than
365
+ Game Theory for retail problem-solving.
366
+ Advice: Prefer TRIZ framework for retail domain in Phase 3.
367
+ Domains: retail | Phases: phase-3 | Skills: problem-solver-enhanced | Evidence: 6
368
+
369
+ 3. [0.55 HIGH] Low performance in Phase 2 correlates with low
370
+ performance in Phase 5 across multiple cases.
371
+ Advice: Improving Phase 2 (Research) may improve Phase 5 (Presentation).
372
+ Phases: phase-2, phase-5 | Evidence: 4 records
373
+ ───────────────────────────────────────────────────────────────
374
+ Showing 3 of 12 insights (confidence >= 0.3)
375
+ ```
376
+
377
+ ### For `/dream status`:
378
+
379
+ ```
380
+ Dream Cycle Status
381
+ ───────────────────────────────────────────────────
382
+ Last dream: dream-20260301-143022 (2h 15m ago)
383
+ Records since: 7 / 20 (volume threshold)
384
+ Pending events: 0
385
+ Time threshold: 60 min (MET -- 135 min elapsed)
386
+ Volume threshold: 20 (not met -- 7/20)
387
+ Event trigger: enabled (no pending events)
388
+
389
+ Status: TRIGGER MET (time)
390
+ Recommendation: Run /dream run or it will auto-trigger at next pipeline start
391
+ ───────────────────────────────────────────────────
392
+ ```
393
+
394
+ ## Error Handling
395
+
396
+ | Error | Behavior |
397
+ |-------|----------|
398
+ | `.keysarium/memory/` does not exist | Exit with status `no_data`, zero insights |
399
+ | Fewer than 5 valid records | Exit with status `insufficient_data`, zero insights |
400
+ | `reward-summary.json` missing | Proceed without summary data (build graph from raw records only) |
401
+ | `domain-patterns.json` missing | Proceed without existing patterns (generate all from scratch) |
402
+ | Malformed reward record | Skip record, log warning, continue processing |
403
+ | `trigger-state.json` missing or malformed | Create with defaults |
404
+ | `.keysarium/insights/` does not exist | Create on first write |
405
+ | Write failure (permissions, disk) | Log error, set dream status to `failed` |
406
+ | Previous dream file read failure (for dedup) | Skip deduplication, generate fresh insights |
407
+
408
+ ## Integration Points
409
+
410
+ ### With memory_store() (lib/memory-protocol.md)
411
+
412
+ After each `memory_store()` call, the system should increment the trigger state counter:
413
+
414
+ ```
415
+ Protocol addition to memory_store():
416
+ 7. (After writing reward record) Read .keysarium/insights/trigger-state.json
417
+ 8. Increment records_since_last_dream
418
+ 9. Write updated trigger-state.json
419
+ ```
420
+
421
+ This is an advisory integration -- if trigger-state.json does not exist, skip silently.
422
+
423
+ ### With /casarium Phase 0
424
+
425
+ At the start of Phase 0 (Discovery), before beginning analysis:
426
+
427
+ ```
428
+ 1. Check if .keysarium/insights/ exists
429
+ 2. If it contains dream-*.json files, read the most recent one
430
+ 3. Filter insights by detected domain (if domain is already known)
431
+ 4. Log: "Loaded {count} dream insights for {domain} domain"
432
+ 5. Apply actionable advice to phase time budget adjustments
433
+ ```
434
+
435
+ ### With /brain-export
436
+
437
+ When brain export runs, include dream insights in the container:
438
+
439
+ ```json
440
+ {
441
+ "dream_insights": {
442
+ "latest_dream_id": "dream-20260301-143022",
443
+ "insight_count": 12,
444
+ "top_insights": [/* top 5 insights by rank_score */],
445
+ "exported_from": ".keysarium/insights/"
446
+ }
447
+ }
448
+ ```
449
+
450
+ ### With Background Workers (lib/background-workers.md)
451
+
452
+ Dream cycle is registered as a worker type:
453
+
454
+ | Type | Description | Model | Template |
455
+ |------|-------------|-------|----------|
456
+ | `dream-cycle` | Run DreamEngine: build concept graph, generate insights | sonnet | `lib/worker-templates/dream-cycle.md` |
457
+
458
+ ## Modular Reuse
459
+
460
+ The DreamEngine protocol is domain-agnostic. It operates on any data that conforms to the RewardRecord schema from `lib/memory-protocol.md`. The association detection rules and insight generation can be adapted for any multi-phase pipeline with reward tracking.