Cortex — Task and Decision Effect Lifecycle
This spec adds lifecycle semantics, template governance, and completion validation to Tasks, and pack-level implementation guidance to Decision Effects. The Task (WorkflowTask) schema is documented in §task.fields below; the Effect schema is authoritative in component.cortex.decision-evidence#effect.fields.
The Three Layers
| Layer | When | Objects | What Happens | Who Acts |
|---|---|---|---|---|
| A. Deliberation | During investigation | Blocks, Events, Tasks | Evidence gathered, work routed, decisions drafted | Analysts, Agents, Reviewers |
| B. Decision Artifact | At sealing | Edition (manifest + narrative + attestation) | Immutable record created, hash chain locked | Author + Attester |
| C. Execution | After attestation | Decision Effects | Sealed decision dispatched to external systems | System, Human Process |
Tasks live in Layer A — they produce evidence. Effects live in Layer C — they execute sealed decisions. The Edition (Layer B) is the firewall: nothing crosses from A to C without attestation.
Task Object Model
A Task IS a governance-bound unit of work created from a TaskTemplate; attached to an Investigation (insight_id required); assigned to a role, not a user (workers pull, not push); logged in the append-only Event ledger; optionally producing new Blocks or Editions.
A Task IS NOT a workflow engine (no branching, no conditional routing), a state machine that orchestrates (transitions are validated, not orchestrated), auto-advancing logic (no system-driven changes except SLA timeout), or a replacement for Effects. Architecture guardrail: "Tasks are PUBLISHED → Workers PULL → No SEQUENCE."
Task Schema and Extensions
The WorkflowTask key fields: task_id, task_type, status, assigned_to (OR-logic: roles_any, groups_any, user_id), insight_id, edition_id, summary, priority, due_by, created_by, outcome. The DES core Task object also models schema_version, assignee (type/value), result (outcome, notes, produced_block_ids — required when completed), and created_at.
Task invariants (DES core): tasks are optional (DES requires no task for any lifecycle transition); tasks are non-deterministic (no prescribed ordering/routing/sequencing); task outputs are blocks; attestation requires a human, not a task (a attest-type task is a coordination request — the attestation itself is an event with actor.type = user); agent execution is opaque; task status is descriptive, not gating.
Cortex extensions stored via additionalProperties:
template_id— links the task to its governing template for completion validationsla_hours— from template, used to computedue_byorigin_event_id— traceability: what event caused this taskattached_block_ids— which evidence blocks are provided as context
Task Types and Assignee Types
Task types: review (review an edition before attestation), attest (formally attest a reviewed edition), gather_evidence (collect additional evidence), acknowledge (acknowledge a signal/finding), refresh (re-execute stale evidence blocks). Assignee types: user, role, group, agent.
Task Lifecycle
OPEN ──accept──> IN_PROGRESS ──complete──> COMPLETED
│ │
│ └──reject──> REJECTED
└──expire──> EXPIRED ──(generates Signal)
| Status | Meaning | Who Transitions |
|---|---|---|
open |
Published to queue, awaiting claim | System (on create) |
in_progress |
Worker has claimed and started | User (explicit accept) |
completed |
Work done, outcome recorded | User (after validation) |
rejected |
Worker declined or returned | User |
expired |
SLA deadline passed without completion | System (SLA engine) |
Transition rules: open → in_progress (any user whose roles match assigned_to.roles_any); open/in_progress → expired (system, when now > due_by); in_progress → completed (user, after completion_requirements validation); in_progress → rejected (user, with reason). expired is a system-generated terminal state; implementations without SLA engines skip it. Task state machines are descriptive, not prescriptive — DES does not gate investigation/edition lifecycle on task status; implementations MAY enforce task-gating via accountability constraints.
Event Ledger Integration
Every transition appends an event to the investigation's ledger (event taxonomy in component.cortex.decision-evidence#event):
| Event Type | Status Transition | Payload |
|---|---|---|
task_created |
→ open |
{task_id, task_type, template_id, assigned_to, attached_block_ids} |
task_accepted |
open → in_progress |
{task_id, accepted_by} |
task_completed |
in_progress → completed |
{task_id, outcome, completion_note} |
task_rejected |
in_progress → rejected |
{task_id, rejection_reason} |
task_expired |
→ expired |
{task_id, sla_hours, due_by} |
task_created and task_completed are DES Level 2 core events. task_accepted, task_rejected, task_expired are Level 3 (full audit trail). All task events are append-only via _append_event().
Task Outputs Are Blocks
When a task produces meaningful work, that work MUST be captured as Blocks. An agent assigned gather_evidence executes queries, produces Blocks, and completes with result.produced_block_ids referencing them. The blocks are the evidence; the task is the coordination record. Agent execution is opaque — DES records what blocks were produced, not agent reasoning.
Task Templates
Task templates are pack-level configuration owned by the Governance Manager. They define runtime validation rules, not workflow logic. They inherit defaults from _base/task_templates.yaml. Schema in component.cortex.pack-reference; template defaults in component.cortex.domain-loading.
templates:
- template_id: tmpl_task_risk_review_v1
name: Risk Review Request
task_type: review
routing_rules: { assignee_role: role-risk, priority_default: high, sla_hours: 48, escalation_after_hours: 72 }
required_context: { minimum_pinned_blocks: 1 }
completion_requirements: { must_create_edition: true, minimum_new_blocks: 1 }
Template Fields
| Field | Purpose |
|---|---|
template_id |
Unique id, referenced by accountability pack |
task_type |
Coarse task category (routing + Inbox UI) |
routing_rules.assignee_role |
Default role assignment |
routing_rules.priority_default |
Default priority |
routing_rules.sla_hours |
SLA deadline (hours from creation), used for due_by |
routing_rules.escalation_after_hours |
When to escalate if not completed |
required_context.insight_id |
Task must be linked to an investigation |
required_context.edition_id |
Task must reference an edition |
required_context.minimum_pinned_blocks |
Minimum evidence before task creation |
required_context.description_required |
Summary field mandatory |
completion_requirements.must_add_evidence |
Worker must pin ≥1 new block |
completion_requirements.must_create_edition |
Worker must create an edition |
completion_requirements.minimum_new_blocks |
Minimum new blocks during task |
completion_requirements.must_attest |
Edition must be attested to close task |
completion_requirements.minimum_attesters |
Minimum attestation count |
Resolution Chain
User creates task → Cortex loads Profile → Profile references accountability_id
→ Accountability pack has task_template_ids: { review: tmpl_task_risk_review_v1 }
→ Cortex loads template from ES/Redis cache → routing_rules + required_context + completion_requirements
→ Task enriched with template defaults → required_context checked before creation
→ completion_requirements checked before close
TheBank Task Templates
| Template | Task Type | Routes To | SLA | Completion Requires |
|---|---|---|---|---|
tmpl_task_risk_review_v1 |
review | RISK_MANAGER | 48h | 1 new block + edition |
tmpl_task_treasury_review_v1 |
review | TREASURY_ANALYST | 72h | 1 new block |
tmpl_task_committee_review_v1 |
committee_review | COMMITTEE | 120h | Attestation (2 attesters) |
Task Creation Triggers
Explicit User Action (Primary)
User clicks "Request Review" in Beacon → Beacon reads accountability pack → shows task type picker → Cortex create_task(). Accountability gate: the pack must contain task_template_ids for the requested task_type, else 403 TASK_TEMPLATE_NOT_AUTHORIZED. Routing gate: assignee_role must match the pack's review_routing.default_reviewers.roles_any, else 403 ROUTING_NOT_AUTHORIZED.
Decision Template Effect (Post-Attestation)
A decision template may declare an effect of type task_creation:
effects:
- type: task_creation
template_id: tmpl_task_committee_review_v1
condition: "decision_type == 'escalation'"
This bridges Tasks (Layer A) and Effects (Layer C): the attested edition triggers a new task via create_task() with actor.type: system and origin_event_id pointing to the attested event.
SLA Breach Escalation (System-Generated)
When a task expires, the system MAY generate a task_sla_breach Signal (source.type: system, system_id: cortex-sla-engine, severity: high, subject: {type: task, id: ...}). This signal enters the standard OODA flow, creating a self-healing governance loop: Signal → Task → Signal. System signals carry no source_model (see component.cortex.signal-lifecycle#sources.system).
Task Completion Validation
When complete_task() is called, Cortex MUST validate against the governing template before accepting completion.
Validation rules (against template.completion_requirements): must_add_evidence → count blocks added since origin_event_id ≥ 1, else COMPLETION_REQUIRES_EVIDENCE; minimum_new_blocks → blocks added ≥ N, else COMPLETION_REQUIRES_{N}_BLOCKS; must_create_edition → insight has ≥1 edition, else COMPLETION_REQUIRES_EDITION; must_attest → at least one edition attested, else COMPLETION_REQUIRES_ATTESTATION; minimum_attesters → attester count ≥ N, else COMPLETION_REQUIRES_{N}_ATTESTERS.
On failure → 409 with structured error {error: TASK_COMPLETION_REQUIREMENTS_NOT_MET, message, unmet_requirements[], template_id}. No silent success, no partial completion — the worker must fulfill all requirements or the task stays in_progress.
Task ↔ Accountability Pack Interaction
| Accountability Field | Task Behavior |
|---|---|
task_template_ids |
Which task types this role can create |
review_routing.default_reviewers |
Who tasks route to |
review_routing.escalation_path |
Escalation chain |
guardrails.minimum_evidence_count |
Minimum evidence before task creation |
attestation.separation_of_duties |
Attester ≠ author on editions produced by tasks |
Tasks cannot bypass governance: minimum-evidence requirements apply before task creation; editions created during a task still pass separation-of-duties at attestation; the worker's pack constrains which decision_type they can produce.
Task ↔ Evidence Interaction
Key rule: Tasks NEVER freeze evidence. Tasks may create blocks (block_created), pin blocks (block_pinned), draft editions (edition_created), and request review (review_requested). Tasks cannot freeze blocks (only edition submission/attestation freezes), attest editions (attestation is role-gated, not task-gated), or modify frozen blocks.
Task ↔ Investigation Lifecycle
Tasks do NOT change investigation lifecycle automatically. RM creates task / Risk Manager claims / adds blocks / creates edition — the investigation stays ACTIVE throughout. Only attesting an edition may close the investigation (if the last signal resolves). Tasks support decisions; they do not define them. Investigation status is driven by edition and signal lifecycle, not task lifecycle (see component.cortex.investigation-lifecycle#states.drivers).
Decision Effects
Decision Effects are Layer C — post-attestation execution. The Effect object model, types, lifecycle, timeout/escalation, and federated verification are authoritative in component.cortex.decision-evidence#effect. This section adds pack-level declaration syntax and implementation guidance.
Effect Declaration in Decision Templates
Effects are declared in decision template YAML (schema in component.cortex.pack-reference#decision-template):
templates:
- template_id: tmpl_credit_review_action_v1
effects:
- type: external_routing
target: credit_committee_queue
condition: "exposure > 5000000"
- type: notification
channel: email
recipients: { roles_any: [RISK_MANAGER] }
- type: task_creation
template_id: tmpl_task_committee_review_v1
condition: "decision_type == 'escalation'"
Pack type → DES effect_type Mapping
| DES Effect Type | Pack Equivalent | Description |
|---|---|---|
external_dispatch |
external_routing, webhook |
Route to external system |
notification |
notification |
Email, webhook, or message — no completion expected |
human_process |
task_creation |
Create a new task for human action |
Effects CREATE Tasks (One-Way)
Attested Edition
├── Effect: external_routing → Core Banking System
├── Effect: notification → Email to Risk Manager
└── Effect: task_creation → Committee Review Task → New Task (Layer A)
One-way rule: Effects can create tasks. Tasks cannot create effects. The Edition is the firewall.
SLA Engine (Future — Not Yet Implemented)
A periodic process (cron or background worker) queries tasks with status IN (open, in_progress) and due_by < now; for each overdue task sets status = expired, appends task_expired, and optionally generates a task_sla_breach Signal. The governance loop: Signal → Investigation → Task → (SLA breach) → Signal → new Investigation. Self-healing governance with no human intervention to escalate overdue work. Recommended deployment: background worker in the Cortex process.
Pack Author Guide
Adding a task template: define it in <domain>/task_templates.yaml; reference it in <domain>/accountability.yaml under insights.task_template_ids; cross-validate every task_template_ids value exists. Adding a decision effect: add an effects: array to the decision template in <domain>/decision_templates.yaml; each effect needs type, target (or template_id for task_creation), optional condition; effects are evaluated post-attestation and don't block edition creation. Task templates inherit _base defaults (required_context.insight_id: true, required_context.description_required: true, completion_requirements.must_add_evidence: true); domain templates override only what differs; explicit null removes a base value.
Implementation Status
What exists today: WorkflowTask model; create_task() (partially template-aware); complete_task() (NO completion validation); list_tasks_for_user(); update_task_status() (no transition validation); task template resolution (accountability → template_id → ES); template enrichment (routing + SLA applied; context/completion NOT validated); _TaskTemplateClient. NOT implemented: completion validation, SLA engine, Decision Effects (Effect event types are defined in the DES schema but not wired in Cortex).
What's missing (priority order):
| ID | Gap | Priority |
|---|---|---|
| T1 | Completion validation in complete_task() |
P1 — demo correctness |
| T2 | Task creation precondition validation (minimum_pinned_blocks, etc.) | P1 — demo correctness |
| T3 | Status transition validation (prevent open→completed skip) | P2 — correctness |
| T4 | task_accepted event type on status change |
P2 — audit trail |
| T5 | task_expired status + SLA engine |
P3 — production |
| T6 | task_sla_breach signal generation |
P3 — production |
| E1 | Effect object model (DecisionEffect model) |
P4 |
| E2 | Effect creation from decision template effects[] |
P4 |
| E3 | Effect lifecycle events (effect_created, etc.) |
P4 |
| E4 | Effect timeout → Signal generation | P4 |
Depends on: component.cortex.decision-evidence, component.cortex.pack-reference
Realizes: product.task
Required by: component.conduit.effect-engine