continuum-toolkit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli/__init__.py +12 -0
- cli/main.py +415 -0
- confidence/__init__.py +13 -0
- confidence/calculator.py +308 -0
- confidence/models.py +40 -0
- context/__init__.py +17 -0
- context/models.py +108 -0
- context/pruner.py +228 -0
- context/selector.py +193 -0
- continuum_toolkit-1.0.0.dist-info/METADATA +511 -0
- continuum_toolkit-1.0.0.dist-info/RECORD +72 -0
- continuum_toolkit-1.0.0.dist-info/WHEEL +5 -0
- continuum_toolkit-1.0.0.dist-info/entry_points.txt +2 -0
- continuum_toolkit-1.0.0.dist-info/licenses/LICENSE +21 -0
- continuum_toolkit-1.0.0.dist-info/top_level.txt +13 -0
- contradictions/__init__.py +19 -0
- contradictions/detector.py +442 -0
- contradictions/models.py +75 -0
- core/__init__.py +97 -0
- core/enums.py +130 -0
- core/evidence.py +117 -0
- core/interfaces.py +209 -0
- core/schema.py +391 -0
- core/serializer.py +84 -0
- core/state_models.py +530 -0
- daemon/__init__.py +12 -0
- daemon/service.py +170 -0
- extractors/__init__.py +51 -0
- extractors/base.py +117 -0
- extractors/config/parsers.py +288 -0
- extractors/config/secret_sanitizer.py +91 -0
- extractors/config_extractor.py +172 -0
- extractors/conversation/analyzers.py +193 -0
- extractors/conversation/models.py +148 -0
- extractors/conversation_extractor.py +166 -0
- extractors/git_extractor.py +305 -0
- extractors/parsers/base.py +91 -0
- extractors/parsers/comment_parser.py +51 -0
- extractors/parsers/js_ts_parser.py +171 -0
- extractors/parsers/python_parser.py +180 -0
- extractors/verification/runners.py +278 -0
- extractors/verification_extractor.py +263 -0
- extractors/workspace_extractor.py +221 -0
- graph/__init__.py +24 -0
- graph/diff.py +109 -0
- graph/manager.py +473 -0
- graph/models.py +62 -0
- graph/propagator.py +194 -0
- graph/query.py +86 -0
- graph/snapshot.py +85 -0
- handoff/__init__.py +28 -0
- handoff/adapters/__init__.py +45 -0
- handoff/adapters/base.py +90 -0
- handoff/adapters/claude_adapter.py +176 -0
- handoff/adapters/codex_gpt_adapter.py +151 -0
- handoff/adapters/gemini_adapter.py +151 -0
- handoff/adapters/local_model_adapter.py +130 -0
- handoff/models.py +88 -0
- handoff/packager.py +288 -0
- pipeline/__init__.py +9 -0
- pipeline/orchestrator.py +260 -0
- resolution/__init__.py +15 -0
- resolution/resolver.py +311 -0
- storage/__init__.py +18 -0
- storage/hooks.py +125 -0
- storage/manager.py +127 -0
- storage/models.py +39 -0
- storage/recovery.py +98 -0
- watcher/__init__.py +17 -0
- watcher/detector.py +136 -0
- watcher/models.py +62 -0
- watcher/updater.py +163 -0
confidence/calculator.py
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project Continuum - Confidence Calculation Engine
|
|
3
|
+
=================================================
|
|
4
|
+
Milestone 3 - Phase 8: Confidence Calculation & Status Evaluation.
|
|
5
|
+
Calculates explainable confidence metrics (0.0% to 100.0%) grounded in physical proof
|
|
6
|
+
and governed by contradiction caps and hierarchy constraints.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
10
|
+
|
|
11
|
+
from core.enums import EvidenceLevel, EvidenceType, NodeType, Status
|
|
12
|
+
from core.evidence import Evidence
|
|
13
|
+
from core.interfaces import IConfidenceCalculator
|
|
14
|
+
from core.state_models import (
|
|
15
|
+
CanonicalProjectState,
|
|
16
|
+
ContradictionRecord,
|
|
17
|
+
GraphNode,
|
|
18
|
+
ProjectState,
|
|
19
|
+
)
|
|
20
|
+
from confidence.models import ConfidenceBreakdown
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConfidenceCalculator(IConfidenceCalculator):
|
|
24
|
+
"""
|
|
25
|
+
Computes grounded confidence scores for nodes, components, tasks, and entire projects.
|
|
26
|
+
|
|
27
|
+
Scoring Dimensions:
|
|
28
|
+
- Implementation Presence (Level 2): 0 to 35 pts
|
|
29
|
+
- Test Existence: 0 to 15 pts
|
|
30
|
+
- Test Execution & Passing Status (Level 1): 0 to 30 pts
|
|
31
|
+
- Git Working Tree Consistency (Level 3): 0 to 10 pts
|
|
32
|
+
- Documentation Presence (Level 4): 0 to 10 pts
|
|
33
|
+
|
|
34
|
+
Hard Upper Caps:
|
|
35
|
+
- High-Severity Contradiction on target: Capped at MAX 20.0%
|
|
36
|
+
- Medium-Severity Contradiction on target: Capped at MAX 50.0%
|
|
37
|
+
- Conversational Claim only (0 physical evidence): Capped at MAX 15.0%
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def calculate_node_confidence(
|
|
41
|
+
self,
|
|
42
|
+
node: GraphNode,
|
|
43
|
+
evidence_pool: Dict[str, Evidence],
|
|
44
|
+
contradictions: List[ContradictionRecord]
|
|
45
|
+
) -> Tuple[float, Dict[str, Any]]:
|
|
46
|
+
"""
|
|
47
|
+
Calculates confidence score for a GraphNode.
|
|
48
|
+
Implements IConfidenceCalculator Protocol.
|
|
49
|
+
"""
|
|
50
|
+
# Gather relevant evidence for this node
|
|
51
|
+
node_evidence: List[Evidence] = []
|
|
52
|
+
for ev_id in node.evidence_ids:
|
|
53
|
+
if ev_id in evidence_pool:
|
|
54
|
+
node_evidence.append(evidence_pool[ev_id])
|
|
55
|
+
|
|
56
|
+
# Also search pool if evidence_ids was empty
|
|
57
|
+
if not node_evidence:
|
|
58
|
+
node_name_lower = node.name.lower()
|
|
59
|
+
for ev in evidence_pool.values():
|
|
60
|
+
if node_name_lower in ev.summary.lower() or node_name_lower in str(ev.raw_payload).lower():
|
|
61
|
+
node_evidence.append(ev)
|
|
62
|
+
|
|
63
|
+
breakdown = self.evaluate_component_confidence(
|
|
64
|
+
target_id=node.id,
|
|
65
|
+
target_name=node.name,
|
|
66
|
+
evidence_list=node_evidence,
|
|
67
|
+
contradictions=contradictions
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
node.confidence_score = breakdown.final_score
|
|
71
|
+
return breakdown.final_score, breakdown.to_dict()
|
|
72
|
+
|
|
73
|
+
def evaluate_component_confidence(
|
|
74
|
+
self,
|
|
75
|
+
target_id: str,
|
|
76
|
+
target_name: str,
|
|
77
|
+
evidence_list: List[Evidence],
|
|
78
|
+
contradictions: List[ContradictionRecord]
|
|
79
|
+
) -> ConfidenceBreakdown:
|
|
80
|
+
"""
|
|
81
|
+
Evaluates multi-dimensional confidence breakdown for any target identifier/name.
|
|
82
|
+
"""
|
|
83
|
+
breakdown = ConfidenceBreakdown(target_id=target_id)
|
|
84
|
+
breakdown.contributing_evidence_ids = [e.id for e in evidence_list]
|
|
85
|
+
|
|
86
|
+
if not evidence_list:
|
|
87
|
+
# Check if there are active contradictions
|
|
88
|
+
relevant_contradictions = self._get_relevant_contradictions(target_id, target_name, contradictions)
|
|
89
|
+
breakdown.active_contradiction_ids = [c.id for c in relevant_contradictions]
|
|
90
|
+
breakdown.final_score = 0.0
|
|
91
|
+
breakdown.explanation = f"Target '{target_name or target_id}' has zero evidence. Confidence: 0.0%."
|
|
92
|
+
return breakdown
|
|
93
|
+
|
|
94
|
+
# Check for pure conversational evidence
|
|
95
|
+
has_physical_evidence = any(
|
|
96
|
+
e.level in {EvidenceLevel.LEVEL_1_RUNTIME_TEST, EvidenceLevel.LEVEL_2_CODE_AST, EvidenceLevel.LEVEL_3_GIT_STATE}
|
|
97
|
+
for e in evidence_list
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# 1. Implementation Presence (Max 35)
|
|
101
|
+
for ev in evidence_list:
|
|
102
|
+
if ev.type in {EvidenceType.SOURCE_CODE, EvidenceType.AST_SYMBOL}:
|
|
103
|
+
# If valid AST without syntax errors
|
|
104
|
+
if not ev.raw_payload.get("errors"):
|
|
105
|
+
breakdown.implementation_presence = 35.0
|
|
106
|
+
else:
|
|
107
|
+
breakdown.implementation_presence = 15.0 # Partial due to errors
|
|
108
|
+
elif ev.type == EvidenceType.CONFIG_FILE:
|
|
109
|
+
breakdown.implementation_presence = max(breakdown.implementation_presence, 25.0)
|
|
110
|
+
|
|
111
|
+
# 2. Test Existence (Max 15)
|
|
112
|
+
has_tests = any(
|
|
113
|
+
ev.type == EvidenceType.TEST_RUN or "test" in ev.summary.lower() or "test" in ev.raw_payload.get("file_path", "").lower()
|
|
114
|
+
for ev in evidence_list
|
|
115
|
+
)
|
|
116
|
+
if has_tests:
|
|
117
|
+
breakdown.test_existence = 15.0
|
|
118
|
+
|
|
119
|
+
# 3. Test Execution Pass (Max 30)
|
|
120
|
+
for ev in evidence_list:
|
|
121
|
+
if ev.type in {EvidenceType.TEST_RUN, EvidenceType.BUILD_LOG, EvidenceType.RUNTIME_LOG}:
|
|
122
|
+
exit_code = ev.raw_payload.get("exit_code", 0)
|
|
123
|
+
if exit_code == 0:
|
|
124
|
+
breakdown.test_execution_pass = 30.0
|
|
125
|
+
else:
|
|
126
|
+
breakdown.test_execution_pass = 0.0
|
|
127
|
+
breakdown.test_existence = 15.0 # Test exists but failed
|
|
128
|
+
|
|
129
|
+
# 4. Git Consistency (Max 10)
|
|
130
|
+
for ev in evidence_list:
|
|
131
|
+
if ev.type == EvidenceType.GIT_STATUS:
|
|
132
|
+
is_dirty = ev.raw_payload.get("is_dirty", False)
|
|
133
|
+
if not is_dirty:
|
|
134
|
+
breakdown.git_consistency = 10.0
|
|
135
|
+
else:
|
|
136
|
+
breakdown.git_consistency = 4.0
|
|
137
|
+
elif ev.type in {EvidenceType.GIT_COMMIT, EvidenceType.GIT_DIFF}:
|
|
138
|
+
breakdown.git_consistency = max(breakdown.git_consistency, 8.0)
|
|
139
|
+
|
|
140
|
+
# 5. Documentation Presence (Max 10)
|
|
141
|
+
for ev in evidence_list:
|
|
142
|
+
if ev.type == EvidenceType.DOCUMENTATION or ev.level == EvidenceLevel.LEVEL_4_DOCUMENTATION:
|
|
143
|
+
breakdown.documentation_presence = 10.0
|
|
144
|
+
|
|
145
|
+
# Sum raw score
|
|
146
|
+
raw = (
|
|
147
|
+
breakdown.implementation_presence
|
|
148
|
+
+ breakdown.test_existence
|
|
149
|
+
+ breakdown.test_execution_pass
|
|
150
|
+
+ breakdown.git_consistency
|
|
151
|
+
+ breakdown.documentation_presence
|
|
152
|
+
)
|
|
153
|
+
breakdown.raw_score = raw
|
|
154
|
+
|
|
155
|
+
# 6. Apply Contradiction Penalties and Hard Caps
|
|
156
|
+
relevant_contradictions = self._get_relevant_contradictions(target_id, target_name, contradictions)
|
|
157
|
+
breakdown.active_contradiction_ids = [c.id for c in relevant_contradictions]
|
|
158
|
+
|
|
159
|
+
score = raw
|
|
160
|
+
cap: Optional[float] = None
|
|
161
|
+
|
|
162
|
+
if not has_physical_evidence:
|
|
163
|
+
# Pure conversational claims without physical grounding capped at 15.0%
|
|
164
|
+
cap = 15.0
|
|
165
|
+
breakdown.contradiction_penalties += 20.0
|
|
166
|
+
|
|
167
|
+
for contra in relevant_contradictions:
|
|
168
|
+
if contra.severity == "HIGH":
|
|
169
|
+
cap = min(cap, 20.0) if cap is not None else 20.0
|
|
170
|
+
breakdown.contradiction_penalties += 50.0
|
|
171
|
+
elif contra.severity == "MEDIUM":
|
|
172
|
+
cap = min(cap, 50.0) if cap is not None else 50.0
|
|
173
|
+
breakdown.contradiction_penalties += 25.0
|
|
174
|
+
elif contra.severity == "LOW":
|
|
175
|
+
breakdown.contradiction_penalties += 10.0
|
|
176
|
+
|
|
177
|
+
score = max(0.0, score - breakdown.contradiction_penalties)
|
|
178
|
+
if cap is not None:
|
|
179
|
+
score = min(score, cap)
|
|
180
|
+
breakdown.contradiction_cap = cap
|
|
181
|
+
|
|
182
|
+
breakdown.final_score = round(min(100.0, score), 1)
|
|
183
|
+
|
|
184
|
+
# 7. Compose detailed explainability rationale
|
|
185
|
+
reasons = []
|
|
186
|
+
if breakdown.implementation_presence > 0:
|
|
187
|
+
reasons.append(f"Physical implementation verified (+{breakdown.implementation_presence:.0f}%)")
|
|
188
|
+
if breakdown.test_existence > 0:
|
|
189
|
+
reasons.append(f"Test suite exists (+{breakdown.test_existence:.0f}%)")
|
|
190
|
+
if breakdown.test_execution_pass > 0:
|
|
191
|
+
reasons.append(f"Automated tests passed (+{breakdown.test_execution_pass:.0f}%)")
|
|
192
|
+
if breakdown.git_consistency > 0:
|
|
193
|
+
reasons.append(f"Git state recorded (+{breakdown.git_consistency:.0f}%)")
|
|
194
|
+
if breakdown.documentation_presence > 0:
|
|
195
|
+
reasons.append(f"Documentation verified (+{breakdown.documentation_presence:.0f}%)")
|
|
196
|
+
|
|
197
|
+
if not has_physical_evidence:
|
|
198
|
+
reasons.append("Unbacked conversational claim capped at 15.0%")
|
|
199
|
+
|
|
200
|
+
if cap is not None and cap < raw:
|
|
201
|
+
reasons.append(f"Capped at {cap:.0f}% due to active contradictions ({len(relevant_contradictions)} detected)")
|
|
202
|
+
|
|
203
|
+
breakdown.explanation = (
|
|
204
|
+
f"Confidence {breakdown.final_score:.1f}% for '{target_name or target_id}'. "
|
|
205
|
+
+ "; ".join(reasons)
|
|
206
|
+
+ "."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
return breakdown
|
|
210
|
+
|
|
211
|
+
def calculate_project_confidence(
|
|
212
|
+
self,
|
|
213
|
+
canonical_state: CanonicalProjectState
|
|
214
|
+
) -> Tuple[float, Dict[str, Any]]:
|
|
215
|
+
"""
|
|
216
|
+
Calculates global aggregate project confidence and detailed telemetry.
|
|
217
|
+
Implements IConfidenceCalculator Protocol.
|
|
218
|
+
"""
|
|
219
|
+
scores: List[float] = []
|
|
220
|
+
node_breakdowns: Dict[str, Any] = {}
|
|
221
|
+
|
|
222
|
+
# 1. Evaluate registered graph nodes
|
|
223
|
+
for node in canonical_state.graph_nodes.values():
|
|
224
|
+
sc, bd = self.calculate_node_confidence(
|
|
225
|
+
node=node,
|
|
226
|
+
evidence_pool=canonical_state.evidence_pool,
|
|
227
|
+
contradictions=canonical_state.contradictions
|
|
228
|
+
)
|
|
229
|
+
scores.append(sc)
|
|
230
|
+
node_breakdowns[node.id] = bd
|
|
231
|
+
|
|
232
|
+
# 2. Evaluate User Requirements
|
|
233
|
+
req_scores = []
|
|
234
|
+
for req in canonical_state.conversational_state.user_requirements:
|
|
235
|
+
matching_ev = [
|
|
236
|
+
ev for ev in canonical_state.evidence_pool.values()
|
|
237
|
+
if req.title.lower() in ev.summary.lower()
|
|
238
|
+
or (req.evidence_id and ev.id == req.evidence_id)
|
|
239
|
+
]
|
|
240
|
+
bd = self.evaluate_component_confidence(
|
|
241
|
+
target_id=req.id,
|
|
242
|
+
target_name=req.title,
|
|
243
|
+
evidence_list=matching_ev,
|
|
244
|
+
contradictions=canonical_state.contradictions
|
|
245
|
+
)
|
|
246
|
+
req_scores.append(bd.final_score)
|
|
247
|
+
scores.append(bd.final_score)
|
|
248
|
+
|
|
249
|
+
# 3. Factor in Project Physical State metrics
|
|
250
|
+
pstate = canonical_state.project_state
|
|
251
|
+
has_code = len(pstate.files) > 0
|
|
252
|
+
has_symbols = len(pstate.symbols) > 0
|
|
253
|
+
test_pass_rate = 0.0
|
|
254
|
+
if pstate.test_results:
|
|
255
|
+
passed_tests = sum(1 for t in pstate.test_results if t.status == Status.VERIFIED)
|
|
256
|
+
test_pass_rate = (passed_tests / len(pstate.test_results)) * 100.0
|
|
257
|
+
|
|
258
|
+
if not scores:
|
|
259
|
+
# Baseline calculation from project state if graph is not yet populated
|
|
260
|
+
base_score = 0.0
|
|
261
|
+
if has_code:
|
|
262
|
+
base_score += 35.0
|
|
263
|
+
if has_symbols:
|
|
264
|
+
base_score += 15.0
|
|
265
|
+
if pstate.test_results:
|
|
266
|
+
base_score += (test_pass_rate * 0.35)
|
|
267
|
+
if pstate.git_state.is_repo and not pstate.git_state.is_dirty:
|
|
268
|
+
base_score += 15.0
|
|
269
|
+
|
|
270
|
+
# Contradiction discount
|
|
271
|
+
unresolved_high = sum(1 for c in canonical_state.contradictions if c.severity == "HIGH" and not c.resolved)
|
|
272
|
+
unresolved_med = sum(1 for c in canonical_state.contradictions if c.severity == "MEDIUM" and not c.resolved)
|
|
273
|
+
penalty = (unresolved_high * 25.0) + (unresolved_med * 10.0)
|
|
274
|
+
final_project_score = round(max(0.0, min(100.0, base_score - penalty)), 1)
|
|
275
|
+
else:
|
|
276
|
+
final_project_score = round(sum(scores) / len(scores), 1)
|
|
277
|
+
|
|
278
|
+
summary_data = {
|
|
279
|
+
"project_confidence": final_project_score,
|
|
280
|
+
"evaluated_node_count": len(scores),
|
|
281
|
+
"unresolved_contradictions": len([c for c in canonical_state.contradictions if not c.resolved]),
|
|
282
|
+
"test_pass_rate": round(test_pass_rate, 1),
|
|
283
|
+
"has_physical_code": has_code,
|
|
284
|
+
"has_ast_symbols": has_symbols,
|
|
285
|
+
"node_breakdowns": node_breakdowns
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return final_project_score, summary_data
|
|
289
|
+
|
|
290
|
+
def _get_relevant_contradictions(
|
|
291
|
+
self,
|
|
292
|
+
target_id: str,
|
|
293
|
+
target_name: str,
|
|
294
|
+
contradictions: List[ContradictionRecord]
|
|
295
|
+
) -> List[ContradictionRecord]:
|
|
296
|
+
"""Filters contradictions related to the specific target."""
|
|
297
|
+
relevant = []
|
|
298
|
+
target_lower = (target_name or target_id).lower()
|
|
299
|
+
|
|
300
|
+
for c in contradictions:
|
|
301
|
+
if c.resolved:
|
|
302
|
+
continue
|
|
303
|
+
if c.claim_id == target_id:
|
|
304
|
+
relevant.append(c)
|
|
305
|
+
elif target_lower and (target_lower in c.explanation.lower() or target_lower in c.claim_text.lower()):
|
|
306
|
+
relevant.append(c)
|
|
307
|
+
|
|
308
|
+
return relevant
|
confidence/models.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project Continuum - Confidence Metrics & Breakdown Models
|
|
3
|
+
=========================================================
|
|
4
|
+
Milestone 3 - Phase 8: Confidence Calculation & Status Evaluation.
|
|
5
|
+
Defines structured multi-dimensional confidence breakdown schemas.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field, asdict
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ConfidenceBreakdown:
|
|
14
|
+
"""
|
|
15
|
+
Explainable multi-dimensional confidence scoring breakdown.
|
|
16
|
+
Total potential raw score: 100.0%.
|
|
17
|
+
"""
|
|
18
|
+
target_id: str
|
|
19
|
+
implementation_presence: float = 0.0 # Max 35.0 (AST symbols, files, exports)
|
|
20
|
+
test_existence: float = 0.0 # Max 15.0 (Test files / suites present)
|
|
21
|
+
test_execution_pass: float = 0.0 # Max 30.0 (Passing test exit codes)
|
|
22
|
+
git_consistency: float = 0.0 # Max 10.0 (Clean working tree & commits)
|
|
23
|
+
documentation_presence: float = 0.0 # Max 10.0 (Docstrings / Markdown)
|
|
24
|
+
|
|
25
|
+
# Penalties & Contradiction Caps:
|
|
26
|
+
raw_score: float = 0.0
|
|
27
|
+
contradiction_penalties: float = 0.0
|
|
28
|
+
contradiction_cap: Optional[float] = None
|
|
29
|
+
final_score: float = 0.0 # Clamped 0.0 - 100.0%
|
|
30
|
+
|
|
31
|
+
explanation: str = ""
|
|
32
|
+
contributing_evidence_ids: List[str] = field(default_factory=list)
|
|
33
|
+
active_contradiction_ids: List[str] = field(default_factory=list)
|
|
34
|
+
|
|
35
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
36
|
+
return asdict(self)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ConfidenceBreakdown":
|
|
40
|
+
return cls(**data)
|
context/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project Continuum - Context Selection Subsystem Package
|
|
3
|
+
========================================================
|
|
4
|
+
Milestone 5 - Phases 12 & 13.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from context.models import TaskContext
|
|
8
|
+
from context.selector import TaskContextSelector
|
|
9
|
+
from context.pruner import ContextPruner, ContextPriorityTier, PrunedContextResult
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TaskContext",
|
|
13
|
+
"TaskContextSelector",
|
|
14
|
+
"ContextPruner",
|
|
15
|
+
"ContextPriorityTier",
|
|
16
|
+
"PrunedContextResult",
|
|
17
|
+
]
|
context/models.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project Continuum - Task Context Models
|
|
3
|
+
=======================================
|
|
4
|
+
Milestone 5 - Phase 12: Task-Driven Context Selection.
|
|
5
|
+
Defines the focused, task-scoped representation of project truth.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field, asdict
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from core.state_models import (
|
|
12
|
+
ArchitecturalDecision,
|
|
13
|
+
AstSymbol,
|
|
14
|
+
ContradictionRecord,
|
|
15
|
+
GraphNode,
|
|
16
|
+
TestResult,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class TaskContext:
|
|
22
|
+
"""
|
|
23
|
+
Focused, high-signal project slice containing only information relevant to a target task.
|
|
24
|
+
"""
|
|
25
|
+
task_description: str
|
|
26
|
+
primary_nodes: List[GraphNode] = field(default_factory=list)
|
|
27
|
+
dependency_nodes: List[GraphNode] = field(default_factory=list)
|
|
28
|
+
relevant_files: List[str] = field(default_factory=list)
|
|
29
|
+
relevant_symbols: List[AstSymbol] = field(default_factory=list)
|
|
30
|
+
relevant_tests: List[TestResult] = field(default_factory=list)
|
|
31
|
+
active_constraints: List[str] = field(default_factory=list)
|
|
32
|
+
relevant_decisions: List[ArchitecturalDecision] = field(default_factory=list)
|
|
33
|
+
known_blockers: List[ContradictionRecord] = field(default_factory=list)
|
|
34
|
+
verification_status_summary: Dict[str, Any] = field(default_factory=dict)
|
|
35
|
+
omitted_node_count: int = 0
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
38
|
+
return {
|
|
39
|
+
"task_description": self.task_description,
|
|
40
|
+
"primary_nodes": [n.to_dict() for n in self.primary_nodes],
|
|
41
|
+
"dependency_nodes": [n.to_dict() for n in self.dependency_nodes],
|
|
42
|
+
"relevant_files": self.relevant_files,
|
|
43
|
+
"relevant_symbols": [s.to_dict() for s in self.relevant_symbols],
|
|
44
|
+
"relevant_tests": [t.to_dict() for t in self.relevant_tests],
|
|
45
|
+
"active_constraints": self.active_constraints,
|
|
46
|
+
"relevant_decisions": [d.to_dict() for d in self.relevant_decisions],
|
|
47
|
+
"known_blockers": [b.to_dict() for b in self.known_blockers],
|
|
48
|
+
"verification_status_summary": self.verification_status_summary,
|
|
49
|
+
"omitted_node_count": self.omitted_node_count
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def to_markdown(self) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Formats the focused task context into a high-density, LLM-ready prompt briefing.
|
|
55
|
+
"""
|
|
56
|
+
lines = [
|
|
57
|
+
f"# Task Briefing: {self.task_description}",
|
|
58
|
+
"",
|
|
59
|
+
"## ๐ฏ Primary Components",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
if self.primary_nodes:
|
|
63
|
+
for n in self.primary_nodes:
|
|
64
|
+
lines.append(f"- **{n.name}** (`{n.node_type.value}`): Status `[{n.status.value}]` (Confidence: {n.confidence_score:.0f}%)")
|
|
65
|
+
else:
|
|
66
|
+
lines.append("- *No specific primary nodes identified.*")
|
|
67
|
+
|
|
68
|
+
if self.dependency_nodes:
|
|
69
|
+
lines.append("\n## ๐ Dependencies & Direct Prerequisites")
|
|
70
|
+
for d in self.dependency_nodes:
|
|
71
|
+
lines.append(f"- **{d.name}** (`{d.node_type.value}`): Status `[{d.status.value}]`")
|
|
72
|
+
|
|
73
|
+
if self.relevant_symbols:
|
|
74
|
+
lines.append("\n## ๐งฌ Relevant Code Symbols & Interfaces")
|
|
75
|
+
for s in self.relevant_symbols:
|
|
76
|
+
lines.append(f"- `{s.kind} {s.name}` in `{s.file_path}` (lines {s.line_start}-{s.line_end})")
|
|
77
|
+
|
|
78
|
+
if self.relevant_tests:
|
|
79
|
+
lines.append("\n## ๐งช Verification & Test Status")
|
|
80
|
+
for t in self.relevant_tests:
|
|
81
|
+
status_icon = "โ
" if t.status.value == "VERIFIED" else "โ"
|
|
82
|
+
lines.append(f"- {status_icon} **{t.name}** (`{t.suite}`): `{t.status.value}` (exit code {t.exit_code})")
|
|
83
|
+
if t.error_message:
|
|
84
|
+
lines.append(f" - *Error*: `{t.error_message}`")
|
|
85
|
+
|
|
86
|
+
if self.known_blockers:
|
|
87
|
+
lines.append("\n## โ ๏ธ Known Blockers & Active Discrepancies")
|
|
88
|
+
for b in self.known_blockers:
|
|
89
|
+
lines.append(f"- **[{b.severity}]**: {b.explanation}")
|
|
90
|
+
|
|
91
|
+
if self.active_constraints:
|
|
92
|
+
lines.append("\n## ๐ก๏ธ Active Constraints")
|
|
93
|
+
for c in self.active_constraints:
|
|
94
|
+
lines.append(f"- {c}")
|
|
95
|
+
|
|
96
|
+
if self.relevant_decisions:
|
|
97
|
+
lines.append("\n## ๐ Architectural Decisions")
|
|
98
|
+
for d in self.relevant_decisions:
|
|
99
|
+
lines.append(f"- **{d.title}**: {d.rationale}")
|
|
100
|
+
|
|
101
|
+
lines.append(f"\n*(Project Context Optimized: {self.omitted_node_count} unrelated nodes omitted)*")
|
|
102
|
+
return "\n".join(lines)
|
|
103
|
+
|
|
104
|
+
def prune(self, token_budget: int) -> Any:
|
|
105
|
+
"""Prunes this task context to fit within a specific token budget."""
|
|
106
|
+
from context.pruner import ContextPruner
|
|
107
|
+
pruner = ContextPruner()
|
|
108
|
+
return pruner.prune_to_budget(self, token_budget=token_budget)
|