-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode.py
More file actions
108 lines (84 loc) · 4.26 KB
/
Copy pathcode.py
File metadata and controls
108 lines (84 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""Prioritization — decide what to do next when you can't do everything.
Scoring is the easy half. The half that actually bites:
dependencies - a high-scoring task blocked by a low-scoring one can't run
first, no matter what the score says.
starvation - a permanently low-priority task must eventually run, or the
queue quietly drops work forever.
capacity - the point of a priority order is that the tail doesn't run.
The model scores; the scheduler decides. Keeping those apart matters, because
a model asked to "pick what to do next" will happily ignore a dependency.
"""
from resources.agent import llm
from resources.helper import parse_json
TASKS = [
{"id": "T1", "title": "Prod API returning 500s for 30% of checkout traffic",
"needs": []},
{"id": "T2", "title": "Rotate the leaked staging credentials", "needs": []},
{"id": "T3", "title": "Ship the new pricing page copy", "needs": ["T5"]},
{"id": "T4", "title": "Reply to the quarterly vendor survey", "needs": []},
{"id": "T5", "title": "Legal sign-off on pricing claims", "needs": []},
{"id": "T6", "title": "Upgrade the CI runner image", "needs": []},
]
CAPACITY = 3 # tasks that actually get done this cycle
AGE = {"T4": 4} # cycles each task has already been passed over
def score_tasks(tasks):
"""Ask the model to score urgency and impact. It does not decide order."""
listing = "\n".join(f"{t['id']}: {t['title']}" for t in tasks)
reply = llm.invoke(
"Score each engineering task 1-10 for urgency and for impact.\n"
'Reply with JSON only: [{"id": "T1", "urgency": 9, "impact": 8, '
'"why": "<6 words>"}]\n\n' + listing
)
return {s["id"]: s for s in parse_json(reply.content)}
def priority(task, scores):
"""Combine model scores with an ageing bonus.
The ageing term is the anti-starvation mechanism. Without it, a task that
is never the most important thing is never done at all - which is not
deprioritisation, it's silent abandonment.
"""
s = scores[task["id"]]
base = s["urgency"] * 2 + s["impact"]
return base + 3 * AGE.get(task["id"], 0)
def schedule(tasks, scores, capacity):
"""Order by priority, but never before a task's dependencies.
Deliberately not done by the model. Dependency order is a hard constraint,
and hard constraints belong in code where they cannot be reasoned away.
"""
ordered, remaining, done = [], list(tasks), set()
while remaining:
# Only tasks whose dependencies are already scheduled are eligible.
ready = [t for t in remaining if set(t["needs"]) <= done]
if not ready:
print(f" [!] deadlock: {[t['id'] for t in remaining]} "
f"have unmet dependencies")
break
best = max(ready, key=lambda t: priority(t, scores))
ordered.append(best)
done.add(best["id"])
remaining.remove(best)
return ordered[:capacity], ordered[capacity:]
if __name__ == "__main__":
scores = score_tasks(TASKS)
print(f"{'=' * 60}\nSCORES\n{'=' * 60}")
for task in TASKS:
s = scores[task["id"]]
aged = AGE.get(task["id"], 0)
print(f" {task['id']} urgency={s['urgency']:<3} impact={s['impact']:<3} "
f"age={aged} priority={priority(task, scores):<4} {s['why']}")
doing, deferred = schedule(TASKS, scores, CAPACITY)
print(f"\n{'=' * 60}\nTHIS CYCLE (capacity {CAPACITY})\n{'=' * 60}")
for i, task in enumerate(doing, 1):
blocked_by = f" (after {','.join(task['needs'])})" if task["needs"] else ""
print(f" {i}. {task['id']} {task['title'][:50]}{blocked_by}")
print(f"\n{'=' * 60}\nDEFERRED\n{'=' * 60}")
for task in deferred:
# Deferred work is named, not silently dropped. A queue you can't see
# is a queue nobody notices growing.
next_age = AGE.get(task["id"], 0) + 1
print(f" - {task['id']} {task['title'][:50]} (age -> {next_age})")
print(f"\n{'=' * 60}\nDEPENDENCY CHECK\n{'=' * 60}")
positions = {t["id"]: i for i, t in enumerate(doing + deferred)}
for task in TASKS:
for need in task["needs"]:
ok = positions[need] < positions[task["id"]]
print(f" [{'ok ' if ok else 'FAIL'}] {task['id']} scheduled after {need}")