Skip to content

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

ModeOne tick createsSet
standaloneone new record every interval_daystarget_entity, interval_days, template_json, dedupe_target_field
source-drivenone record per due row of a source entityalso 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 in last_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 idequipment_id and asset_tagequipment_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

SymptomCauseFix
a new row every single tickinterval_days missing or 0set it
duplicate rowsdedupe_target_field not set, or not a stable columnin source-driven mode use the source-id column
${date} appears literally in a non-string fieldsubstitution only works in string template valueskeep templated values as strings
job not registeredno register rule shippedship it