Skip to main content

Implementation Reuse Map — Agents / Skills / Tasks

Status: Draft · 2026-05-25 Audience: Lead engineer + reviewers. Every new piece of Agents/Skills/Tasks scope is mapped to the existing platform asset it reuses. The goal: prove that the "minimal new code" claim holds, and surface exactly what's genuinely new.

Why this doc. Round 1-3 specs say "reuse the X facade" / "extend Y pattern" repeatedly. This doc makes it concrete: name the file, name the verb, name the test fixture. So an implementer can scan one table and find their handhold.


1. Entities — map to reuse pattern​

| New entity | Reuse pattern | Genuinely new | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ---- | ------- | | Agent | Same TypeORM shape as Mission (mission.entity.ts); polymorphic owner cols + @Index('uq_..._userId_slug') unique. | Five inline TEXT cols for DB-only tenant file storage. | | AgentRun | Same as WorkGenerationHistory (work-generation-history.entity.ts) — status enum, started/finished timestamps, error, summary, optional triggerRunId. | The triggerKind discriminator (heartbeat | task | chat). | | AgentRunLog | Verbatim copy of WorkAgentRunLog (work-agent-run-log.entity.ts) — only FK renamed (runId → agent_runs.id). | Nothing. | | AgentBudget | Polymorphic owner already lives on WorkBudget (work-budget.entity.ts) — extend BudgetOwnerType enum in _types.ts with AGENT + TASK. | Nothing schema-side; values only. | | AgentMembership | Same shape as the polymorphic (ownerType, ownerId) columns already used by WorkBudget. | The unique index on (agentId, targetType, targetId). | | Skill | Closest analog is Template (template.entity.ts). Owned by the "Ever Works Skills" plugin (ADR-012) — entity lives in packages/agent/src/entities/ but writes go through SkillsFacadeService → ISkillsProviderPlugin. | Frontmatter jsonb + content hash. | | SkillBinding | New shape — no exact precedent. Closest is WorkAdvancedPrompts (one-per-Work). Multiplicity differs (many-to-many). | The targetType polymorphic + injectIntoAgent / injectIntoGenerator booleans. | | Task | Closest analog is WorkProposal (work-proposal.entity.ts) — also has status enum, priority-like field, slug. Owned by the "Ever Works Task Tracker" plugin (ADR-013); third-party task-tracker plugins (Linear / Jira / GitHub) replace storage but use the same DTO shape. | The state machine + parentTaskId recursion + requireAllApprovers. | | TaskAssignee | Polymorphic shape mirrors WorkBudget's owner cols. (assigneeType, assigneeId). | Nothing. | | TaskBlocks | Self-referential join — same shape as task_relations. Cycle detection helper is the only new helper. | The cycle detector (runs recursive CTE). | | TaskRelations | Same as the existing template-customization back-pointer pattern. | The kind enum field. | | TaskChatMessage | Closest analog is ConversationMessage (conversation-message.entity.ts) — role, content, FK to parent. | The authorType polymorphic + mentions jsonb + attachments jsonb. | | TaskAttachment | Reuses work_knowledge_upload.id FK — no new upload pipeline. | Nothing. | | TaskWatcher | Same as the existing join-table pattern (e.g. work_member). | Nothing. | | TaskKbMention | Same. | Nothing. | | UserTaskCounter | Single-row-per-user atomic counter; no direct analog but trivial. | The atomic increment SQL. |

Genuinely new entity rows: 0. Every new entity is a variation of an existing shape.


2. Services — map to reuse​

New serviceReuseGenuinely new logic
AgentServiceCRUD + scope validation — analogous to MissionService.Scope-cascade validator (in validateScopeOwnership()).
AgentRunServiceRun lifecycle + status transitions — analogous to WorkGenerationService.The 3-trigger dispatch (heartbeat / task / chat) + prompt assembly orchestration.
AgentFileServiceBranches between GitFacadeService.commit() and inline DB write.The branching logic + secret-scan pre-write.
AgentToolServiceTool registration shape from agent-pipeline / claude-code plugins.The tool catalog + per-tool permission gate.
AgentBudgetServicePolymorphic delegation to existing BudgetService.summarizeForOwner({ownerType: AGENT}) — zero new aggregation.Nothing.
AgentScheduleDispatcherServiceVerbatim pattern of WorkScheduleDispatcherService — CAS-claim via markRunDispatched. (research)Different filter (Agents not Schedules); identical claim logic.
PromptAssemblerServiceReads from existing WorkAdvancedPrompts repo + Skill resolver + activity-log queries.The 11-segment assembly recipe (the one piece of code without a clear precedent).
SkillsFacadeService + "Ever Works Skills" plugin (per ADR-012)Plugin clones ever-works/skills repo (per ADR-014). Facade resolves enabled skills-provider plugins and dedupes by slug.ISkillsProviderPlugin contract; facade dedup logic.
SkillBindingServiceStandard CRUD; reuses repository pattern.The resolveActive() priority-sorted resolver.
SkillFileServiceSame branch as AgentFileService (Git for Mission/Work, DB-inline for Tenant).Nothing.
TasksFacadeService + "Ever Works Task Tracker" plugin (per ADR-013)First-party plugin uses platform DB tables via the existing TaskRepository. Facade routes all task operations through the active task-tracker plugin (one per tenant). Third-party plugins (Linear / Jira / GH Issues) proxy CRUD to their respective APIs and store side-table task_agent_assignments for Agent assignees when the external tracker doesn't support that.ITaskTrackerPlugin contract; facade routing + side-table Agent assignment when supportsAgentAssignees=false.
TaskTransitionServiceState-machine guarded transitions — closest precedent is WorkProposalService.acceptInternal() with its fromStatuses check.The full state machine.
TaskChatServicePolling endpoint + mention parsing — analogous to AI Conversation's ConversationController.Mention parsing + dispatch hooks.
TaskNotificationServiceThin wrapper that calls existing NotificationsService.create() (notification.entity.ts shape).Recipient computation logic.

Genuinely new service files: ~14. All thin (mostly orchestration over existing services); the heaviest is PromptAssemblerService.


3. Background jobs — map to reuse​

New Trigger.dev taskReuse
agent-heartbeat-dispatcherIdentical shape to mission-tick task (mission-tick.task.ts) — schedules.task({cron: '* * * * *'}) + service call.
agent-heartbeatIdentical shape to work-generation task — task() with maxDuration: 30 * 60, bootstraps Nest, calls service via remote proxy.
agent-task-executeSame as above with maxDuration: 60 * 60.
agent-chat-replySame with maxDuration: 5 * 60.

All four reuse the existing internal RPC channel (ADR-002): x-trigger-secret header + createRemoteProxy(). Genuinely new transport: 0.


4. Controllers / endpoints — map to reuse​

Every new controller decorator pattern lives in existing code:

PatternExisting example
@Controller('api/agents') (unversioned)@Controller('api/me/missions') in missions.controller.ts
Cross-user 404 (not 403) on readAI Conversation controller
@CurrentUser() decoratorEvery authenticated controller
@Throttle({ default: { limit: N, ttl: M } })quickCreateWork in works.controller.ts
Offset pagination DTO with {data, meta}Every list endpoint
ParseUUIDPipe for path paramsEvery detail endpoint
class-validator DTOs in dto/ subfolderEvery module
SSE (only for streaming chat) text/event-streamopenai-compat.controller.ts
Activity emission via event listenerWorkCreatedEvent → ActivityLogListener.onWorkCreated

Genuinely new controller shape: 0. Even the SSE streaming for Agent chat reply (if we land N10-c) reuses the openai-compat pattern.


5. Web (frontend) — map to reuse​

New UI surfaceReuse
/agents list (Cards/Table/Kanban)View-mode switcher + localStorage persistence: works-client.tsx. Kanban shape: WorksKanbanView.tsx.
/agents/[id] tab stripWorkTabs.tsx shape; new component AgentTabs.tsx copies it.
Agent detail Dashboard (live, charts)Stat cards: apps/web/src/components/dashboard/*Tile.tsx. Bar chart: same lib already in use.
Activity tab per AgentActivityFeedClient.tsx extended with agentId filter.
Instructions tab (5-file editor)KbEditor.tsx (Tiptap) wrapped in a 5-pill switcher. Zero new editor stack.
Budgets tabShape identical to existing Work Budgets / Mission Budget cards (per BudgetSummaryCard.tsx).
Create-Agent dialog (2-step)Form patterns from WorkManualForm.tsx: useState + useTransition + server actions.
/skills list (3 sections)PluginsList.tsx shape: search + filter + cards.
/skills/[id] Body + Bindings tabsNew mini-tabs; Tiptap editor for body.
/tasks listView-mode switcher from /works. New TasksKanbanView.tsx adapts WorksKanbanView.tsx columns.
Task detail pageStacked layout; right-rail metadata + main scroll. Chat: lightweight Tiptap.
Sidebar itemsInsert into DashboardSidebar.tsx config array.
New dashboard tilesNew AgentsCountTile.tsx, TasksInProgressTile.tsx — clone existing tile shape.

Genuinely new components: ~12, all thin. No new state lib, no new design system, no new routing.


6. Migration ordering​

Run in this order (all forward-only, additive):

  1. CreateAgentsTables.ts — creates agents, agent_runs, agent_run_logs, agent_budgets, agent_memberships.
  2. AddAgentIdToPluginUsageEvents.ts — additive column.
  3. ExtendBudgetOwnerTypeEnum (no DB change; enum string union in TS code).
  4. CreateSkillsTables.ts — creates skills, skill_bindings.
  5. CreateTasksTables.ts — creates tasks, task_assignees, task_reviewers, task_approvers, task_blocks, task_relations, task_chat_messages, task_attachments.
  6. AddTaskAuxTables.ts — user_task_counter, task_watchers, task_kb_mentions.
  7. AddTaskIdToPluginUsageEvents.ts — additive column.

Each migration:

  • runs in a single transaction;
  • ships with a down() that drops the new tables/columns (safe rollback within minutes of deploy);
  • emits a startup log line "Applied migration NNN".

Rollback story: revert the API to the previous tagged build; the migrations' down() drops the new tables. No data loss for non-feature flows.


7. N+1 risk catalog​

The dispatcher and list endpoints are the hot paths. Each one's risk + mitigation:

RiskMitigation
GET /agents with each card showing bound-skills-countAggregate query with LEFT JOIN skill_bindings GROUP BY agentId. Single round trip.
GET /agents with each card showing lastRunStatusStored denormalized on agents.lastRunStatus (already in entity).
GET /tasks Kanban with 500 items × assignees × labelsLoad tasks; then one batch query per join (assigneeIds IN (...)). Three queries total.
Heartbeat dispatcher with 10k active Agents per tickSingle SELECT with index (status, nextHeartbeatAt); CAS-claim atomic SQL UPDATE per agent.
AgentRunService.execute() loading files + skills + activityAll loads in Promise.all() (see agent-prompt-assembly.md §8).
GET /tasks/:id/chat with mentions referencing other tablesMentions stored as embedded JSON; UI dereferences via batched lookups on a separate ?expand=mentions query.
Skill.resolveActive() for Agent with bindings at 5 scope tiersOne JOIN query covering all scope tiers; results de-duped in app by slug.

Lazy loading (TypeORM lazy: true) is avoided for hot paths — explicit JOINs only.


8. Caching strategy​

CacheBacked byInvalidation
Skill catalog (in-memory)SkillCatalogService boot-time load + MapAPI restart; no runtime invalidation needed
Agent file content (5 min LRU)Existing cache_entries table (TypeORM-backed)Explicit cache.delete(agentId) on PUT
Resolved active skills per Agentcache_entries 60s TTLExplicit delete on skill_binding mutation
Activity feedClient polling, no server cachen/a
Agent run dashboard chartsServer-side aggregation, 30s response cacheTTL only
Mission/Idea/Work tab countsSame as existing tab-count cachingn/a

We use the existing cache_entries distributed-cache table (cache.entity.ts) so multi-pod deployments share state. No Redis added.


9. Test infrastructure reuse​

Test typeExisting patternReuse
Entity unitpackages/agent/src/entities/__tests__/work.entity.spec.tsSame Jest setup; same factory pattern.
Repository unitpackages/agent/src/database/repositories/__tests__/*.spec.tsMock DataSource pattern (existing).
Service unitpackages/agent/src/services/__tests__/*.spec.tsNestJS @nestjs/testing module setup.
API e2e (NestJS)apps/api/test/*.e2e-spec.tsExisting test app bootstrap with in-memory SQLite or test Postgres.
Dispatcher CAS raceExisting test for WorkScheduleDispatcherService.markRunDispatchedSpin up 2 parallel calls; assert exactly one wins.
Trigger.dev mockExisting pattern in packages/tasks/src/__tests__/Mock runs.trigger() returning a stub run-id.
AI provider mockExisting pattern: mock AiFacadeService.createChatCompletion to return canned responsesSame.
Web Playwright e2eapps/web/playwright.config.ts + apps/web/tests/Same.
Plugin contract testpackages/plugin/src/contracts/__tests__/Add a contract test for the reserved IExternalTaskTrackerPlugin interface.

E2E cost concern: tests that call real AI providers cost real $. Existing posture: AI calls always mocked in CI via the test-provider mock. We inherit this. Local dev that wants to hit a real provider uses .env.test.local with their own API key.


10. Local dev experience​

ConcernSolution
Bootstrap an Agent without GitHub repoTenant-scope Agents work DB-only; no Git needed.
Force a heartbeat without waiting for cronPOST /agents/:id/run-now available in UI.
Force a Trigger.dev cron in pnpm dev:triggerExisting dev server runs cron at higher frequency for testability.
Mock AI provider for offline devOPENAI_MOCK=true env var (existing); responds with canned data.
Inspect prompt assembly outputNew POST /agents/:id/dry-run (see QUESTIONS N4) — returns the assembled prompt without calling AI.
Reset Agent stateDELETE /agents/:id cascades; or pnpm db:reset --feature=agents (script to add).

11. What's GENUINELY new (the minimal addition set)​

This is the only code that has no clear precedent in the existing platform and therefore needs the most design care:

  1. PromptAssemblerService — the 11-segment system-message assembly. ~250-400 lines of code. Risk: prompt-engineering correctness; mitigated by progressive disclosure + token budgeting + extensive unit tests.
  2. AgentToolService.resolveAllowedTools — the per-Agent tool catalog with permission gates. ~150 lines. Risk: privilege escalation; mitigated by security model §7.
  3. SkillBindingRepository.resolveActive — priority-sorted resolver across 5 scope tiers. ~80 lines. Risk: incorrect resolution order; mitigated by exhaustive unit tests.
  4. TaskTransitionService — state machine with blocker/approver guards. ~120 lines.
  5. Cycle detection (parent + blockers) — recursive CTE or iterative walker. ~60 lines.
  6. Mention parser — extracts @<slug> / [[kb-slug]] from rich-text body. ~80 lines.
  7. Two custom Trigger.dev tasks (agent-heartbeat, agent-task-execute) — bodies are thin orchestrators.
  8. Web UI: ~12 new React components per the table in §5. Each is a thin variant of an existing component.

Conservative estimate: ~3000 lines of new TS code total, of which ~600 is genuinely novel. Spread across packages/agent/, apps/api/, apps/web/, packages/tasks/. Add ~1500 lines of test code.

For a comparable feature size, this slot is ~⅓ the size of the AI Conversation feature when it landed.


12. Deployment / rollout impact​

ConcernImpact
Trigger.dev run volumeNew dispatcher fires every minute. If 100 active Agents and dispatcher batches in 1 task → +~525k runs/month. Cost the Trigger.dev tier before launch.
DB row volumeagent_runs at 1-min heartbeat × 100 Agents = 144k rows/day. With 30-day rolling history, ~4M rows. Index on (agentId, startedAt) handles.
API memorySkill catalog cache: ~10 MB (1000 entries × ~10 KB). Agent file cache: ~100 KB per active Agent × cap = bounded.
API CPUHeartbeat AI calls happen on the worker, not the API. API CPU impact: negligible.
Worker memoryEach agent-heartbeat run bootstraps Nest (~100 MB) + holds prompt context (~50 KB). Trigger.dev's per-run isolation handles this.
Plugin usage events tableAlready at heavy write volume; +1 column (agentId) + 1 column (taskId). Index (agentId, occurredAt) adds ~10% storage.
Network egress to AI providersWhatever the user authorizes; bounded by per-Agent budgets.

No new infrastructure deployed (Redis, k8s, queue system, etc.).



14. PR / shipping plan​

Estimated ~12-18 PRs for full feature delivery:

PRScopeRisk
1agents table + migration + entity + repository unit testsLow
2agent_runs / agent_run_logs / agent_budgets + migrationLow
3AgentService + AgentsController (read-only)Low
4AgentFileService + PUT/GET files endpointsMedium
5/agents web list + Instructions tab + Create dialogMedium
6agent-heartbeat-dispatcher Trigger.dev task + CAS-claimMedium
7agent-heartbeat task + AgentRunService.execute() + PromptAssemblerServiceHigh
8Skill catalog + skills + skill_bindings + read-only APILow
9Skill mutations + /skills page + Bindings tabLow
10Skill injection into AI calls + getSkillBody toolMedium
11tasks family of tables + migrations + repositoriesLow
12TaskService + TasksController + /tasks list + CardsMedium
13Task detail page + chat backendMedium
14Task Kanban + per-target tabs (incl. Mission tab strip)Medium
15agent-task-execute + agent-chat-reply Trigger.dev tasksMedium
16Tools surface (createTask, etc.) wired to Agent runsHigh
17Dashboard tiles + Recent Tasks block + notifications wiringLow
18Default-on rollout + docs site updatesLow

Critical-path PRs: 7 (AgentRunService + PromptAssembler) and 16 (tools wired to runs). Everything else can land in parallel after #3.


15. Hand-off checklist​

Before any implementation PR opens:

  • All open QUESTIONS answered (or punted with a documented [NEEDS CLARIFICATION] mark).
  • Migrations reviewed for synchronize: true accidents.
  • Constitution gates ticked in every feature spec.
  • Test plan reviewed (unit + e2e + Playwright).
  • Operator approved the default budget caps.
  • Trigger.dev project tier confirmed (cost of +500k runs/month).
  • Sentry tags added to telemetry config.

16. References​