@polymorphism-tech/morph-spec 2.4.0 → 3.0.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.
- package/CLAUDE.md +158 -26
- package/LICENSE +72 -72
- package/bin/detect-agents.js +225 -225
- package/bin/morph-spec.js +8 -0
- package/bin/render-template.js +302 -302
- package/bin/semantic-detect-agents.js +246 -246
- package/bin/validate-agents-skills.js +251 -251
- package/bin/validate-agents.js +69 -69
- package/bin/validate-phase.js +263 -263
- package/content/.azure/README.md +293 -293
- package/content/.azure/docs/azure-devops-setup.md +454 -454
- package/content/.azure/docs/branch-strategy.md +398 -398
- package/content/.azure/docs/local-development.md +515 -515
- package/content/.azure/pipelines/pipeline-variables.yml +34 -34
- package/content/.azure/pipelines/prod-pipeline.yml +319 -319
- package/content/.azure/pipelines/staging-pipeline.yml +234 -234
- package/content/.azure/pipelines/templates/build-dotnet.yml +75 -75
- package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -94
- package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -120
- package/content/.azure/pipelines/templates/infra-deploy.yml +90 -90
- package/content/.claude/commands/morph-archive.md +79 -79
- package/content/.claude/commands/morph-deploy.md +529 -0
- package/content/.claude/commands/morph-infra.md +209 -209
- package/content/.claude/commands/morph-preflight.md +227 -227
- package/content/.claude/commands/morph-troubleshoot.md +122 -122
- package/content/.claude/settings.local.json +15 -15
- package/content/.claude/skills/infra/azure-deploy-specialist.md +699 -0
- package/content/.claude/skills/level-0-meta/README.md +7 -0
- package/content/.claude/skills/{checklists → level-0-meta}/morph-checklist.md +117 -117
- package/content/.claude/skills/level-1-workflows/README.md +7 -0
- package/content/.claude/skills/{workflows → level-1-workflows}/morph-replicate.md +213 -213
- package/content/.claude/skills/{workflows → level-1-workflows}/phase-clarify.md +131 -131
- package/content/.claude/skills/{workflows → level-1-workflows}/phase-design.md +213 -205
- package/content/.claude/skills/{workflows → level-1-workflows}/phase-setup.md +106 -92
- package/content/.claude/skills/{workflows → level-1-workflows}/phase-tasks.md +164 -164
- package/content/.claude/skills/{workflows → level-1-workflows}/phase-uiux.md +169 -138
- package/content/.claude/skills/level-2-domains/README.md +14 -0
- package/content/.claude/skills/{specialists → level-2-domains/quality}/testing-specialist.md +126 -126
- package/content/.claude/skills/level-3-technologies/README.md +7 -0
- package/content/.claude/skills/level-4-patterns/README.md +7 -0
- package/content/.claude/skills/specialists/prompt-engineer.md +189 -0
- package/content/.claude/skills/specialists/seo-growth-hacker.md +320 -0
- package/content/.morph/.morphversion +5 -5
- package/content/.morph/archive/.gitkeep +25 -25
- package/content/.morph/config/agents.json +742 -358
- package/content/.morph/config/config.template.json +33 -0
- package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -392
- package/content/.morph/docs/workflows/enforcement-pipeline.md +668 -0
- package/content/.morph/examples/api-nextjs/README.md +241 -241
- package/content/.morph/examples/api-nextjs/contracts.ts +307 -307
- package/content/.morph/examples/api-nextjs/spec.md +399 -399
- package/content/.morph/examples/api-nextjs/tasks.md +168 -168
- package/content/.morph/examples/micro-saas/README.md +125 -125
- package/content/.morph/examples/micro-saas/contracts.cs +358 -358
- package/content/.morph/examples/micro-saas/decisions.md +246 -246
- package/content/.morph/examples/micro-saas/spec.md +236 -236
- package/content/.morph/examples/micro-saas/tasks.md +150 -150
- package/content/.morph/examples/multi-agent/README.md +309 -309
- package/content/.morph/examples/multi-agent/contracts.cs +433 -433
- package/content/.morph/examples/multi-agent/spec.md +479 -479
- package/content/.morph/examples/multi-agent/tasks.md +185 -185
- package/content/.morph/examples/scheduled-reports/decisions.md +158 -158
- package/content/.morph/examples/scheduled-reports/proposal.md +95 -95
- package/content/.morph/examples/scheduled-reports/spec.md +267 -267
- package/content/.morph/examples/state-v3.json +188 -188
- package/content/.morph/features/.gitkeep +25 -25
- package/content/.morph/hooks/README.md +158 -0
- package/content/.morph/hooks/pre-commit-all.sh +48 -48
- package/content/.morph/hooks/pre-commit-specs.sh +49 -49
- package/content/.morph/hooks/pre-commit-tests.sh +60 -60
- package/content/.morph/hooks/task-completed.js +73 -0
- package/content/.morph/hooks/teammate-idle.js +68 -0
- package/content/.morph/project.md +160 -160
- package/content/.morph/schemas/agent.schema.json +296 -296
- package/content/.morph/schemas/tasks.schema.json +220 -220
- package/content/.morph/specs/.gitkeep +20 -20
- package/content/.morph/standards/agent-teams-workflow.md +474 -0
- package/content/.morph/standards/coding.md +377 -377
- package/content/.morph/standards/fluent-ui-setup.md +590 -590
- package/content/.morph/standards/migration-guide.md +514 -514
- package/content/.morph/standards/passkeys-auth.md +423 -423
- package/content/.morph/standards/vector-search-rag.md +536 -536
- package/content/.morph/state.json +17 -17
- package/content/.morph/templates/CONTEXT-FEATURE.md +276 -0
- package/content/.morph/templates/CONTEXT.md +170 -0
- package/content/.morph/templates/FluentDesignTheme.cs +149 -149
- package/content/.morph/templates/MudTheme.cs +281 -281
- package/content/.morph/templates/clarify-questions.md +159 -159
- package/content/.morph/templates/component.razor +239 -239
- package/content/.morph/templates/contracts/Commands.cs +74 -74
- package/content/.morph/templates/contracts/Entities.cs +25 -25
- package/content/.morph/templates/contracts/Queries.cs +74 -74
- package/content/.morph/templates/contracts/README.md +74 -74
- package/content/.morph/templates/contracts.cs +217 -217
- package/content/.morph/templates/design-system.css +226 -226
- package/content/.morph/templates/infra/.dockerignore.example +89 -89
- package/content/.morph/templates/infra/Dockerfile.example +82 -82
- package/content/.morph/templates/infra/README.md +286 -286
- package/content/.morph/templates/infra/app-insights.bicep +63 -63
- package/content/.morph/templates/infra/app-service.bicep +164 -164
- package/content/.morph/templates/infra/azure-pipelines-deploy.yml +480 -0
- package/content/.morph/templates/infra/container-app-env.bicep +49 -49
- package/content/.morph/templates/infra/container-app.bicep +156 -156
- package/content/.morph/templates/infra/deploy-checklist.md +426 -426
- package/content/.morph/templates/infra/deploy.ps1 +229 -229
- package/content/.morph/templates/infra/deploy.sh +208 -208
- package/content/.morph/templates/infra/key-vault.bicep +91 -91
- package/content/.morph/templates/infra/main.bicep +189 -189
- package/content/.morph/templates/infra/parameters.dev.json +29 -29
- package/content/.morph/templates/infra/parameters.prod.json +29 -29
- package/content/.morph/templates/infra/parameters.staging.json +29 -29
- package/content/.morph/templates/infra/sql-database.bicep +103 -103
- package/content/.morph/templates/infra/storage.bicep +106 -106
- package/content/.morph/templates/integrations/asaas-client.cs +387 -387
- package/content/.morph/templates/integrations/asaas-webhook.cs +351 -351
- package/content/.morph/templates/integrations/azure-identity-config.cs +288 -288
- package/content/.morph/templates/integrations/clerk-config.cs +258 -258
- package/content/.morph/templates/job.cs +171 -171
- package/content/.morph/templates/migration.cs +83 -83
- package/content/.morph/templates/repository.cs +141 -141
- package/content/.morph/templates/saas/subscription.cs +347 -347
- package/content/.morph/templates/saas/tenant.cs +338 -338
- package/content/.morph/templates/service.cs +139 -139
- package/content/.morph/templates/sprint-status.yaml +68 -68
- package/content/.morph/templates/story.md +143 -143
- package/content/.morph/templates/test.cs +239 -239
- package/content/.morph/templates/ui-design-system.md +286 -286
- package/content/.morph/templates/ui-flows.md +336 -336
- package/content/.morph/templates/ui-mockups.md +133 -133
- package/content/.morph/test-infra/example.bicep +59 -59
- package/content/README.md +79 -79
- package/detectors/config-detector.js +223 -223
- package/detectors/conversation-analyzer.js +163 -163
- package/detectors/index.js +84 -84
- package/detectors/standards-generator.js +275 -275
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
- package/docs/api/scripts/collapse.js +38 -38
- package/docs/api/scripts/commonNav.js +28 -28
- package/docs/api/scripts/linenumber.js +25 -25
- package/docs/api/scripts/nav.js +12 -12
- package/docs/api/scripts/polyfill.js +3 -3
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
- package/docs/api/scripts/prettify/lang-css.js +2 -2
- package/docs/api/scripts/prettify/prettify.js +28 -28
- package/docs/api/scripts/search.js +98 -98
- package/docs/api/styles/jsdoc.css +776 -776
- package/docs/api/styles/prettify.css +80 -80
- package/docs/examples.md +328 -328
- package/docs/templates.md +418 -418
- package/package.json +1 -1
- package/scripts/postinstall.js +132 -132
- package/src/commands/advance-phase.js +83 -0
- package/src/commands/analyze-blazor-concurrency.js +193 -193
- package/src/commands/create-story.js +351 -351
- package/src/commands/deploy.js +780 -0
- package/src/commands/detect-agents.js +34 -6
- package/src/commands/detect.js +104 -104
- package/src/commands/generate-context.js +40 -0
- package/src/commands/generate.js +149 -149
- package/src/commands/lint-fluent.js +352 -352
- package/src/commands/rollback-phase.js +185 -185
- package/src/commands/session-summary.js +291 -291
- package/src/commands/shard-spec.js +224 -224
- package/src/commands/sprint-status.js +250 -250
- package/src/commands/state.js +333 -333
- package/src/commands/sync.js +167 -167
- package/src/commands/troubleshoot.js +222 -222
- package/src/commands/validate-blazor-state.js +210 -210
- package/src/commands/validate-blazor.js +156 -156
- package/src/commands/validate-css.js +84 -84
- package/src/commands/validate-phase.js +221 -221
- package/src/lib/blazor-concurrency-analyzer.js +288 -288
- package/src/lib/blazor-state-validator.js +291 -291
- package/src/lib/blazor-validator.js +374 -374
- package/src/lib/context-generator.js +513 -0
- package/src/lib/css-validator.js +352 -352
- package/src/lib/design-system-detector.js +187 -0
- package/src/lib/design-system-generator.js +298 -298
- package/src/lib/design-system-scaffolder.js +299 -0
- package/src/lib/hook-executor.js +256 -0
- package/src/lib/learning-system.js +520 -520
- package/src/lib/mockup-generator.js +366 -366
- package/src/lib/spec-validator.js +258 -0
- package/src/lib/standards-context-injector.js +287 -0
- package/src/lib/team-orchestrator.js +322 -0
- package/src/lib/troubleshoot-grep.js +194 -194
- package/src/lib/troubleshoot-index.js +144 -144
- package/src/lib/ui-detector.js +350 -350
- package/src/lib/validation-runner.js +65 -13
- package/src/lib/validators/architecture-validator.js +387 -387
- package/src/lib/validators/design-system-validator.js +231 -0
- package/src/lib/validators/package-validator.js +360 -360
- package/src/lib/validators/ui-contrast-validator.js +422 -422
- package/src/utils/file-copier.js +9 -1
- package/src/utils/logger.js +32 -32
- package/src/utils/version-checker.js +175 -175
- /package/content/.claude/skills/{checklists → level-0-meta}/code-review.md +0 -0
- /package/content/.claude/skills/{checklists → level-0-meta}/simulation-checklist.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/ai-agents}/ai-system-architect.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/architecture}/po-pm-advisor.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/architecture}/standards-architect.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/backend}/dotnet-senior.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/backend}/ef-modeler.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/backend}/hangfire-orchestrator.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/backend}/ms-agent-expert.md +0 -0
- /package/content/.claude/skills/{stacks/dotnet-blazor.md → level-2-domains/frontend/blazor-builder.md} +0 -0
- /package/content/.claude/skills/{stacks/dotnet-nextjs.md → level-2-domains/frontend/nextjs-expert.md} +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/frontend}/ui-ux-designer.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/infrastructure}/azure-architect.md +0 -0
- /package/content/.claude/skills/{infra → level-2-domains/infrastructure}/bicep-architect.md +0 -0
- /package/content/.claude/skills/{infra → level-2-domains/infrastructure}/container-specialist.md +0 -0
- /package/content/.claude/skills/{infra → level-2-domains/infrastructure}/devops-engineer.md +0 -0
- /package/content/.claude/skills/{integrations → level-2-domains/integrations}/asaas-financial.md +0 -0
- /package/content/.claude/skills/{integrations → level-2-domains/integrations}/azure-identity.md +0 -0
- /package/content/.claude/skills/{integrations → level-2-domains/integrations}/clerk-auth.md +0 -0
- /package/content/.claude/skills/{integrations → level-2-domains/integrations}/resend-email.md +0 -0
- /package/content/.claude/skills/{specialists → level-2-domains/quality}/code-analyzer.md +0 -0
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// HANGFIRE JOB TEMPLATE
|
|
3
|
-
// Generated by MORPH Framework
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
using Hangfire;
|
|
7
|
-
using Microsoft.Extensions.Logging;
|
|
8
|
-
|
|
9
|
-
namespace MyProject.Application.Features.{Feature}.Jobs;
|
|
10
|
-
|
|
11
|
-
/// <summary>
|
|
12
|
-
/// Background job for processing {Feature}.
|
|
13
|
-
/// Uses Hangfire for scheduling and retry handling.
|
|
14
|
-
/// </summary>
|
|
15
|
-
public class {Feature}ProcessorJob(
|
|
16
|
-
I{Feature}Service service,
|
|
17
|
-
I{Feature}AnalyzerAgent analyzer,
|
|
18
|
-
ILogger<{Feature}ProcessorJob> logger) : I{Feature}ProcessorJob
|
|
19
|
-
{
|
|
20
|
-
/// <summary>
|
|
21
|
-
/// Executes the {Feature} processing job.
|
|
22
|
-
/// </summary>
|
|
23
|
-
/// <param name="id">The {Feature} ID to process</param>
|
|
24
|
-
/// <param name="cancellationToken">Cancellation token</param>
|
|
25
|
-
[AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]
|
|
26
|
-
[Queue("default")]
|
|
27
|
-
[JobDisplayName("{Feature} Processing - ID: {0}")]
|
|
28
|
-
public async Task ExecuteAsync(int id, CancellationToken cancellationToken)
|
|
29
|
-
{
|
|
30
|
-
logger.LogInformation("Starting {Feature} processing for ID {Id}", id);
|
|
31
|
-
|
|
32
|
-
try
|
|
33
|
-
{
|
|
34
|
-
// Get the entity
|
|
35
|
-
var item = await service.GetByIdAsync(id, cancellationToken);
|
|
36
|
-
if (item is null)
|
|
37
|
-
{
|
|
38
|
-
logger.LogWarning("{Feature} with ID {Id} not found, skipping", id);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Check if already processed
|
|
43
|
-
if (item.Status == {Feature}Status.Completed)
|
|
44
|
-
{
|
|
45
|
-
logger.LogInformation("{Feature} {Id} already completed, skipping", id);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Perform analysis (if applicable)
|
|
50
|
-
var analysisData = new {Feature}Data(item.Name);
|
|
51
|
-
var analysis = await analyzer.AnalyzeAsync(analysisData, cancellationToken);
|
|
52
|
-
|
|
53
|
-
logger.LogInformation(
|
|
54
|
-
"{Feature} {Id} analyzed. Confidence: {Confidence:P0}",
|
|
55
|
-
id, analysis.ConfidenceScore);
|
|
56
|
-
|
|
57
|
-
// Update status
|
|
58
|
-
// Note: You might need to add a method to update with analysis results
|
|
59
|
-
// await service.CompleteWithAnalysisAsync(id, analysis, cancellationToken);
|
|
60
|
-
|
|
61
|
-
logger.LogInformation("Completed {Feature} processing for ID {Id}", id);
|
|
62
|
-
}
|
|
63
|
-
catch (Exception ex)
|
|
64
|
-
{
|
|
65
|
-
logger.LogError(ex, "Failed to process {Feature} {Id}", id);
|
|
66
|
-
throw; // Re-throw to trigger Hangfire retry
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ============================================================
|
|
72
|
-
// RECURRING JOB CONFIGURATION
|
|
73
|
-
// ============================================================
|
|
74
|
-
//
|
|
75
|
-
// For scheduled/recurring jobs, configure in Program.cs:
|
|
76
|
-
//
|
|
77
|
-
// // Run every hour
|
|
78
|
-
// RecurringJob.AddOrUpdate<I{Feature}ProcessorJob>(
|
|
79
|
-
// "{feature}-processor",
|
|
80
|
-
// job => job.ExecuteAsync(0, CancellationToken.None),
|
|
81
|
-
// Cron.Hourly);
|
|
82
|
-
//
|
|
83
|
-
// // Run daily at midnight
|
|
84
|
-
// RecurringJob.AddOrUpdate<I{Feature}ProcessorJob>(
|
|
85
|
-
// "{feature}-daily-processor",
|
|
86
|
-
// job => job.ExecuteAsync(0, CancellationToken.None),
|
|
87
|
-
// Cron.Daily);
|
|
88
|
-
//
|
|
89
|
-
// // Custom cron expression (every 15 minutes)
|
|
90
|
-
// RecurringJob.AddOrUpdate<I{Feature}ProcessorJob>(
|
|
91
|
-
// "{feature}-frequent-processor",
|
|
92
|
-
// job => job.ExecuteAsync(0, CancellationToken.None),
|
|
93
|
-
// "*/15 * * * *");
|
|
94
|
-
//
|
|
95
|
-
// ============================================================
|
|
96
|
-
|
|
97
|
-
// ============================================================
|
|
98
|
-
// BATCH JOB TEMPLATE
|
|
99
|
-
// ============================================================
|
|
100
|
-
|
|
101
|
-
/// <summary>
|
|
102
|
-
/// Batch job for processing multiple {Feature}s.
|
|
103
|
-
/// </summary>
|
|
104
|
-
public class {Feature}BatchProcessorJob(
|
|
105
|
-
I{Feature}Service service,
|
|
106
|
-
I{Feature}ProcessorJob itemProcessor,
|
|
107
|
-
ILogger<{Feature}BatchProcessorJob> logger)
|
|
108
|
-
{
|
|
109
|
-
/// <summary>
|
|
110
|
-
/// Processes all pending {Feature}s.
|
|
111
|
-
/// </summary>
|
|
112
|
-
[AutomaticRetry(Attempts = 1)]
|
|
113
|
-
[Queue("batch")]
|
|
114
|
-
[JobDisplayName("{Feature} Batch Processing")]
|
|
115
|
-
public async Task ProcessAllPendingAsync(CancellationToken cancellationToken)
|
|
116
|
-
{
|
|
117
|
-
logger.LogInformation("Starting batch processing for pending {Feature}s");
|
|
118
|
-
|
|
119
|
-
var items = await service.GetAllAsync(cancellationToken);
|
|
120
|
-
var pending = items.Where(x => x.Status == {Feature}Status.Pending).ToList();
|
|
121
|
-
|
|
122
|
-
logger.LogInformation("Found {Count} pending {Feature}s to process", pending.Count);
|
|
123
|
-
|
|
124
|
-
foreach (var item in pending)
|
|
125
|
-
{
|
|
126
|
-
// Enqueue individual processing jobs
|
|
127
|
-
BackgroundJob.Enqueue<I{Feature}ProcessorJob>(
|
|
128
|
-
job => job.ExecuteAsync(item.Id, CancellationToken.None));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
logger.LogInformation("Enqueued {Count} processing jobs", pending.Count);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ============================================================
|
|
136
|
-
// HANGFIRE CONFIGURATION
|
|
137
|
-
// ============================================================
|
|
138
|
-
//
|
|
139
|
-
// In Program.cs:
|
|
140
|
-
//
|
|
141
|
-
// // Add Hangfire services
|
|
142
|
-
// builder.Services.AddHangfire(config => config
|
|
143
|
-
// .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
|
144
|
-
// .UseSimpleAssemblyNameTypeSerializer()
|
|
145
|
-
// .UseRecommendedSerializerSettings()
|
|
146
|
-
// .UseSqlServerStorage(connectionString, new SqlServerStorageOptions
|
|
147
|
-
// {
|
|
148
|
-
// CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
|
149
|
-
// SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
|
150
|
-
// QueuePollInterval = TimeSpan.Zero,
|
|
151
|
-
// UseRecommendedIsolationLevel = true,
|
|
152
|
-
// DisableGlobalLocks = true
|
|
153
|
-
// }));
|
|
154
|
-
//
|
|
155
|
-
// builder.Services.AddHangfireServer(options =>
|
|
156
|
-
// {
|
|
157
|
-
// options.Queues = new[] { "default", "batch" };
|
|
158
|
-
// options.WorkerCount = Environment.ProcessorCount * 2;
|
|
159
|
-
// });
|
|
160
|
-
//
|
|
161
|
-
// // Register jobs
|
|
162
|
-
// builder.Services.AddScoped<I{Feature}ProcessorJob, {Feature}ProcessorJob>();
|
|
163
|
-
// builder.Services.AddScoped<{Feature}BatchProcessorJob>();
|
|
164
|
-
//
|
|
165
|
-
// // In app pipeline
|
|
166
|
-
// app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
|
167
|
-
// {
|
|
168
|
-
// Authorization = new[] { new HangfireAuthorizationFilter() }
|
|
169
|
-
// });
|
|
170
|
-
//
|
|
171
|
-
// ============================================================
|
|
1
|
+
// ============================================================
|
|
2
|
+
// HANGFIRE JOB TEMPLATE
|
|
3
|
+
// Generated by MORPH Framework
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
using Hangfire;
|
|
7
|
+
using Microsoft.Extensions.Logging;
|
|
8
|
+
|
|
9
|
+
namespace MyProject.Application.Features.{Feature}.Jobs;
|
|
10
|
+
|
|
11
|
+
/// <summary>
|
|
12
|
+
/// Background job for processing {Feature}.
|
|
13
|
+
/// Uses Hangfire for scheduling and retry handling.
|
|
14
|
+
/// </summary>
|
|
15
|
+
public class {Feature}ProcessorJob(
|
|
16
|
+
I{Feature}Service service,
|
|
17
|
+
I{Feature}AnalyzerAgent analyzer,
|
|
18
|
+
ILogger<{Feature}ProcessorJob> logger) : I{Feature}ProcessorJob
|
|
19
|
+
{
|
|
20
|
+
/// <summary>
|
|
21
|
+
/// Executes the {Feature} processing job.
|
|
22
|
+
/// </summary>
|
|
23
|
+
/// <param name="id">The {Feature} ID to process</param>
|
|
24
|
+
/// <param name="cancellationToken">Cancellation token</param>
|
|
25
|
+
[AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]
|
|
26
|
+
[Queue("default")]
|
|
27
|
+
[JobDisplayName("{Feature} Processing - ID: {0}")]
|
|
28
|
+
public async Task ExecuteAsync(int id, CancellationToken cancellationToken)
|
|
29
|
+
{
|
|
30
|
+
logger.LogInformation("Starting {Feature} processing for ID {Id}", id);
|
|
31
|
+
|
|
32
|
+
try
|
|
33
|
+
{
|
|
34
|
+
// Get the entity
|
|
35
|
+
var item = await service.GetByIdAsync(id, cancellationToken);
|
|
36
|
+
if (item is null)
|
|
37
|
+
{
|
|
38
|
+
logger.LogWarning("{Feature} with ID {Id} not found, skipping", id);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if already processed
|
|
43
|
+
if (item.Status == {Feature}Status.Completed)
|
|
44
|
+
{
|
|
45
|
+
logger.LogInformation("{Feature} {Id} already completed, skipping", id);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Perform analysis (if applicable)
|
|
50
|
+
var analysisData = new {Feature}Data(item.Name);
|
|
51
|
+
var analysis = await analyzer.AnalyzeAsync(analysisData, cancellationToken);
|
|
52
|
+
|
|
53
|
+
logger.LogInformation(
|
|
54
|
+
"{Feature} {Id} analyzed. Confidence: {Confidence:P0}",
|
|
55
|
+
id, analysis.ConfidenceScore);
|
|
56
|
+
|
|
57
|
+
// Update status
|
|
58
|
+
// Note: You might need to add a method to update with analysis results
|
|
59
|
+
// await service.CompleteWithAnalysisAsync(id, analysis, cancellationToken);
|
|
60
|
+
|
|
61
|
+
logger.LogInformation("Completed {Feature} processing for ID {Id}", id);
|
|
62
|
+
}
|
|
63
|
+
catch (Exception ex)
|
|
64
|
+
{
|
|
65
|
+
logger.LogError(ex, "Failed to process {Feature} {Id}", id);
|
|
66
|
+
throw; // Re-throw to trigger Hangfire retry
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================
|
|
72
|
+
// RECURRING JOB CONFIGURATION
|
|
73
|
+
// ============================================================
|
|
74
|
+
//
|
|
75
|
+
// For scheduled/recurring jobs, configure in Program.cs:
|
|
76
|
+
//
|
|
77
|
+
// // Run every hour
|
|
78
|
+
// RecurringJob.AddOrUpdate<I{Feature}ProcessorJob>(
|
|
79
|
+
// "{feature}-processor",
|
|
80
|
+
// job => job.ExecuteAsync(0, CancellationToken.None),
|
|
81
|
+
// Cron.Hourly);
|
|
82
|
+
//
|
|
83
|
+
// // Run daily at midnight
|
|
84
|
+
// RecurringJob.AddOrUpdate<I{Feature}ProcessorJob>(
|
|
85
|
+
// "{feature}-daily-processor",
|
|
86
|
+
// job => job.ExecuteAsync(0, CancellationToken.None),
|
|
87
|
+
// Cron.Daily);
|
|
88
|
+
//
|
|
89
|
+
// // Custom cron expression (every 15 minutes)
|
|
90
|
+
// RecurringJob.AddOrUpdate<I{Feature}ProcessorJob>(
|
|
91
|
+
// "{feature}-frequent-processor",
|
|
92
|
+
// job => job.ExecuteAsync(0, CancellationToken.None),
|
|
93
|
+
// "*/15 * * * *");
|
|
94
|
+
//
|
|
95
|
+
// ============================================================
|
|
96
|
+
|
|
97
|
+
// ============================================================
|
|
98
|
+
// BATCH JOB TEMPLATE
|
|
99
|
+
// ============================================================
|
|
100
|
+
|
|
101
|
+
/// <summary>
|
|
102
|
+
/// Batch job for processing multiple {Feature}s.
|
|
103
|
+
/// </summary>
|
|
104
|
+
public class {Feature}BatchProcessorJob(
|
|
105
|
+
I{Feature}Service service,
|
|
106
|
+
I{Feature}ProcessorJob itemProcessor,
|
|
107
|
+
ILogger<{Feature}BatchProcessorJob> logger)
|
|
108
|
+
{
|
|
109
|
+
/// <summary>
|
|
110
|
+
/// Processes all pending {Feature}s.
|
|
111
|
+
/// </summary>
|
|
112
|
+
[AutomaticRetry(Attempts = 1)]
|
|
113
|
+
[Queue("batch")]
|
|
114
|
+
[JobDisplayName("{Feature} Batch Processing")]
|
|
115
|
+
public async Task ProcessAllPendingAsync(CancellationToken cancellationToken)
|
|
116
|
+
{
|
|
117
|
+
logger.LogInformation("Starting batch processing for pending {Feature}s");
|
|
118
|
+
|
|
119
|
+
var items = await service.GetAllAsync(cancellationToken);
|
|
120
|
+
var pending = items.Where(x => x.Status == {Feature}Status.Pending).ToList();
|
|
121
|
+
|
|
122
|
+
logger.LogInformation("Found {Count} pending {Feature}s to process", pending.Count);
|
|
123
|
+
|
|
124
|
+
foreach (var item in pending)
|
|
125
|
+
{
|
|
126
|
+
// Enqueue individual processing jobs
|
|
127
|
+
BackgroundJob.Enqueue<I{Feature}ProcessorJob>(
|
|
128
|
+
job => job.ExecuteAsync(item.Id, CancellationToken.None));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
logger.LogInformation("Enqueued {Count} processing jobs", pending.Count);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================
|
|
136
|
+
// HANGFIRE CONFIGURATION
|
|
137
|
+
// ============================================================
|
|
138
|
+
//
|
|
139
|
+
// In Program.cs:
|
|
140
|
+
//
|
|
141
|
+
// // Add Hangfire services
|
|
142
|
+
// builder.Services.AddHangfire(config => config
|
|
143
|
+
// .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
|
144
|
+
// .UseSimpleAssemblyNameTypeSerializer()
|
|
145
|
+
// .UseRecommendedSerializerSettings()
|
|
146
|
+
// .UseSqlServerStorage(connectionString, new SqlServerStorageOptions
|
|
147
|
+
// {
|
|
148
|
+
// CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
|
149
|
+
// SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
|
150
|
+
// QueuePollInterval = TimeSpan.Zero,
|
|
151
|
+
// UseRecommendedIsolationLevel = true,
|
|
152
|
+
// DisableGlobalLocks = true
|
|
153
|
+
// }));
|
|
154
|
+
//
|
|
155
|
+
// builder.Services.AddHangfireServer(options =>
|
|
156
|
+
// {
|
|
157
|
+
// options.Queues = new[] { "default", "batch" };
|
|
158
|
+
// options.WorkerCount = Environment.ProcessorCount * 2;
|
|
159
|
+
// });
|
|
160
|
+
//
|
|
161
|
+
// // Register jobs
|
|
162
|
+
// builder.Services.AddScoped<I{Feature}ProcessorJob, {Feature}ProcessorJob>();
|
|
163
|
+
// builder.Services.AddScoped<{Feature}BatchProcessorJob>();
|
|
164
|
+
//
|
|
165
|
+
// // In app pipeline
|
|
166
|
+
// app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
|
167
|
+
// {
|
|
168
|
+
// Authorization = new[] { new HangfireAuthorizationFilter() }
|
|
169
|
+
// });
|
|
170
|
+
//
|
|
171
|
+
// ============================================================
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// EF CORE MIGRATION TEMPLATE
|
|
3
|
-
// Generated by MORPH Framework
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
using Microsoft.EntityFrameworkCore.Migrations;
|
|
7
|
-
|
|
8
|
-
#nullable disable
|
|
9
|
-
|
|
10
|
-
namespace MyProject.Infrastructure.Data.Migrations;
|
|
11
|
-
|
|
12
|
-
/// <inheritdoc />
|
|
13
|
-
public partial class Add{Feature} : Migration
|
|
14
|
-
{
|
|
15
|
-
/// <inheritdoc />
|
|
16
|
-
protected override void Up(MigrationBuilder migrationBuilder)
|
|
17
|
-
{
|
|
18
|
-
migrationBuilder.CreateTable(
|
|
19
|
-
name: "{Feature}s",
|
|
20
|
-
columns: table => new
|
|
21
|
-
{
|
|
22
|
-
Id = table.Column<int>(type: "int", nullable: false)
|
|
23
|
-
.Annotation("SqlServer:Identity", "1, 1"),
|
|
24
|
-
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
|
25
|
-
Status = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
|
26
|
-
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false,
|
|
27
|
-
defaultValueSql: "GETUTCDATE()"),
|
|
28
|
-
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true)
|
|
29
|
-
},
|
|
30
|
-
constraints: table =>
|
|
31
|
-
{
|
|
32
|
-
table.PrimaryKey("PK_{Feature}s", x => x.Id);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Indexes
|
|
36
|
-
migrationBuilder.CreateIndex(
|
|
37
|
-
name: "IX_{Feature}s_Name",
|
|
38
|
-
table: "{Feature}s",
|
|
39
|
-
column: "Name");
|
|
40
|
-
|
|
41
|
-
migrationBuilder.CreateIndex(
|
|
42
|
-
name: "IX_{Feature}s_Status",
|
|
43
|
-
table: "{Feature}s",
|
|
44
|
-
column: "Status");
|
|
45
|
-
|
|
46
|
-
migrationBuilder.CreateIndex(
|
|
47
|
-
name: "IX_{Feature}s_CreatedAt",
|
|
48
|
-
table: "{Feature}s",
|
|
49
|
-
column: "CreatedAt");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/// <inheritdoc />
|
|
53
|
-
protected override void Down(MigrationBuilder migrationBuilder)
|
|
54
|
-
{
|
|
55
|
-
migrationBuilder.DropTable(
|
|
56
|
-
name: "{Feature}s");
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ============================================================
|
|
61
|
-
// HOW TO CREATE A MIGRATION
|
|
62
|
-
// ============================================================
|
|
63
|
-
//
|
|
64
|
-
// 1. Add your entity to AppDbContext:
|
|
65
|
-
//
|
|
66
|
-
// public DbSet<{Feature}> {Feature}s => Set<{Feature}>();
|
|
67
|
-
//
|
|
68
|
-
// 2. Add your configuration:
|
|
69
|
-
//
|
|
70
|
-
// protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
71
|
-
// {
|
|
72
|
-
// modelBuilder.ApplyConfiguration(new {Feature}Configuration());
|
|
73
|
-
// }
|
|
74
|
-
//
|
|
75
|
-
// 3. Run migration command:
|
|
76
|
-
//
|
|
77
|
-
// dotnet ef migrations add Add{Feature} -p src/MyProject.Infrastructure -s src/MyProject.Web
|
|
78
|
-
//
|
|
79
|
-
// 4. Apply migration:
|
|
80
|
-
//
|
|
81
|
-
// dotnet ef database update -p src/MyProject.Infrastructure -s src/MyProject.Web
|
|
82
|
-
//
|
|
83
|
-
// ============================================================
|
|
1
|
+
// ============================================================
|
|
2
|
+
// EF CORE MIGRATION TEMPLATE
|
|
3
|
+
// Generated by MORPH Framework
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
using Microsoft.EntityFrameworkCore.Migrations;
|
|
7
|
+
|
|
8
|
+
#nullable disable
|
|
9
|
+
|
|
10
|
+
namespace MyProject.Infrastructure.Data.Migrations;
|
|
11
|
+
|
|
12
|
+
/// <inheritdoc />
|
|
13
|
+
public partial class Add{Feature} : Migration
|
|
14
|
+
{
|
|
15
|
+
/// <inheritdoc />
|
|
16
|
+
protected override void Up(MigrationBuilder migrationBuilder)
|
|
17
|
+
{
|
|
18
|
+
migrationBuilder.CreateTable(
|
|
19
|
+
name: "{Feature}s",
|
|
20
|
+
columns: table => new
|
|
21
|
+
{
|
|
22
|
+
Id = table.Column<int>(type: "int", nullable: false)
|
|
23
|
+
.Annotation("SqlServer:Identity", "1, 1"),
|
|
24
|
+
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
|
25
|
+
Status = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
|
26
|
+
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false,
|
|
27
|
+
defaultValueSql: "GETUTCDATE()"),
|
|
28
|
+
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true)
|
|
29
|
+
},
|
|
30
|
+
constraints: table =>
|
|
31
|
+
{
|
|
32
|
+
table.PrimaryKey("PK_{Feature}s", x => x.Id);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Indexes
|
|
36
|
+
migrationBuilder.CreateIndex(
|
|
37
|
+
name: "IX_{Feature}s_Name",
|
|
38
|
+
table: "{Feature}s",
|
|
39
|
+
column: "Name");
|
|
40
|
+
|
|
41
|
+
migrationBuilder.CreateIndex(
|
|
42
|
+
name: "IX_{Feature}s_Status",
|
|
43
|
+
table: "{Feature}s",
|
|
44
|
+
column: "Status");
|
|
45
|
+
|
|
46
|
+
migrationBuilder.CreateIndex(
|
|
47
|
+
name: "IX_{Feature}s_CreatedAt",
|
|
48
|
+
table: "{Feature}s",
|
|
49
|
+
column: "CreatedAt");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// <inheritdoc />
|
|
53
|
+
protected override void Down(MigrationBuilder migrationBuilder)
|
|
54
|
+
{
|
|
55
|
+
migrationBuilder.DropTable(
|
|
56
|
+
name: "{Feature}s");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================================
|
|
61
|
+
// HOW TO CREATE A MIGRATION
|
|
62
|
+
// ============================================================
|
|
63
|
+
//
|
|
64
|
+
// 1. Add your entity to AppDbContext:
|
|
65
|
+
//
|
|
66
|
+
// public DbSet<{Feature}> {Feature}s => Set<{Feature}>();
|
|
67
|
+
//
|
|
68
|
+
// 2. Add your configuration:
|
|
69
|
+
//
|
|
70
|
+
// protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
71
|
+
// {
|
|
72
|
+
// modelBuilder.ApplyConfiguration(new {Feature}Configuration());
|
|
73
|
+
// }
|
|
74
|
+
//
|
|
75
|
+
// 3. Run migration command:
|
|
76
|
+
//
|
|
77
|
+
// dotnet ef migrations add Add{Feature} -p src/MyProject.Infrastructure -s src/MyProject.Web
|
|
78
|
+
//
|
|
79
|
+
// 4. Apply migration:
|
|
80
|
+
//
|
|
81
|
+
// dotnet ef database update -p src/MyProject.Infrastructure -s src/MyProject.Web
|
|
82
|
+
//
|
|
83
|
+
// ============================================================
|