Files
jarvis/api/endpoints/directives.php
myron aaf07edacb Phase 8: Mission Directives — OKR/goal tracking with AI review
- DB: directives, directive_key_results, directive_links tables
- reactor.py v8.0.0: directive_review handler — fetches active directives + KRs + links, Claude generates executive progress briefing, injects into conversations
- directives.php: new API endpoint (list/get/save/delete/key_result_update/link/summary)
- api.php: routes directives/* endpoint
- admin/index.php: Directives nav + tab — objective cards with progress bars, editor with multi-KR builder (title/current/target/unit), AI Review button per directive and global
- index.html: DIRECTIVES tab — collapsible objective cards with progress bars, KR counts, AI Review button, link to admin
- chat.php: Tier 0.9i directive review detection; daily briefing now includes active directive progress %

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 11:59:44 +00:00

172 lines
7.7 KiB
PHP

<?php
// JARVIS Directives — OKR / mission goal tracking
// Actions: list | get | save | delete | key_result_update | link | unlink | review
switch ($action) {
// GET directives/list — all active directives with key results + progress
case 'list':
$status = $_GET['status'] ?? 'active';
$category = $_GET['category'] ?? '';
$where = '1=1';
$params = [];
if ($status && $status !== 'all') { $where .= ' AND d.status=?'; $params[] = $status; }
if ($category) { $where .= ' AND d.category=?'; $params[] = $category; }
$rows = JarvisDB::query(
"SELECT d.*,
COUNT(kr.id) AS kr_count,
COALESCE(SUM(kr.current_value),0) AS kr_current_sum,
COALESCE(SUM(kr.target_value),0) AS kr_target_sum,
(SELECT COUNT(*) FROM directive_links dl WHERE dl.directive_id=d.id) AS link_count
FROM directives d
LEFT JOIN directive_key_results kr ON kr.directive_id=d.id
WHERE {$where}
GROUP BY d.id
ORDER BY d.priority DESC, d.target_date ASC, d.created_at DESC",
$params
) ?: [];
// Compute progress pct per directive
foreach ($rows as &$r) {
$r['progress'] = ($r['kr_target_sum'] > 0)
? (float)round($r['kr_current_sum'] / $r['kr_target_sum'] * 100, 1)
: 0;
}
unset($r);
echo json_encode(['directives' => $rows]);
break;
// GET directives/get?id=X — full directive with key results and links
case 'get':
$id = (int)($_GET['id'] ?? 0);
if (!$id) { echo json_encode(['error' => 'Missing id']); break; }
$d = JarvisDB::single("SELECT * FROM directives WHERE id=?", [$id]);
if (!$d) { echo json_encode(['error' => 'Not found']); break; }
$krs = JarvisDB::query("SELECT * FROM directive_key_results WHERE directive_id=? ORDER BY id ASC", [$id]) ?: [];
$links = JarvisDB::query(
"SELECT dl.*, t.title AS task_title, a.title AS appt_title
FROM directive_links dl
LEFT JOIN tasks t ON dl.link_type='task' AND t.id=dl.link_id
LEFT JOIN appointments a ON dl.link_type='appointment' AND a.id=dl.link_id
WHERE dl.directive_id=?
ORDER BY dl.created_at DESC",
[$id]
) ?: [];
$sum_cur = array_sum(array_column($krs, 'current_value'));
$sum_tgt = array_sum(array_column($krs, 'target_value'));
$d['progress'] = ($sum_tgt > 0) ? round($sum_cur / $sum_tgt * 100, 1) : 0;
$d['key_results'] = $krs;
$d['links'] = $links;
echo json_encode($d);
break;
// POST directives/save — create or update directive + key results
case 'save':
$id = (int)($data['id'] ?? 0);
$title = trim($data['title'] ?? '');
$description = trim($data['description'] ?? '');
$category = $data['category'] ?? 'work';
$status = $data['status'] ?? 'active';
$priority = (int)($data['priority'] ?? 5);
$target_date = !empty($data['target_date']) ? $data['target_date'] : null;
if (!$title) { echo json_encode(['error' => 'Title required']); break; }
if ($id) {
JarvisDB::execute(
"UPDATE directives SET title=?,description=?,category=?,status=?,priority=?,target_date=?,updated_at=NOW() WHERE id=?",
[$title,$description,$category,$status,$priority,$target_date,$id]
);
} else {
$id = JarvisDB::insert(
"INSERT INTO directives (title,description,category,status,priority,target_date) VALUES (?,?,?,?,?,?)",
[$title,$description,$category,$status,$priority,$target_date]
);
}
// Replace key results if provided
if (isset($data['key_results']) && is_array($data['key_results'])) {
JarvisDB::execute("DELETE FROM directive_key_results WHERE directive_id=?", [$id]);
foreach ($data['key_results'] as $kr) {
$krtitle = trim($kr['title'] ?? ''); if (!$krtitle) continue;
JarvisDB::execute(
"INSERT INTO directive_key_results (directive_id,title,current_value,target_value,unit) VALUES (?,?,?,?,?)",
[$id, $krtitle, (float)($kr['current_value']??0), (float)($kr['target_value']??100), $kr['unit']??'%']
);
}
}
echo json_encode(['ok' => true, 'id' => $id]);
break;
// POST directives/delete?id=X
case 'delete':
$id = (int)($_GET['id'] ?? $data['id'] ?? 0);
if (!$id) { echo json_encode(['error' => 'Missing id']); break; }
JarvisDB::execute("DELETE FROM directive_key_results WHERE directive_id=?", [$id]);
JarvisDB::execute("DELETE FROM directive_links WHERE directive_id=?", [$id]);
JarvisDB::execute("DELETE FROM directives WHERE id=?", [$id]);
echo json_encode(['ok' => true]);
break;
// POST directives/key_result_update — update a single KR's current value
case 'key_result_update':
$krid = (int)($data['id'] ?? 0);
$value = (float)($data['current_value'] ?? 0);
if (!$krid) { echo json_encode(['error' => 'Missing kr id']); break; }
JarvisDB::execute(
"UPDATE directive_key_results SET current_value=?, updated_at=NOW() WHERE id=?",
[$value, $krid]
);
echo json_encode(['ok' => true]);
break;
// POST directives/link — link a task or appointment to a directive
case 'link':
$did = (int)($data['directive_id'] ?? 0);
$link_type = $data['link_type'] ?? 'task';
$link_id = (int)($data['link_id'] ?? 0);
$note = trim($data['note'] ?? '');
if (!$did) { echo json_encode(['error' => 'Missing directive_id']); break; }
JarvisDB::execute(
"INSERT INTO directive_links (directive_id,link_type,link_id,note) VALUES (?,?,?,?)",
[$did, $link_type, $link_id ?: null, $note]
);
echo json_encode(['ok' => true]);
break;
// POST directives/unlink?id=X — remove a link
case 'unlink':
$lid = (int)($_GET['id'] ?? $data['id'] ?? 0);
if (!$lid) { echo json_encode(['error' => 'Missing id']); break; }
JarvisDB::execute("DELETE FROM directive_links WHERE id=?", [$lid]);
echo json_encode(['ok' => true]);
break;
// GET directives/summary — compact progress snapshot for chat/briefing injection
case 'summary':
$rows = JarvisDB::query(
"SELECT d.id, d.title, d.category, d.target_date,
COALESCE(SUM(kr.current_value),0) AS kr_cur,
COALESCE(SUM(kr.target_value),1) AS kr_tgt
FROM directives d
LEFT JOIN directive_key_results kr ON kr.directive_id=d.id
WHERE d.status='active'
GROUP BY d.id
ORDER BY d.priority DESC, d.target_date ASC
LIMIT 10"
) ?: [];
$summary = [];
foreach ($rows as $r) {
$pct = round($r['kr_cur'] / max($r['kr_tgt'], 1) * 100, 0);
$summary[] = [
'id' => $r['id'],
'title' => $r['title'],
'category' => $r['category'],
'target_date' => $r['target_date'],
'progress' => $pct,
];
}
echo json_encode(['directives' => $summary]);
break;
default:
http_response_code(404);
echo json_encode(['error' => "Unknown directives action: {$action}"]);
}