Workflow Pattern — cron + queue + Janus
iec-cron → POST /internal/cron/{trigger} Your service: fetch pending items, fan out jobs → POST {IEC_QUEUE_URL}/jobs (one per item) iec-queue → POST…
Ruleconfigure
About
iec-cron → POST /internal/cron/{trigger} Your service: fetch pending items, fan out jobs → POST {IEC_QUEUE_URL}/jobs (one per item) iec-queue → POST /internal/jobs/{worker} Your worker: process one item, Septor audit ```
Skill Content
This is the raw markdown that gets installed as a Claude Code rule.
# Workflow Pattern — cron + queue + Janus
## Core Pattern
```
iec-cron → POST /internal/cron/{trigger}
Your service: fetch pending items, fan out jobs
→ POST {IEC_QUEUE_URL}/jobs (one per item)
iec-queue → POST /internal/jobs/{worker}
Your worker: process one item, Septor audit
```
## catalog-info.yaml
```yaml
spec:
schedules:
- name: nightly-sync
cron: "0 2 * * *"
endpoint: /internal/cron/nightly-sync
timezone: America/Denver
timeoutMs: 60000
queues:
- name: sync-record
endpoint: /internal/jobs/sync-record
concurrency: 10
retries: 3
timeoutMs: 30000
internalDependencies:
- service: iec-queue # IEC_QUEUE_URL
- service: septor # SEPTOR_URL
```
## Trigger (fan-out) — return 200 FIRST
```typescript
app.post('/internal/cron/nightly-sync', async (req, res) => {
res.json({ success: true }) // respond immediately
try {
const records = await getPendingRecords()
await Promise.allSettled(records.map((r) =>
fetch(`${process.env.IEC_QUEUE_URL}/jobs`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ namespace: 'my-service', queue: 'sync-record', data: { recordId: r.id } }),
}).catch((err) => logger.warn({ err }, 'Enqueue failed'))
))
} catch (err) { logger.error({ err }, 'Fan-out failed') }
})
```
## Worker — check idempotency, return 500 to retry
```typescript
app.post('/internal/jobs/sync-record', async (req, res) => {
const { jobId, data, attempt } = req.body
if (await isAlreadySynced(data.recordId)) return res.json({ success: true, skipped: true })
try {
await syncRecord(data.recordId)
septor.emit('record.synced', { entityId: data.recordId, data: { jobId }, metadata: { who: 'worker' } })
.catch((err) => logger.error({ err }, 'Septor failed'))
res.json({ success: true })
} catch (err) {
logger.error({ jobId, attempt, err }, 'Sync failed')
res.status(500).json({ success: false }) // triggers retry
}
})
```
## Gas
| Operation | Gas |
|-----------|-----|
| iec-cron fires (prod) | 5 tokens |
| iec-queue job | 0 (platform) |
| Internal HTTP | 0 |
## Anti-Patterns
- Awaiting all fan-out before responding 200 → cron timeout
- `setTimeout` for scheduling → use iec-cron
- Rolling your own BullMQ → use iec-queue
- No idempotency check in worker → dupes on retry
Install
Copy the skill content and save it to:
~/.claude/rules/tawa-workflow-pattern.mdComing soon via CLI:
tawa chaac install tawa-workflow-patternDetails
- Format
- Rule
- Category
- configure
- Version
- 1.0.39767
- Tokens
- ~604
- Updated
- 2026-06-24
platformworkflowcronqueuejanusasync