feat(FN-1452): make structured run records the source of truth for agent run state
- Update AGENTS.md with authoritative run lifecycle semantics documentation - Add API-level regression test for repeated manual run prevention - Update run lifecycle tests to match corrected behavior - Refactor AgentStore to use structured run records as the authoritative source for run state - Improve run status queries with better filtering and ordering
This commit is contained in:
@@ -44,10 +44,10 @@ Heartbeat values are validated and minimum-clamped.
|
||||
|
||||
The New Agent dialog in the dashboard provides quick-start presets for common agent roles. Each preset includes:
|
||||
|
||||
- **Name and icon** — Display identification
|
||||
- **Professional title** — Descriptive role title
|
||||
- **Soul** — Personality and operating principles defining how the agent thinks and communicates
|
||||
- **Instructions** — Actionable behavioral guidelines
|
||||
- **Name and icon** - Display identification
|
||||
- **Professional title** - Descriptive role title
|
||||
- **Soul** - Personality and operating principles defining how the agent thinks and communicates
|
||||
- **Instructions** - Actionable behavioral guidelines
|
||||
|
||||
### Preset Library Location
|
||||
|
||||
@@ -98,10 +98,10 @@ Each `soul.md` file is a Markdown document containing:
|
||||
|
||||
Soul content should be:
|
||||
|
||||
- **First-person** — Written from the agent's perspective ("I am...")
|
||||
- **Role-specific** — Defines the unique character of this role
|
||||
- **Actionable** — Describes concrete behaviors, not abstract qualities
|
||||
- **Paperclip-inspired** — Clear ownership, decision discipline, communication standards
|
||||
- **First-person** - Written from the agent's perspective ("I am...")
|
||||
- **Role-specific** - Defines the unique character of this role
|
||||
- **Actionable** - Describes concrete behaviors, not abstract qualities
|
||||
- **Paperclip-inspired** - Clear ownership, decision discipline, communication standards
|
||||
|
||||
### Adding or Modifying Presets
|
||||
|
||||
@@ -157,7 +157,7 @@ Behavior:
|
||||
|
||||
## Heartbeat Monitoring and Trigger Scheduling
|
||||
|
||||
Fusion’s `HeartbeatTriggerScheduler` supports three trigger types:
|
||||
Fusion's `HeartbeatTriggerScheduler` supports three trigger types:
|
||||
|
||||
- `timer` — periodic wake based on heartbeat interval
|
||||
- `assignment` — wake when task is assigned to agent
|
||||
@@ -165,6 +165,58 @@ Fusion’s `HeartbeatTriggerScheduler` supports three trigger types:
|
||||
|
||||
All triggers respect per-agent `maxConcurrentRuns` and produce structured wake context metadata.
|
||||
|
||||
## Heartbeat Run Lifecycle
|
||||
|
||||
Agent runs have a defined lifecycle managed by `AgentStore`:
|
||||
|
||||
### Run States
|
||||
|
||||
A heartbeat run can be in one of these states:
|
||||
|
||||
- `active` — Run is currently executing
|
||||
- `completed` — Run finished successfully (via `endHeartbeatRun(runId, "completed")`)
|
||||
- `terminated` — Run was stopped (via `endHeartbeatRun(runId, "terminated")`)
|
||||
- `failed` — Run encountered an error
|
||||
|
||||
### Run Lifecycle API
|
||||
|
||||
- `startHeartbeatRun(agentId)` — Creates a new run and persists it to structured storage
|
||||
- `endHeartbeatRun(runId, status)` — Ends a run with terminal status, updates persisted state
|
||||
- `getActiveHeartbeatRun(agentId)` — Returns the current active run (or null)
|
||||
- `getCompletedHeartbeatRuns(agentId)` — Returns all terminal runs (newest first)
|
||||
- `saveRun(run)` — Persists run to structured storage
|
||||
- `getRunDetail(agentId, runId)` — Gets a specific run by ID
|
||||
|
||||
### Active-Run Conflict Semantics
|
||||
|
||||
When an agent already has an active run, attempts to start a new run return **409 Conflict**:
|
||||
|
||||
```
|
||||
POST /api/agents/:id/runs → 409 { error: "Agent already has an active run", details: { runId } }
|
||||
```
|
||||
|
||||
After a run is completed (or terminated), a new run can be started successfully:
|
||||
|
||||
```
|
||||
POST /api/agents/:id/runs → 201 { id: "run-xxx", status: "active", ... }
|
||||
```
|
||||
|
||||
### Storage Architecture
|
||||
|
||||
Run records are stored in structured JSON files at `.fusion/agents/{agentId}-runs/{runId}.json`.
|
||||
|
||||
Heartbeat events are also appended to `.fusion/agents/{agentId}-heartbeats.jsonl` for legacy compatibility. The structured storage is the source of truth; heartbeat events provide a fallback for older run data.
|
||||
|
||||
### Stopping Runs
|
||||
|
||||
Use `POST /api/agents/:id/runs/stop` to terminate an active run:
|
||||
|
||||
```
|
||||
POST /api/agents/:id/runs/stop → 200 { ok: true, runId: "run-xxx" }
|
||||
```
|
||||
|
||||
If there's no active run, returns `{ ok: true, message: "No active run" }`.
|
||||
|
||||
## Related Docs
|
||||
|
||||
- [Workflow Steps](./workflow-steps.md)
|
||||
|
||||
Reference in New Issue
Block a user