@nextsparkjs/ai-workflow 0.1.0-beta.100

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,671 @@
1
+ ---
2
+ name: scheduled-actions
3
+ description: |
4
+ Scheduled Actions system for background task processing in this application.
5
+ Covers action scheduling, handler creation, webhook configuration, and cron processing.
6
+ Use this skill when creating, debugging, or configuring scheduled actions.
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit
8
+ version: 1.1.0
9
+ ---
10
+
11
+ # Scheduled Actions Skill
12
+
13
+ Patterns for background task processing and webhook systems.
14
+
15
+ ## Architecture Overview
16
+
17
+ ```
18
+ SCHEDULED ACTIONS SYSTEM:
19
+
20
+ Core Layer (core/lib/scheduled-actions/):
21
+ ├── scheduler.ts # scheduleAction(), scheduleRecurringAction()
22
+ ├── processor.ts # Cron processing logic
23
+ ├── registry.ts # Handler registration
24
+ └── types.ts # TypeScript interfaces
25
+
26
+ Theme Layer (contents/themes/{theme}/lib/scheduled-actions/):
27
+ ├── index.ts # Handler initialization + registerAllHandlers()
28
+ ├── entity-hooks.ts # Entity event → action mapping
29
+ └── handlers/ # Handler implementations
30
+ ├── webhook.ts # Webhook sender
31
+ ├── email.ts # Email sender (if configured)
32
+ └── {custom}.ts # Custom handlers
33
+
34
+ Configuration (contents/themes/{theme}/config/app.config.ts):
35
+ └── scheduledActions: {
36
+ enabled: true,
37
+ deduplication: { windowSeconds: 10 },
38
+ webhooks: { endpoints: {...}, patterns: {...} }
39
+ }
40
+
41
+ Flow:
42
+ Entity Event → Entity Hook → scheduleAction() → DB Table → Cron → Handler → Result
43
+ ```
44
+
45
+ > **📍 Context-Aware Paths:** Core layer (`core/lib/scheduled-actions/`) is read-only in consumer projects.
46
+ > Create handlers in `contents/themes/{theme}/lib/scheduled-actions/handlers/`.
47
+ > See `core-theme-responsibilities` skill for complete rules.
48
+
49
+ ## Initialization Flow
50
+
51
+ **CRITICAL:** Initialization happens in `instrumentation.ts` at server startup.
52
+
53
+ ```
54
+ Server Start (instrumentation.ts)
55
+
56
+ ├─ initializeScheduledActions() # Sync - registers handlers
57
+ │ └─ Calls theme's registerAllHandlers()
58
+ │ ├─ Register action handlers (e.g., content:publish)
59
+ │ └─ Register entity hooks (e.g., on entity.contents.updated)
60
+
61
+ └─ initializeRecurringActions() # Async - creates DB rows (once per server)
62
+ └─ Calls theme's registerRecurringActions()
63
+ └─ Creates recurring action rows in DB if not exist
64
+ (e.g., social:refresh-tokens every 30 minutes)
65
+
66
+ Then...
67
+
68
+ Cron Endpoint (/api/v1/cron/process) - Called every ~1 minute
69
+
70
+ └─ processPendingActions() # Async - executes due actions
71
+ └─ Processes actions where scheduledAt <= now
72
+ ```
73
+
74
+ ### Server Initialization (instrumentation.ts)
75
+
76
+ ```typescript
77
+ // instrumentation.ts (root of project)
78
+ export async function register() {
79
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
80
+ const {
81
+ initializeScheduledActions,
82
+ initializeRecurringActions,
83
+ } = await import('@nextsparkjs/core/lib/scheduled-actions')
84
+
85
+ console.log('[Instrumentation] Initializing scheduled actions...')
86
+
87
+ // 1. Register handlers + entity hooks (sync, idempotent)
88
+ initializeScheduledActions()
89
+
90
+ // 2. Create recurring actions in DB (async, idempotent)
91
+ await initializeRecurringActions()
92
+
93
+ console.log('[Instrumentation] ✅ Scheduled actions initialized')
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### Cron Endpoint (Simplified)
99
+
100
+ ```typescript
101
+ // app/api/v1/cron/process/route.ts
102
+ import {
103
+ processPendingActions,
104
+ cleanupOldActions
105
+ } from '@nextsparkjs/core/lib/scheduled-actions'
106
+
107
+ export async function GET(request: NextRequest): Promise<NextResponse> {
108
+ // Handlers already registered via instrumentation.ts
109
+
110
+ // 1. Validate CRON_SECRET...
111
+ // 2. Process pending actions...
112
+ // 3. Cleanup old actions...
113
+ }
114
+ ```
115
+
116
+ ### Theme Registration Functions
117
+
118
+ ```typescript
119
+ // contents/themes/{theme}/lib/scheduled-actions/index.ts
120
+
121
+ // Called by initializeScheduledActions() - registers handlers
122
+ export function registerAllHandlers() {
123
+ registerContentPublishHandler() // One-time actions
124
+ registerTokenRefreshHandler() // Handler for recurring action
125
+ registerEntityHooks() // Entity event → action mapping
126
+ }
127
+
128
+ // Called by initializeRecurringActions() - creates DB rows
129
+ export async function registerRecurringActions(): Promise<void> {
130
+ // Check if already exists to avoid duplicates
131
+ const existing = await queryWithRLS(
132
+ `SELECT id FROM "scheduled_actions" WHERE "actionType" = $1 AND "recurringInterval" IS NOT NULL`,
133
+ ['social:refresh-tokens'],
134
+ null
135
+ )
136
+
137
+ if (existing.length === 0) {
138
+ await scheduleRecurringAction('social:refresh-tokens', {}, 'every-30-minutes')
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Action Types
144
+
145
+ | Type | Trigger | Created By | Example |
146
+ |------|---------|------------|---------|
147
+ | **One-time** | Entity event | Entity hooks | `content:publish` when content.status='scheduled' |
148
+ | **Recurring** | Cron interval | `registerRecurringActions()` | `social:refresh-tokens` every 30 min |
149
+
150
+ > **✅ ALWAYS use `instrumentation.ts`** for scheduled actions initialization.
151
+ > This is the correct place because:
152
+ > - Runs ONCE at server startup (not on every cron request)
153
+ > - Entity hooks are registered before any API requests
154
+ > - Official Next.js 13+ pattern for global initialization
155
+ > - Idempotent functions handle edge cases safely
156
+
157
+ ## When to Use This Skill
158
+
159
+ - Creating new action handlers
160
+ - Setting up webhooks for entity events
161
+ - Debugging pending/failed actions
162
+ - Configuring deduplication or batching
163
+ - Understanding the scheduled actions flow
164
+
165
+ ## Key Files Reference
166
+
167
+ | File | Purpose |
168
+ |------|---------|
169
+ | `core/lib/scheduled-actions/scheduler.ts` | `scheduleAction()`, `scheduleRecurringAction()` |
170
+ | `core/lib/scheduled-actions/processor.ts` | Cron processing logic |
171
+ | `core/lib/scheduled-actions/registry.ts` | Handler registration |
172
+ | `contents/themes/{theme}/lib/scheduled-actions/index.ts` | Handler initialization |
173
+ | `contents/themes/{theme}/lib/scheduled-actions/handlers/` | Handler implementations |
174
+ | `contents/themes/{theme}/config/app.config.ts` | Configuration section |
175
+
176
+ ## Creating Action Handlers
177
+
178
+ ### Handler Template
179
+
180
+ ```typescript
181
+ // contents/themes/{theme}/lib/scheduled-actions/handlers/{name}.ts
182
+ import { registerScheduledAction } from '@/core/lib/scheduled-actions'
183
+
184
+ interface MyPayload {
185
+ entityId: string
186
+ teamId: string
187
+ data: Record<string, unknown>
188
+ }
189
+
190
+ export function registerMyHandler() {
191
+ registerScheduledAction('my-action:type', async (payload, action) => {
192
+ const data = payload as MyPayload
193
+
194
+ try {
195
+ // Implementation logic
196
+ await processMyAction(data)
197
+
198
+ return {
199
+ success: true,
200
+ message: 'Action completed successfully'
201
+ }
202
+ } catch (error) {
203
+ return {
204
+ success: false,
205
+ message: error instanceof Error ? error.message : 'Unknown error'
206
+ }
207
+ }
208
+ })
209
+ }
210
+ ```
211
+
212
+ ### Registering Handler
213
+
214
+ ```typescript
215
+ // contents/themes/{theme}/lib/scheduled-actions/index.ts
216
+ import { registerMyHandler } from './handlers/my-handler'
217
+
218
+ let initialized = false
219
+
220
+ export function registerAllHandlers() {
221
+ if (initialized) return
222
+ initialized = true
223
+
224
+ // Register all handlers
225
+ registerWebhookHandler()
226
+ registerMyHandler() // Add new handler here
227
+ }
228
+ ```
229
+
230
+ ### Handler Types
231
+
232
+ | Type | Description | Use Case |
233
+ |------|-------------|----------|
234
+ | `webhook` | Send HTTP POST to external endpoint | Integrations, notifications |
235
+ | `email` | Send transactional emails | User notifications |
236
+ | `data-processor` | Process/transform data | ETL, aggregations |
237
+ | `cleanup` | Clean up old records | Maintenance tasks |
238
+
239
+ ## Webhook Configuration
240
+
241
+ ### Entity Hook Pattern
242
+
243
+ ```typescript
244
+ // contents/themes/{theme}/lib/scheduled-actions/entity-hooks.ts
245
+ import { scheduleAction } from '@/core/lib/scheduled-actions'
246
+ import { hookSystem } from '@/core/lib/hooks'
247
+
248
+ export function registerEntityHooks() {
249
+ // Hook for task creation
250
+ hookSystem.register('entity.tasks.created', async ({ entity, teamId }) => {
251
+ await scheduleAction({
252
+ type: 'webhook:send',
253
+ payload: {
254
+ endpointKey: 'tasks',
255
+ event: 'task.created',
256
+ data: entity
257
+ },
258
+ scheduledFor: new Date(),
259
+ teamId
260
+ })
261
+ })
262
+
263
+ // Hook for task updates
264
+ hookSystem.register('entity.tasks.updated', async ({ entity, teamId }) => {
265
+ await scheduleAction({
266
+ type: 'webhook:send',
267
+ payload: {
268
+ endpointKey: 'tasks',
269
+ event: 'task.updated',
270
+ data: entity
271
+ },
272
+ scheduledFor: new Date(),
273
+ teamId
274
+ })
275
+ })
276
+ }
277
+ ```
278
+
279
+ ### Webhook Configuration in app.config.ts
280
+
281
+ ```typescript
282
+ // contents/themes/{theme}/config/app.config.ts
283
+ export const appConfig = {
284
+ // ... other config
285
+
286
+ scheduledActions: {
287
+ enabled: true,
288
+
289
+ deduplication: {
290
+ windowSeconds: 10 // 0 to disable
291
+ },
292
+
293
+ webhooks: {
294
+ endpoints: {
295
+ // Key -> Environment variable mapping
296
+ tasks: 'WEBHOOK_URL_TASKS',
297
+ subscriptions: 'WEBHOOK_URL_SUBSCRIPTIONS',
298
+ default: 'WEBHOOK_URL_DEFAULT'
299
+ },
300
+ patterns: {
301
+ // Event pattern -> Endpoint key
302
+ 'task.*': 'tasks',
303
+ 'subscription.*': 'subscriptions',
304
+ '*': 'default' // Fallback
305
+ }
306
+ }
307
+ }
308
+ }
309
+ ```
310
+
311
+ ### Environment Variables
312
+
313
+ ```env
314
+ # Required for cron processing
315
+ CRON_SECRET=your-secure-secret-min-32-chars
316
+
317
+ # Webhook URLs (one per endpoint key)
318
+ WEBHOOK_URL_TASKS=https://your-webhook-url/tasks
319
+ WEBHOOK_URL_SUBSCRIPTIONS=https://your-webhook-url/subs
320
+ WEBHOOK_URL_DEFAULT=https://fallback-url
321
+ ```
322
+
323
+ ## Scheduling Actions
324
+
325
+ ### Immediate Action
326
+
327
+ ```typescript
328
+ import { scheduleAction } from '@/core/lib/scheduled-actions'
329
+
330
+ await scheduleAction({
331
+ type: 'my-action:type',
332
+ payload: {
333
+ entityId: 'abc123',
334
+ data: { field: 'value' }
335
+ },
336
+ scheduledFor: new Date(), // Now
337
+ teamId: 'team_123'
338
+ })
339
+ ```
340
+
341
+ ### Delayed Action
342
+
343
+ ```typescript
344
+ await scheduleAction({
345
+ type: 'reminder:send',
346
+ payload: { userId: 'user_123', message: 'Follow up' },
347
+ scheduledFor: new Date(Date.now() + 24 * 60 * 60 * 1000), // Tomorrow
348
+ teamId: 'team_123'
349
+ })
350
+ ```
351
+
352
+ ### Recurring Action
353
+
354
+ **IMPORTANT:** Recurring actions should be created in `registerRecurringActions()`, NOT ad-hoc.
355
+
356
+ ```typescript
357
+ // In theme's lib/scheduled-actions/index.ts
358
+ export async function registerRecurringActions(): Promise<void> {
359
+ const { scheduleRecurringAction } = await import('@nextsparkjs/core/lib/scheduled-actions')
360
+
361
+ // Check if already exists (idempotent)
362
+ const existing = await queryWithRLS(
363
+ `SELECT id FROM "scheduled_actions" WHERE "actionType" = $1 AND "recurringInterval" IS NOT NULL AND status = 'pending'`,
364
+ ['report:generate'],
365
+ null
366
+ )
367
+
368
+ if (existing.length > 0) return
369
+
370
+ // Available intervals: 'every-minute', 'every-5-minutes', 'every-15-minutes',
371
+ // 'every-30-minutes', 'every-hour', 'every-6-hours', 'every-day'
372
+ await scheduleRecurringAction(
373
+ 'report:generate',
374
+ { reportType: 'daily-summary' },
375
+ 'every-day'
376
+ )
377
+ }
378
+ ```
379
+
380
+ **Flow:** After processing a recurring action, it auto-reschedules for the next interval.
381
+
382
+ ## Recurrence Types
383
+
384
+ For recurring actions, you can control how the next execution time is calculated using the `recurrenceType` parameter.
385
+
386
+ ### Fixed Schedule (default)
387
+
388
+ Maintains exact schedule times, ignoring execution delays. Ideal for reports, batch jobs, or any task that should run at specific times.
389
+
390
+ ```typescript
391
+ await scheduleRecurringAction(
392
+ 'report:daily',
393
+ { type: 'sales' },
394
+ 'daily',
395
+ {
396
+ scheduledAt: new Date('2026-02-05T12:00:00Z'),
397
+ recurrenceType: 'fixed' // or omit, defaults to 'fixed'
398
+ }
399
+ )
400
+ ```
401
+
402
+ **Behavior:**
403
+ - Action scheduled: 12:00:00
404
+ - Actually runs: 12:05:00 (5 min delay due to cron or processing)
405
+ - **Next scheduled: 12:00:00 tomorrow** ✅ (maintains exact time)
406
+
407
+ ### Rolling Interval
408
+
409
+ Calculates intervals from actual completion time. Ideal for token refreshes, polling, or tasks where consistent spacing matters more than exact timing.
410
+
411
+ ```typescript
412
+ await scheduleRecurringAction(
413
+ 'social:refresh-tokens',
414
+ {},
415
+ 'every-30-minutes',
416
+ {
417
+ recurrenceType: 'rolling'
418
+ }
419
+ )
420
+ ```
421
+
422
+ **Behavior:**
423
+ - Action scheduled: 12:00:00
424
+ - Actually runs: 12:15:00 (15 min delay)
425
+ - **Next scheduled: 12:45:00** ✅ (30 min from actual execution)
426
+
427
+ ### Comparison
428
+
429
+ | Scenario | Fixed | Rolling |
430
+ |----------|-------|---------|
431
+ | Daily report at 9:00 AM | ✅ Runs at 9:00 daily (or as soon as possible) | ❌ Drifts if delayed |
432
+ | Token refresh every 30 min | ❌ Can stack up if delayed | ✅ Consistent 30 min gaps |
433
+ | Batch cleanup at midnight | ✅ Runs at midnight sharp | ❌ Drifts based on execution time |
434
+ | API polling every 5 min | ⚠️ Can create bursts if delayed | ✅ Steady 5 min spacing |
435
+
436
+ ### Usage Examples
437
+
438
+ ```typescript
439
+ // Example 1: Fixed - Daily backup at 2:00 AM
440
+ await scheduleRecurringAction(
441
+ 'backup:database',
442
+ { retention: 30 },
443
+ 'daily',
444
+ {
445
+ scheduledAt: new Date('2026-02-05T02:00:00Z'),
446
+ recurrenceType: 'fixed'
447
+ }
448
+ )
449
+
450
+ // Example 2: Rolling - Check external API every 15 minutes
451
+ await scheduleRecurringAction(
452
+ 'external:sync',
453
+ { endpoint: 'https://api.example.com' },
454
+ 'every-15-minutes',
455
+ {
456
+ recurrenceType: 'rolling'
457
+ }
458
+ )
459
+
460
+ // Example 3: Fixed - Weekly report on Mondays at 8:00 AM
461
+ await scheduleRecurringAction(
462
+ 'report:weekly',
463
+ { format: 'pdf' },
464
+ 'weekly',
465
+ {
466
+ scheduledAt: new Date('2026-02-10T08:00:00Z'), // Monday
467
+ recurrenceType: 'fixed'
468
+ }
469
+ )
470
+ ```
471
+
472
+ ## Debugging Actions
473
+
474
+ ### Check Pending Actions
475
+
476
+ ```bash
477
+ curl "http://localhost:5173/api/v1/devtools/scheduled-actions?status=pending" \
478
+ -H "Authorization: Bearer API_KEY"
479
+ ```
480
+
481
+ ### Check Failed Actions
482
+
483
+ ```bash
484
+ curl "http://localhost:5173/api/v1/devtools/scheduled-actions?status=failed" \
485
+ -H "Authorization: Bearer API_KEY"
486
+ ```
487
+
488
+ ### Manually Trigger Processing
489
+
490
+ ```bash
491
+ curl "http://localhost:5173/api/v1/cron/process" \
492
+ -H "x-cron-secret: CRON_SECRET"
493
+ ```
494
+
495
+ ### Console Log Patterns
496
+
497
+ ```
498
+ [ScheduledActions] Processing 5 pending actions
499
+ [ScheduledActions] Action abc123 completed successfully
500
+ [ScheduledActions] Action xyz789 failed: Connection timeout
501
+ [ScheduledActions] Handler not found for type: unknown:type
502
+ ```
503
+
504
+ ## Deduplication
505
+
506
+ ### Purpose
507
+
508
+ Prevents duplicate actions when the same event fires multiple times in quick succession.
509
+
510
+ ### Configuration
511
+
512
+ ```typescript
513
+ scheduledActions: {
514
+ deduplication: {
515
+ windowSeconds: 10 // Actions with same type+payload within 10s are deduplicated
516
+ }
517
+ }
518
+ ```
519
+
520
+ ### Behavior
521
+
522
+ 1. When action is scheduled, system creates hash of `type + payload`
523
+ 2. If action with same hash exists within window, new action is skipped
524
+ 3. Window is reset when action is processed
525
+
526
+ ### Disabling
527
+
528
+ Set `windowSeconds: 0` to disable deduplication entirely.
529
+
530
+ ## API Endpoints
531
+
532
+ | Endpoint | Method | Auth | Purpose |
533
+ |----------|--------|------|---------|
534
+ | `/api/v1/cron/process` | POST | x-cron-secret | Trigger action processing |
535
+ | `/api/v1/devtools/scheduled-actions` | GET | API Key | List actions (debug) |
536
+ | `/api/v1/devtools/scheduled-actions/:id` | DELETE | API Key | Delete action (debug) |
537
+
538
+ ## Troubleshooting
539
+
540
+ | Issue | Cause | Solution |
541
+ |-------|-------|----------|
542
+ | Handler not found | Not registered | Add to `index.ts` `registerAllHandlers()` |
543
+ | Webhook not sent | Pattern mismatch | Check patterns in `app.config.ts` |
544
+ | Duplicate actions | Dedup disabled | Set `windowSeconds > 0` |
545
+ | Actions stuck pending | Cron not running | Verify cron service and `CRON_SECRET` |
546
+ | 401 on cron endpoint | Wrong header | Use `x-cron-secret` (not `Authorization`) |
547
+ | Env variable undefined | Not set | Add to `.env` and restart server |
548
+ | Recurring action not created | Missing `registerRecurringActions()` | Export function from theme's `index.ts` |
549
+ | Recurring action not running | Handlers not initialized | Ensure `instrumentation.ts` exists and runs |
550
+ | Entity hooks not firing | Handlers not registered early | Use `instrumentation.ts`, not cron endpoint |
551
+
552
+ ## Anti-Patterns
553
+
554
+ ```typescript
555
+ // NEVER: Process actions synchronously in API routes
556
+ // This blocks the response
557
+ app.post('/api/entity', async (req, res) => {
558
+ const entity = await createEntity(req.body)
559
+ await sendWebhook(entity) // WRONG - blocks response
560
+ res.json(entity)
561
+ })
562
+
563
+ // CORRECT: Schedule action for async processing
564
+ app.post('/api/entity', async (req, res) => {
565
+ const entity = await createEntity(req.body)
566
+ await scheduleAction({
567
+ type: 'webhook:send',
568
+ payload: { entity },
569
+ scheduledFor: new Date(),
570
+ teamId: req.teamId
571
+ })
572
+ res.json(entity)
573
+ })
574
+
575
+ // NEVER: Store sensitive data in payload
576
+ await scheduleAction({
577
+ type: 'email:send',
578
+ payload: {
579
+ password: 'secret123' // WRONG - stored in DB
580
+ }
581
+ })
582
+
583
+ // CORRECT: Store references only
584
+ await scheduleAction({
585
+ type: 'email:send',
586
+ payload: {
587
+ userId: 'user_123', // Lookup at processing time
588
+ templateKey: 'password-reset'
589
+ }
590
+ })
591
+
592
+ // NEVER: Forget to handle errors in handlers
593
+ registerScheduledAction('my:action', async (payload) => {
594
+ await riskyOperation(payload) // WRONG - unhandled rejection
595
+ })
596
+
597
+ // CORRECT: Always return success/failure
598
+ registerScheduledAction('my:action', async (payload) => {
599
+ try {
600
+ await riskyOperation(payload)
601
+ return { success: true, message: 'Done' }
602
+ } catch (error) {
603
+ return { success: false, message: error.message }
604
+ }
605
+ })
606
+
607
+ // NEVER: Initialize in API routes (adds overhead to every request)
608
+ // /api/v1/cron/process/route.ts
609
+ export async function GET(request: NextRequest) {
610
+ initializeScheduledActions() // WRONG - runs on every cron call
611
+ await initializeRecurringActions() // WRONG - unnecessary DB queries
612
+ // ...process actions
613
+ }
614
+
615
+ // CORRECT: Initialize in instrumentation.ts (runs once at startup)
616
+ // instrumentation.ts
617
+ export async function register() {
618
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
619
+ const {
620
+ initializeScheduledActions,
621
+ initializeRecurringActions,
622
+ } = await import('@nextsparkjs/core/lib/scheduled-actions')
623
+
624
+ initializeScheduledActions() // ✅ Registers handlers + hooks
625
+ await initializeRecurringActions() // ✅ Creates recurring actions in DB
626
+ }
627
+ }
628
+ ```
629
+
630
+ ## Checklist
631
+
632
+ ### Creating New Handler
633
+
634
+ - [ ] Handler file created in `handlers/` directory
635
+ - [ ] Handler registered in `index.ts` `registerAllHandlers()`
636
+ - [ ] Handler returns `{ success, message }` object
637
+ - [ ] Error handling with try/catch
638
+ - [ ] Registry rebuilt: `node core/scripts/build/registry.mjs`
639
+
640
+ ### Adding Webhook
641
+
642
+ - [ ] Entity hook registered in `entity-hooks.ts`
643
+ - [ ] Endpoint key added to `webhooks.endpoints` config
644
+ - [ ] Pattern added to `webhooks.patterns` config
645
+ - [ ] Environment variable added to `.env`
646
+ - [ ] Environment variable documented in `.env.example`
647
+
648
+ ### Debugging
649
+
650
+ - [ ] Check if `scheduledActions.enabled: true` in config
651
+ - [ ] Verify `CRON_SECRET` is set
652
+ - [ ] Check handler is registered (console log on startup)
653
+ - [ ] Query devtools endpoint for action status
654
+ - [ ] Check console for `[ScheduledActions]` logs
655
+
656
+ ## Related Skills
657
+
658
+ - `entity-api` - API endpoints that trigger entity hooks
659
+ - `service-layer` - Service patterns for action processing
660
+ - `nextjs-api-development` - Cron endpoint patterns
661
+ - `database-migrations` - scheduled_actions table structure
662
+
663
+ ## Documentation
664
+
665
+ Full documentation: `core/docs/20-scheduled-actions/`
666
+ - `01-overview.md` - System overview
667
+ - `02-scheduling.md` - Scheduling patterns
668
+ - `03-handlers.md` - Handler development
669
+ - `04-webhooks.md` - Webhook configuration
670
+ - `05-cron.md` - Cron processing
671
+ - `06-deduplication.md` - Deduplication system