Appearance
Add a cadence-generator job
What you're doing
"Create new records on a schedule." A quarterly equipment audit, a monthly review, a follow-up task N days after some event. No Java. One entity_cadence_config row, run by the generic engine-entity.cadence-generator job, shipped as seed data.
Two modes
| Mode | One tick creates | Set |
|---|---|---|
| standalone | one new record every interval_days | target_entity, interval_days, template_json, dedupe_target_field |
| source-driven | one record per due row of a source entity | also source_entity, source_date_field, due_within_days, field_map_json |
The complete example — standalone quarterly audit
spk-assembly/metadata/seed-data/office-equipment-cadence-configs.json (standalone example, real file):
json
{
"entity": "entity_cadence_config",
"keyFields": ["cadence_code"],
"source": "office-equipment",
"rows": [
{
"cadence_code": "equipment-audit-schedule",
"target_entity": "oeq_audit",
"interval_days": 90,
"dedupe_target_field": "cadence_marker",
"template_json": {
"audit_number": "AUDIT-${date}-${ts}",
"audit_name": "Scheduled Equipment Audit ${date}",
"status": "DRAFT"
},
"seeded_by": "office-equipment",
"active": true
}
]
}Line by line
cadence_code— unique id; the natural key.target_entity— the entity to create rows in.interval_days— a new row is created only if the last one is older than this many days (tracked inlast_generated_at).dedupe_target_field— a column on the target that the job stamps to recognise its own creations and avoid double-generating in one window.template_json— the field map for the new row. String values support substitutions:${date}(today),${ts}(a timestamp),${uuid}, and (in source-driven mode)${sourceId}.
Source-driven variant
Add:
json
"source_entity": "oeq_equipment",
"source_date_field": "warranty_expiry_date",
"due_within_days": 30,
"field_map_json": { "equipment_id": "id", "equipment_tag": "asset_tag" },
"dedupe_target_field": "equipment_id"→ for every oeq_equipment whose warranty_expiry_date is within 30 days and which doesn't already have a target row (deduped on equipment_id), create one, copying id→equipment_id and asset_tag→equipment_tag.
The register rule
spk-assembly/metadata/rules/ensure_cadence_generator_job_registered.json:
json
{
"entityType": "entity_cadence_config",
"name": "ensure_cadence_generator_job_registered",
"triggerEvent": "AFTER_CREATE",
"conditions": null,
"actions": "[{\"type\": \"EXECUTE_SERVICE\", \"service\": \"ensureEntityCadenceGeneratorJobRegistered\"}]",
"priority": 10,
"active": true
}Ground yourself first
bash
erp schema pull entity-cadence-config
erp examples patterns --kind jobs # "scheduled-record-generation-cadence"How to verify it worked
bash
erp api get "/api/v1/jobs/engine-entity.cadence-generator"
# → { "status": "ENABLED", ... }
erp api post "/api/v1/jobs/engine-entity.cadence-generator/execute" --body "{}"
erp api get "/api/v1/entities/oeq_audit/records/query?size=5"json
{ "rows": [
{ "audit_number": "AUDIT-2026-09-10-1757492940", "audit_name": "Scheduled Equipment Audit 2026-09-10",
"status": "DRAFT", "cadence_marker": "equipment-audit-schedule" }
], "total": 1 }Run it again immediately — no new row (interval not elapsed). That's correct.
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
| a new row every single tick | interval_days missing or 0 | set it |
| duplicate rows | dedupe_target_field not set, or not a stable column | in source-driven mode use the source-id column |
${date} appears literally in a non-string field | substitution only works in string template values | keep templated values as strings |
| job not registered | no register rule shipped | ship it |