Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions docs/dev/scheduler-phase1-payload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Scheduler Phase 1 Payload Notes

Phase 1 keeps HumanCompiler runtime behavior unchanged. This document records
the payload that HumanCompiler can already send today, and the equivalent shape
expected by the external `Scheduler` repository's experimental Human daily
fixtures.

## Current Daily Schedule Request

`POST /api/schedule/daily` keeps the existing request shape:

```json
{
"date": "2026-05-20",
"task_source": {
"type": "all_tasks"
},
"time_slots": [
{
"start": "09:00",
"end": "12:00",
"kind": "focused_work",
"capacity_hours": 2.5
},
{
"start": "13:00",
"end": "16:00",
"kind": "study",
"capacity_hours": 2.5
},
{
"start": "16:30",
"end": "18:00",
"kind": "light_work"
}
],
"preferences": {},
"fixed_assignments": [
{
"task_id": "task-standup-notes",
"slot_index": 1,
"duration_hours": 0.5
}
]
}
```

The API adapter expands this request with database task data before calling the
current optimizer. Regular tasks and quick tasks are both converted to
`SchedulerTask`; quick task IDs are prefixed with `quick_`.

## Equivalent External Scheduler Fixture

The same scheduling intent can be represented in the external Scheduler repo as
an editable YAML fixture:

```yaml
date: "2026-05-20"
metadata:
name: humancompiler_payload_sample

time_slots:
- index: 0
start: "09:00"
end: "12:00"
work_kind: focused_work
capacity_minutes: 150
- index: 1
start: "13:00"
end: "16:00"
work_kind: study
capacity_minutes: 150
- index: 2
start: "16:30"
end: "18:00"
work_kind: light_work

fixed_assignments:
- task_id: task-standup-notes
slot_index: 1
duration_minutes: 30

tasks:
- id: task-proposal
title: Draft product proposal
remaining_minutes: 90
priority: 1
work_kind: focused_work
due_at: "2026-05-22T17:00:00"
project_id: project-alpha
goal_id: goal-launch
source: task
- id: quick-inbox
title: Clear inbox and small replies
remaining_minutes: 30
priority: 4
work_kind: light_work
source: quick_task

task_dependencies:
task-proposal:
- task-research
```

## Input Mapping

| HumanCompiler field | External Scheduler fixture field | Notes |
| --- | --- | --- |
| `DailyScheduleRequest.date` | `date` | Same calendar day. |
| `time_slots[].start` / `end` | `time_slots[].start` / `end` | Same `HH:MM` strings. |
| `time_slots[].kind` | `time_slots[].work_kind` | Values map directly: `light_work`, `focused_work`, `study`. |
| `time_slots[].capacity_hours` | `time_slots[].capacity_minutes` | Convert hours to minutes. If omitted, slot duration is used. |
| `time_slots[].assigned_project_id` | `time_slots[].assigned_project_id` | Project-specific slot constraint. |
| `fixed_assignments[].task_id` | `fixed_assignments[].task_id` | Same task identifier after quick task prefixing. |
| `fixed_assignments[].slot_index` | `fixed_assignments[].slot_index` | Same zero-based slot index. |
| `fixed_assignments[].duration_hours` | `fixed_assignments[].duration_minutes` | Convert hours to minutes. |
| regular `Task.id` | `tasks[].id` | UUID string from HumanCompiler. |
| quick task ID | `tasks[].id` | Prefix with `quick_`, matching current API adapter behavior. |
| task title | `tasks[].title` | Safe to anonymize for exported fixtures later. |
| estimate minus actual logged hours | `tasks[].remaining_minutes` | Current API already computes remaining hours for regular tasks. |
| `Task.priority` / `QuickTask.priority` | `tasks[].priority` | Quick task priority is already passed. Regular task priority is still a known gap in the current adapter. |
| `work_type` | `tasks[].work_kind` | Falls back to title-based kind inference only when regular tasks lack `work_type`. |
| `due_date` | `tasks[].due_at` | Use ISO datetime when present. |
| task goal | `tasks[].goal_id` | Regular tasks only. |
| goal project | `tasks[].project_id` | Derived from the task goal for regular tasks. |
| task dependencies | `task_dependencies` | Map dependent task ID to prerequisite task IDs. |

## Output Mapping

| Current API response field | External Scheduler result field | Notes |
| --- | --- | --- |
| `success` | report status | HumanCompiler should continue returning the current boolean when integrated later. |
| `date` | fixture `date` | Same requested date. |
| `assignments[].task_id` | `plan.blocks[].task_id` | Same task ID. |
| `assignments[].slot_index` | `plan.blocks[].slot_index` | Same slot index. |
| `assignments[].start_time` | `plan.blocks[].start` | External timeline blocks have concrete sequential start times. |
| `assignments[].duration_hours` | `plan.blocks[].duration_minutes` | Convert minutes to hours. |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correct duration conversion direction in output mapping

The output mapping row for assignments[].duration_hours says to “Convert minutes to hours,” but this row maps from HumanCompiler’s hours field to Scheduler’s duration_minutes, so the conversion must be the opposite. If an implementer follows this note literally during integration, block durations will be scaled incorrectly (typically by 60x), which would corrupt exported/translated schedule results.

Useful? React with 👍 / 👎.

| `assignments[].is_fixed` | `plan.blocks[].is_fixed` | Same meaning. |
| `unscheduled_tasks[]` | `unscheduled_tasks[]` | External result adds `reason`; current API does not expose it yet. |
| `optimization_status` | `plan.status` | Exact status values do not need to match during Phase 1. |
| `objective_value` | `score_breakdown[]` | External review output is richer than the current single objective value. |

## Current Gaps For Later Phases

- Regular task priority is still hardcoded to `3` before the daily optimizer.
Quick tasks already pass their real priority.
- The current daily API does not expose unscheduled reasons, score breakdowns,
or constraint violations.
- Current assignments are slot-shaped. Multiple tasks in one slot can still
share the same `start_time`.
- Phase 1 does not import the external package into HumanCompiler and does not
change persisted schedule data.
Loading