05. 制約とスケジューリング

スケジューラに「いつ置くか」を指示する手段を一通り試す。

生成されたレポート

ファイル 内容
tasks.html 制約による配置の違い (responsible 列つき)

実行結果

レポートは開始日の順に並ぶ。主なものを抜き出すと次のとおり。

タスク 開始 終了 効いている制約
① ASAP 2026-08-03 08-07 既定 (前詰め)
⑤a 優先度 900 2026-08-03 08-07 Bob を先に確保
⑤b 優先度 100 2026-08-10 08-14 Bob が空くまで待つ
③ 開始日を固定 2026-08-17 08-21 start 2026-08-17
② ALAP 2026-09-23 09-29 納品期限からの逆算
納品期限 2026-09-30 — milestone + start 固定

⑤a と ⑤b は同じ Bob を奪い合う。priority の高い ⑤a が先に確保し、
⑤b は空くまで押し出される。

ソース

constraints.tjp
/*
 * 05: 制約とスケジューリング方向
 *
 * 実行: bundle exec tj3 -o 05-constraints/out 05-constraints/constraints.tjp
 *
 * スケジューラに「いつ置くか」を指示する手段を一通り試す。
 */

project con "制約とスケジューリング" 2026-08-03 +3m {
  timezone "Asia/Tokyo"
  timeformat "%Y-%m-%d"
  now 2026-07-29
}

// フラグは使う前にトップレベルで宣言しておく必要がある
flags critical, external

resource alice "Alice"
resource bob   "Bob"
resource carol "Carol"

// ---- ① ASAP: 既定の前詰め ----

/*
 * 何も指定しなければ ASAP (As Soon As Possible)。
 * 開始側の条件 (プロジェクト開始日 / start / depends) を起点に前詰めされる。
 */
task asap "① ASAP: できるだけ早く" {
  effort 5d
  allocate alice
}

// ---- ② ALAP: 締め切りからの逆算 ----

// 固定日のマイルストーン。ここが逆算の起点になる
task deadline "納品期限" {
  milestone
  start 2026-09-30
}

/*
 * ALAP (As Late As Possible) は終了側の条件 (end / precedes) が必要。
 * precedes は depends の逆向き ---「自分が終わってから相手が始まる」。
 *
 * scheduling は他の属性から暗黙に上書きされうるので、
 * 公式ドキュメントの推奨どおりタスクの最後に書く。
 */
task alap "② ALAP: できるだけ遅く" {
  effort 5d
  allocate bob
  precedes deadline
  scheduling alap
}

// ---- ③ 開始日/終了日の固定 ----

/*
 * start は「ハードな開始条件」。依存関係より強く効く。
 * end を指定すると ALAP 側の固定になる。
 */
task fixed "③ 開始日を固定" {
  start 2026-08-17
  effort 5d
  allocate alice
}

// ---- ④ 事後チェック ----

/*
 * minstart / maxstart / minend / maxend はスケジューリングには使われず、
 * 全タスクを配置し終えた後に検査され、違反すると *エラー* になる。
 *
 * warn は同じく事後検査だが *警告* で済む。論理式で条件を書き、
 * 真になったら警告が出る。属性は scenario_id 付きで参照する (plan.end など)。
 *
 * 下は「9/1 より後に終わったら警告」。このタスクは 9/1 以前に終わるので
 * 警告は出ない。日付を 2026-08-05 などに変えると警告が出るのを確認できる。
 */
task checked "④ 期限を検査するタスク" {
  effort 10d
  allocate carol
  maxend 2026-10-31
  warn plan.end > 2026-09-01
}

// ---- ⑤ priority によるリソース競合の解決 ----

/*
 * 同じリソースを奪い合ったとき、priority の高いタスクが先に確保する。
 * 既定値は 500、範囲は 1〜1000。
 * 「重要度」ではなく「リソース獲得の優先順位」である点に注意。
 */
task high "⑤a 優先度 900" {
  effort 5d
  allocate bob
  priority 900
}

task low "⑤b 優先度 100" {
  effort 5d
  allocate bob
  priority 100
}

// ---- ⑥ 分類のための属性 ----

/*
 * flags はレポートのフィルタ条件に使う (08 で扱う)。
 * responsible はドキュメント用途のみでスケジューリングに影響しない。
 */
task tagged "⑥ フラグと責任者" {
  effort 3d
  allocate alice
  flags critical, external
  responsible alice
}

// ---- レポート ----

taskreport tasks "tasks" {
  formats html
  headline "制約による配置の違い"
  columns name, start, end, effort, resources, responsible, chart
}

学ぶ内容

ASAP と ALAP

方向 必要な条件 意味
ASAP (既定) 開始側の条件 (start / depends) できるだけ早く
ALAP 終了側の条件 (end / precedes) できるだけ遅く

scheduling は他の属性から暗黙に上書きされうるので、
公式ドキュメントの推奨どおりタスクの最後に書く。

事後検査の3種

いずれもスケジューリングには使われず、配置し終えた後に検査される。

手段 違反時 用途
minstart / maxstart / minend / maxend エラー (停止) 絶対に守るべき期限
warn <論理式> 警告 (終了コードは 0) 監視したい条件
fail <論理式> エラー 論理式で表す厳格な条件

論理式の中では属性を plan.end のようにシナリオ ID 付きで参照する。

ハマりどころ

  1. priority は「重要度」ではなく「リソース獲得の優先順位」。
    既定 500、範囲 1〜1000。リソースを持たないタスク (マイルストーン) には効かない
  2. ASAP と ALAP を混ぜるとスケジューリングが重くなる。
    公式マニュアルによれば数百タスク規模で 2〜10 倍の時間差が出ることがあり、
    依存チェーンが絡むと優先度が高いタスクがリソースを取れない事態も起きうる
  3. flags は使う前にトップレベルで宣言しておく必要がある
  4. responsible はドキュメント用途のみでスケジューリングに影響しない

得られるもの

「この日までに終わらせたい」「この日から始める」「競合したらこちらを優先」という
現実の制約を、スケジューラへの指示として表現できるようになる。
warn を使えば、計画が条件を外れたことを自動で検知できる。