Schedules — Task Checklist
Status: Draft v1 · Date: 2026-07-18 Spec: spec.md · Plan: plan.md
Granular checklist agents and reviewers tick off as work lands. JIRA Epic + Story keys added once tickets exist (see "JIRA linkage" at the bottom).
Phase 1 (P1) — Rename + toggle + aggregation endpoint + Schedules list
Rename (i18n values only — keys untouched)
-
apps/web/messages/en.json—dashboard.activity.title(line ~1219)"Activity Log"→"Activity". -
apps/web/messages/en.json—metadata.pages.activity(line ~5053)"Activity Log"→"Activity". - Mirror both values into all 20 locales (
ar, bg, de, es, fr, he, hi, id, it, ja, ko, nl, pl, pt, ru, th, tr, uk, vi, zh), translated, "Log" qualifier dropped. - Verify NO change to:
ActivityLogentity,activity_logtable,ActivityLogService,/activity-logroutes, i18n keys,activity-log.csvfilename.
Contracts
-
packages/contracts/src/api/schedule/schedule.enum.ts—ScheduleSourceType(6 members). -
packages/contracts/src/api/schedule/schedule.dto.ts—ScheduleDto,ScheduleControls,SchedulesResponseDto. - Export from
packages/contracts/src/api/index.ts.
API
-
apps/api/src/schedules/schedules.module.ts. -
apps/api/src/schedules/schedules.controller.ts—GET /api/schedules(auth-guarded, scope-aware, query paramssourceType/entityKind/enabledOnly, OpenAPI annotated). -
apps/api/src/schedules/schedules.service.ts— six scoped source queries →ScheduleDto[]→ sort (null-last) →countsByType;controlsall-falsein P1. - Status normalization map (spec.md §4.5).
- Owning-entity
linkbuilder (spec.md §5.4). -
computeNextCronFire(expr, from)added topackages/agent/src/missions/cron-matcher.ts. -
describeCadence(sourceType, raw)(RRULE→text viarecurrence.ts, cron→text,WorkScheduleCadence→label, interval→"Every N min"). - Scope: consume
ScopeContext;userIdalways,organizationIdwhen Org scope active,organizationId IS NULL+ legacytenantId IS NULLin bare-Tenant scope;userId-only fallback where scope plumbing absent.
Web
-
activity-client.tsx—activeTabstate +activity-tablocalStorage persistence (mirrorviewModelines 60–68). - Segmented
Log | Schedulescontrol abovePageHeader. - Gate body:
logunchanged;schedulesrenders<SchedulesView />, hides Log chrome (status cards,ActivityFilters,ViewModeSwitch, Export, 5s poll). - Optional
?tab=schedulesURL reflection via existingrouter.replaceblock (lines 72–84). -
apps/web/src/app/actions/schedules.ts—getSchedules(filters)server action. -
apps/web/src/lib/api/schedules.ts—schedulesAPI.getAll(filters)viaserverFetch(+X-Scope-Slug). -
apps/web/src/components/activity-log/schedules/SchedulesView.tsx. -
ScheduleRow.tsx,ScheduleCard.tsx. -
SchedulesFilters.tsx(source-type chips + "Active only"). -
SchedulesEmptyState.tsx. - i18n additive keys
dashboard.activity.tabs.{log,schedules},dashboard.activity.schedules.*in all locales.
Tests
-
schedules.service.spec.ts— per-source mapping, scope filter, status normalization, sort order,countsByType. -
schedules.controller.spec.ts— auth guard + query-param filters. - Web unit — toggle persistence;
schedulestab hides Log chrome; empty state. - E2E — seed one of each source → switch to Schedules → six rows with cadence + next-run.
Phase 2 (P2) — Activity-gap emitters
Enum
- Append
MISSION_TICK = 'mission_tick'+IDEA_GENERATED = 'idea_generated'toActivityActionType(packages/agent/src/entities/activity-log.types.ts) — additive, no migration.
Emitters
-
schedule_executedon cron path —packages/agent/src/services/work-schedule-dispatcher.service.ts(dispatchDue) + NestJS fallbackapps/api/src/works/tasks/work-schedule-dispatcher-cron.service.ts.ingestEventId = schedule-exec:${workId}:${generationHistoryId}. -
agent_heartbeat_started/completed/failed—packages/agent/src/agents/agent-run.service.ts(guardtriggerKind === 'heartbeat'); optionalstartedat claim inagent-schedule-dispatcher.service.ts.ingestEventIdkeyed onagentRunId. -
mission_tick—packages/agent/src/missions/mission-tick.service.ts(tickDue, per matched Mission).ingestEventId = mission-tick:${missionId}:${tickTimestamp}. -
idea_generated—packages/agent/src/user-research/work-proposal.service.tsgenerate(covers Mission-tick + scheduled rerun).ingestEventIdkeyed on new WorkProposal id. - (optional)
task_recurrence_fired—packages/agent/src/tasks-domain/task-recurrence-dispatcher.service.tsspawn path. Include if small; else defer.
Scope + idempotency
- Every emitter stamps
userId,workId(where applicable),tenantId,organizationIdfrom the parent entity. Never NULL. - Deterministic
ingestEventIdon every emitter (dedupe onactivity_logpartial-unique(workId, ingestEventId)).
Tests
- Per-emitter unit — one row, right
actionType, scope set, stableingestEventId. - Idempotency — second dispatch with same
ingestEventIdwrites no duplicate. - Feed —
activity-feed.servicesurfaces cron-emittedschedule_executed.
Phase 3 (P3) — Controls polish
API / contracts
- Populate
ScheduleDto.controlsper source inschedules.service.ts. - Confirm NO new mutation endpoints — all controls map to existing routes (spec.md §4.6).
Web
-
ScheduleControlsMenu.tsx— trailing menu, only enabled controls. - Run-now → mapped endpoint (
agents :id/run-now,me/missions :id/run-now,works/:id/schedule/run,works/:id/sync-data); toast + optimistic state. - Pause/Resume →
agents :id/pause|resume,me/missions :id/pause|resume,PUT works/:id/schedulestatus; flip status pill. - Change-period dialog → RRULE builder (tasks) / cron/interval input; PATCH owning entity (
tasks :id/recurring,PATCH agents :id,PATCH me/missions :id,PUT works/:id/schedule,PUT works/:id/source-validation, data-sync interval). - Reuse existing server actions/BFF routes (e.g.
app/actions/dashboard/work-schedule.ts); add thin actions for agents/missions where none exist. - Re-fetch
GET /api/schedulesafter any mutation.
Tests
- Unit — controls menu renders only enabled controls per
ScheduleDto.controls. - E2E — pause a Work schedule from Schedules view → pill flips →
work_schedules.status = paused. - E2E — run-now an Agent heartbeat →
agent_heartbeat_startedappears in Log tab (needs P2).
Cross-cutting
- No existing UI strings changed other than the two renamed values (NN #20).
- No new tables/columns; the only schema-adjacent change is two additive enum members (no migration).
- Every emitter respects Tenant/Org scope stamping (Tier C rule).
- All PRs target
develop; nevermain/stagedirectly (NN #21). - Green CI (typecheck, lint, agent suite) on every PR.
JIRA linkage
JIRA Epic and per-phase Stories live in the EW project at https://evertech.atlassian.net (keys assigned at ticket creation):
- Epic: EW-TBD — Schedules (unified Activity → Schedules view + activity coverage)
- P1 — Rename + toggle + aggregation endpoint + Schedules list: EW-TBD
- P2 — Activity-gap emitters: EW-TBD
- P3 — Controls polish: EW-TBD