devagent-ai 0.8.1__tar.gz → 0.8.2__tar.gz
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.
- {devagent_ai-0.8.1/devagent_ai.egg-info → devagent_ai-0.8.2}/PKG-INFO +1 -1
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/__init__.py +1 -1
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/tasking.py +128 -4
- {devagent_ai-0.8.1 → devagent_ai-0.8.2/devagent_ai.egg-info}/PKG-INFO +1 -1
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent_ai.egg-info/SOURCES.txt +1 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/pyproject.toml +1 -1
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_production_v040.py +1 -1
- devagent_ai-0.8.2/tests/test_requirement_compiler_v082.py +185 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/LICENSE +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/NOTICE +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/README.md +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/agent/__init__.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/agent/llm.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/agent/loop.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/agent/memory.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/agent/prompts.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/agent/tools.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/__main__.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/artifacts.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/automations.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/autonomy.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/browser.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/cli.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/config.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/discovery.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/evaluation.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/memory.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/models.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/orchestrator.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/provider_benchmark.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/providers.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/qualification.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/realworld.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/report.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/retrieval.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/routing.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/runtime.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/safety.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/skills.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/source_control.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/state_machine.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/technical_review.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/workspace.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent/worktree.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent_ai.egg-info/dependency_links.txt +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent_ai.egg-info/entry_points.txt +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent_ai.egg-info/requires.txt +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/devagent_ai.egg-info/top_level.txt +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/setup.cfg +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_acceptance_contract.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_benchmark_catalog.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_browser_verification.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_capability_discovery.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_cli.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_developer_review_report.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_discovery_memory.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_e2e_fake_provider.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_evaluation_harness.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_evaluation_matrix.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_evaluation_regression_evidence.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_functional_qualification.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_huge_monorepo_v070.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_migration_e2e_v070.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_model_routing.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_multilang_technical_review.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_multistack_devagent_e2e.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_multistack_qualification.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_packaging_metadata.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_plan_verification_normalization.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_preservation_contradiction.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_production_hardening.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_realworld_benchmark.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_retrieval.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_runtime_sandbox.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_safety_workspace.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_source_control_publish.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_structural_devagent_e2e_v070.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_structural_operations.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_structured_provider_contract.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_tasking_state.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_v070_engineering_breadth.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_v080_autonomy.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_v080_provider_benchmark.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_v080_skills_automations.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_workspace_environment.py +0 -0
- {devagent_ai-0.8.1 → devagent_ai-0.8.2}/tests/test_worktree.py +0 -0
|
@@ -63,6 +63,18 @@ _DIRECTIVE = re.compile(
|
|
|
63
63
|
re.IGNORECASE,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
+
# Bounded normalization for terse user intent. This is deliberately not a fuzzy
|
|
67
|
+
# "guess what the user meant" layer: it corrects common engineering shorthand,
|
|
68
|
+
# grammatical number, and operation wording while preserving identifiers,
|
|
69
|
+
# quoted contracts, values, and explicit constraints. Task policy and repository
|
|
70
|
+
# evidence still provide the verification/safety contract.
|
|
71
|
+
_OPERATION_ALIASES: tuple[tuple[str, str], ...] = (
|
|
72
|
+
("substraction", "subtraction"),
|
|
73
|
+
("substract", "subtract"),
|
|
74
|
+
("multipy", "multiply"),
|
|
75
|
+
("mutiply", "multiply"),
|
|
76
|
+
)
|
|
77
|
+
|
|
66
78
|
|
|
67
79
|
def _classify(text: str) -> TaskType:
|
|
68
80
|
lowered = text.lower()
|
|
@@ -98,6 +110,60 @@ def _dedupe(items: list[str]) -> list[str]:
|
|
|
98
110
|
return result
|
|
99
111
|
|
|
100
112
|
|
|
113
|
+
def _normalize_terse_requirement(requirement: str) -> str:
|
|
114
|
+
"""Compile common rough one-line prompts into a clearer engineering request.
|
|
115
|
+
|
|
116
|
+
The compiler is intentionally bounded. It may repair shorthand/grammar and
|
|
117
|
+
make an operation explicit, but it must not add product behavior the user did
|
|
118
|
+
not request. Structured/multi-line requirements are left intact.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
value = re.sub(r"\s+", " ", requirement).strip()
|
|
122
|
+
if not value or "\n" in requirement or _section_header(value) is not None:
|
|
123
|
+
return value
|
|
124
|
+
# An explicit callable name is already a precise user contract; never rename it.
|
|
125
|
+
if re.search(r"\b[A-Za-z_][A-Za-z0-9_]*\s*\(", value):
|
|
126
|
+
return value
|
|
127
|
+
|
|
128
|
+
for source, destination in _OPERATION_ALIASES:
|
|
129
|
+
value = re.sub(rf"\b{re.escape(source)}\b", destination, value, flags=re.IGNORECASE)
|
|
130
|
+
|
|
131
|
+
# Common shorthand from natural prompts such as "addition 2 matrix 2x2".
|
|
132
|
+
# Keep both "matrix" and "matrices" in the normalized contract so
|
|
133
|
+
# deterministic evidence can link either conventional symbol spelling.
|
|
134
|
+
matrix_match = re.search(
|
|
135
|
+
r"\b(add(?:ition)?|sum|subtract(?:ion)?|multiply|multiplication|divide|division)\b"
|
|
136
|
+
r"(?:\s+(?:of|for))?\s+(?:2|two)\s+matrix(?:es)?\s+(\d+x\d+)\b",
|
|
137
|
+
value,
|
|
138
|
+
flags=re.IGNORECASE,
|
|
139
|
+
)
|
|
140
|
+
if matrix_match:
|
|
141
|
+
operation = matrix_match.group(1).lower()
|
|
142
|
+
dimension = matrix_match.group(2).lower()
|
|
143
|
+
canonical_operation = {
|
|
144
|
+
"add": "addition",
|
|
145
|
+
"addition": "addition",
|
|
146
|
+
"sum": "addition",
|
|
147
|
+
"subtract": "subtraction",
|
|
148
|
+
"subtraction": "subtraction",
|
|
149
|
+
"multiply": "multiplication",
|
|
150
|
+
"multiplication": "multiplication",
|
|
151
|
+
"divide": "division",
|
|
152
|
+
"division": "division",
|
|
153
|
+
}[operation]
|
|
154
|
+
prefix = "Add" if re.search(r"\b(add|new|function|implement)\b", value, re.IGNORECASE) else "Implement"
|
|
155
|
+
return (
|
|
156
|
+
f"{prefix} a matrix {canonical_operation} function for two {dimension} matrices "
|
|
157
|
+
f"(matrix inputs)"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Repair simple count+noun shorthand without inventing domain behavior.
|
|
161
|
+
value = re.sub(r"\b2\s+matrix\b", "two matrices", value, flags=re.IGNORECASE)
|
|
162
|
+
value = re.sub(r"\b2\s+file\b", "two files", value, flags=re.IGNORECASE)
|
|
163
|
+
value = re.sub(r"\b2\s+test\b", "two tests", value, flags=re.IGNORECASE)
|
|
164
|
+
return value
|
|
165
|
+
|
|
166
|
+
|
|
101
167
|
def _section_header(line: str) -> tuple[str, str] | None:
|
|
102
168
|
stripped = line.strip()
|
|
103
169
|
markdown = re.match(r"^#{1,6}\s+(.+?)\s*$", stripped)
|
|
@@ -182,15 +248,21 @@ def _append_criterion(
|
|
|
182
248
|
|
|
183
249
|
|
|
184
250
|
def compile_task(requirement: str) -> TaskSpec:
|
|
185
|
-
|
|
186
|
-
if not
|
|
251
|
+
raw_goal = re.sub(r"\s+", " ", requirement).strip()
|
|
252
|
+
if not raw_goal:
|
|
187
253
|
raise ValueError("Engineering requirement cannot be empty")
|
|
254
|
+
goal = _normalize_terse_requirement(requirement)
|
|
188
255
|
task_type = _classify(goal)
|
|
189
256
|
code_change = task_type is not TaskType.UNIT_TEST or "only" not in goal.lower()
|
|
190
257
|
requires_tests = task_type is not TaskType.BUILD_FAILURE
|
|
191
258
|
|
|
192
259
|
criteria: list[AcceptanceCriterion] = []
|
|
193
|
-
|
|
260
|
+
# Structured user requirements remain authoritative. Only an unstructured,
|
|
261
|
+
# terse prompt is compiled into the clearer canonical request.
|
|
262
|
+
user_items = _user_acceptance_items(requirement)
|
|
263
|
+
if len(user_items) == 1 and user_items[0] == raw_goal and goal != raw_goal:
|
|
264
|
+
user_items = [goal]
|
|
265
|
+
for item in user_items:
|
|
194
266
|
_append_criterion(criteria, item, source=AcceptanceSource.USER)
|
|
195
267
|
|
|
196
268
|
if task_type in {TaskType.BUG_FIX, TaskType.RUNTIME_ERROR, TaskType.TEST_FAILURE}:
|
|
@@ -240,9 +312,61 @@ def compile_task(requirement: str) -> TaskSpec:
|
|
|
240
312
|
)
|
|
241
313
|
|
|
242
314
|
|
|
315
|
+
def _repository_language(repository: Any) -> str | None:
|
|
316
|
+
languages = [
|
|
317
|
+
language.lower()
|
|
318
|
+
for component in repository.components
|
|
319
|
+
for language in component.languages
|
|
320
|
+
]
|
|
321
|
+
for preferred in ("python", "java", "csharp", "c#", "javascript", "typescript", "go", "rust"):
|
|
322
|
+
if preferred in languages:
|
|
323
|
+
return preferred
|
|
324
|
+
return languages[0] if languages else None
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _matrix_operation_contract(task: TaskSpec, repository: Any) -> None:
|
|
328
|
+
"""Turn the bounded matrix shorthand compiler output into a repo-style callable contract."""
|
|
329
|
+
|
|
330
|
+
match = re.fullmatch(
|
|
331
|
+
r"(?:Add|Implement) a matrix (addition|subtraction|multiplication|division) "
|
|
332
|
+
r"function for two (\d+x\d+) matrices \(matrix inputs\)",
|
|
333
|
+
task.goal,
|
|
334
|
+
)
|
|
335
|
+
if match is None:
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
operation, dimension = match.groups()
|
|
339
|
+
verb = {
|
|
340
|
+
"addition": "add",
|
|
341
|
+
"subtraction": "subtract",
|
|
342
|
+
"multiplication": "multiply",
|
|
343
|
+
"division": "divide",
|
|
344
|
+
}[operation]
|
|
345
|
+
compact_dimension = dimension.replace("x", "x")
|
|
346
|
+
language = _repository_language(repository)
|
|
347
|
+
if language in {"java", "javascript", "typescript"}:
|
|
348
|
+
symbol = f"{verb}Matrices{compact_dimension}"
|
|
349
|
+
elif language in {"csharp", "c#"}:
|
|
350
|
+
symbol = f"{verb.capitalize()}Matrices{compact_dimension}"
|
|
351
|
+
else:
|
|
352
|
+
symbol = f"{verb}_matrices_{compact_dimension}"
|
|
353
|
+
|
|
354
|
+
compiled = (
|
|
355
|
+
f"Add {symbol}(a, b) to perform element-wise matrix {operation} "
|
|
356
|
+
f"for two {dimension} matrices"
|
|
357
|
+
)
|
|
358
|
+
task.goal = compiled
|
|
359
|
+
user_criteria = [
|
|
360
|
+
criterion for criterion in task.acceptance_criteria if criterion.source is AcceptanceSource.USER
|
|
361
|
+
]
|
|
362
|
+
if len(user_criteria) == 1:
|
|
363
|
+
user_criteria[0].description = compiled
|
|
364
|
+
|
|
365
|
+
|
|
243
366
|
def enrich_acceptance_contract(task: TaskSpec, repository: Any) -> TaskSpec:
|
|
244
|
-
"""
|
|
367
|
+
"""Compile safe repository-aware defaults, then add trusted repository checks."""
|
|
245
368
|
|
|
369
|
+
_matrix_operation_contract(task, repository)
|
|
246
370
|
seen_commands: set[tuple[str, ...]] = set()
|
|
247
371
|
for capability in repository.capabilities:
|
|
248
372
|
if not capability.trusted:
|
|
@@ -67,6 +67,7 @@ tests/test_preservation_contradiction.py
|
|
|
67
67
|
tests/test_production_hardening.py
|
|
68
68
|
tests/test_production_v040.py
|
|
69
69
|
tests/test_realworld_benchmark.py
|
|
70
|
+
tests/test_requirement_compiler_v082.py
|
|
70
71
|
tests/test_retrieval.py
|
|
71
72
|
tests/test_runtime_sandbox.py
|
|
72
73
|
tests/test_safety_workspace.py
|
|
@@ -62,7 +62,7 @@ def test_release_version_is_semver_and_consistent() -> None:
|
|
|
62
62
|
version = match.group(1)
|
|
63
63
|
assert re.fullmatch(r"\d+\.\d+\.\d+", version)
|
|
64
64
|
assert version == __version__
|
|
65
|
-
assert version == "0.8.
|
|
65
|
+
assert version == "0.8.2"
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
def test_release_workflow_requires_green_exact_main_revision() -> None:
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from devagent.models import (
|
|
4
|
+
AcceptanceSource,
|
|
5
|
+
AcceptanceStatus,
|
|
6
|
+
Capability,
|
|
7
|
+
CapabilityProvenance,
|
|
8
|
+
ChangeMetrics,
|
|
9
|
+
CodeSymbol,
|
|
10
|
+
Component,
|
|
11
|
+
DeveloperReviewEvidence,
|
|
12
|
+
Evidence,
|
|
13
|
+
RepositoryModel,
|
|
14
|
+
ReviewDecision,
|
|
15
|
+
TaskType,
|
|
16
|
+
Understanding,
|
|
17
|
+
VerificationResult,
|
|
18
|
+
)
|
|
19
|
+
from devagent.orchestrator import _support_acceptance_criteria
|
|
20
|
+
from devagent.tasking import compile_task, enrich_acceptance_contract
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _repo(language: str = "python") -> RepositoryModel:
|
|
24
|
+
return RepositoryModel(
|
|
25
|
+
root="/repo",
|
|
26
|
+
kind="single-component",
|
|
27
|
+
components=[
|
|
28
|
+
Component(
|
|
29
|
+
path=".",
|
|
30
|
+
languages=[language],
|
|
31
|
+
capabilities=[
|
|
32
|
+
Capability(
|
|
33
|
+
kind="test",
|
|
34
|
+
command=("python", "-m", "pytest", "-q"),
|
|
35
|
+
source="pyproject.toml",
|
|
36
|
+
provenance=CapabilityProvenance.EXPLICIT,
|
|
37
|
+
)
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
],
|
|
41
|
+
facts=[],
|
|
42
|
+
git_head="abc",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _result(command: tuple[str, ...], *, tests: int | None = None) -> VerificationResult:
|
|
47
|
+
return VerificationResult(
|
|
48
|
+
command=command,
|
|
49
|
+
exit_code=0,
|
|
50
|
+
duration_seconds=0.1,
|
|
51
|
+
stdout="passed",
|
|
52
|
+
stderr="",
|
|
53
|
+
classification=None,
|
|
54
|
+
revision=2,
|
|
55
|
+
phase="final",
|
|
56
|
+
tests_run=tests,
|
|
57
|
+
tests_passed=tests,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_rough_matrix_prompt_becomes_precise_python_contract() -> None:
|
|
62
|
+
task = compile_task("add new function addition 2 matrix 2x2")
|
|
63
|
+
assert task.task_type is TaskType.FEATURE
|
|
64
|
+
assert task.goal == "Add a matrix addition function for two 2x2 matrices (matrix inputs)"
|
|
65
|
+
|
|
66
|
+
enrich_acceptance_contract(task, _repo())
|
|
67
|
+
assert task.goal == (
|
|
68
|
+
"Add add_matrices_2x2(a, b) to perform element-wise matrix addition "
|
|
69
|
+
"for two 2x2 matrices"
|
|
70
|
+
)
|
|
71
|
+
user = [item for item in task.acceptance_criteria if item.source is AcceptanceSource.USER]
|
|
72
|
+
assert [item.description for item in user] == [task.goal]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_requirement_compiler_preserves_explicit_user_callable() -> None:
|
|
76
|
+
requirement = "Add matrix_sum(a, b) for two 2x2 matrices"
|
|
77
|
+
task = compile_task(requirement)
|
|
78
|
+
enrich_acceptance_contract(task, _repo())
|
|
79
|
+
assert task.goal == requirement
|
|
80
|
+
user = [item for item in task.acceptance_criteria if item.source is AcceptanceSource.USER]
|
|
81
|
+
assert [item.description for item in user] == [requirement]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_common_typo_is_compiled_without_inventing_behavior() -> None:
|
|
85
|
+
task = compile_task("add new function substraction 2 matrix 2x2")
|
|
86
|
+
enrich_acceptance_contract(task, _repo())
|
|
87
|
+
assert task.goal == (
|
|
88
|
+
"Add subtract_matrices_2x2(a, b) to perform element-wise matrix subtraction "
|
|
89
|
+
"for two 2x2 matrices"
|
|
90
|
+
)
|
|
91
|
+
assert "mutation" not in task.goal.lower()
|
|
92
|
+
assert "invalid" not in task.goal.lower()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_repository_language_selects_conventional_java_callable() -> None:
|
|
96
|
+
task = compile_task("add new function addition 2 matrix 2x2")
|
|
97
|
+
enrich_acceptance_contract(task, _repo("java"))
|
|
98
|
+
assert "addMatrices2x2(a, b)" in task.goal
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_compiled_matrix_contract_links_to_exact_final_symbol_and_tests() -> None:
|
|
102
|
+
task = compile_task("add new function addition 2 matrix 2x2")
|
|
103
|
+
repository = _repo()
|
|
104
|
+
enrich_acceptance_contract(task, repository)
|
|
105
|
+
|
|
106
|
+
pytest_result = _result(("python", "-m", "pytest", "-q"), tests=3)
|
|
107
|
+
diff_check = _result(("git", "diff", "--check"))
|
|
108
|
+
understanding = Understanding(
|
|
109
|
+
problem="Matrix addition is missing.",
|
|
110
|
+
expected_behavior="Add two 2x2 matrices element by element.",
|
|
111
|
+
affected_paths=["calculator.py", "test_calculator.py"],
|
|
112
|
+
root_cause="No matrix addition function exists.",
|
|
113
|
+
evidence=[Evidence("calculator.py contains only existing calculator operations.", ("calculator.py",), 1.0)],
|
|
114
|
+
proposed_solution=["Add the compiled matrix function and regression tests."],
|
|
115
|
+
confidence=1.0,
|
|
116
|
+
)
|
|
117
|
+
review_evidence = DeveloperReviewEvidence(
|
|
118
|
+
changed_symbols=[CodeSymbol("calculator.py", "add_matrices_2x2", "function", 5, "ADDED")],
|
|
119
|
+
test_cases=[
|
|
120
|
+
CodeSymbol("test_calculator.py", "test_add_matrices_2x2", "function", 8, "ADDED"),
|
|
121
|
+
CodeSymbol("test_calculator.py", "test_add_matrices_2x2_negative", "function", 12, "ADDED"),
|
|
122
|
+
],
|
|
123
|
+
test_files=["test_calculator.py"],
|
|
124
|
+
)
|
|
125
|
+
_support_acceptance_criteria(
|
|
126
|
+
task,
|
|
127
|
+
repository,
|
|
128
|
+
understanding,
|
|
129
|
+
ChangeMetrics(2, 20, 0, ["calculator.py", "test_calculator.py"]),
|
|
130
|
+
[pytest_result, diff_check],
|
|
131
|
+
[pytest_result, diff_check],
|
|
132
|
+
ReviewDecision(True, [], "approved"),
|
|
133
|
+
review_evidence,
|
|
134
|
+
["Added the compiled 2x2 matrix addition function and tests"],
|
|
135
|
+
"+def add_matrices_2x2(a, b):\n+ return [[a[r][c] + b[r][c] for c in range(2)] for r in range(2)]\n",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
required = [item for item in task.acceptance_criteria if item.required]
|
|
139
|
+
assert required
|
|
140
|
+
assert all(item.status is AcceptanceStatus.SATISFIED for item in required)
|
|
141
|
+
user = [item for item in required if item.source is AcceptanceSource.USER]
|
|
142
|
+
assert user[0].evidence
|
|
143
|
+
assert any("add_matrices_2x2" in item for item in user[0].evidence)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_compiled_contract_does_not_accept_wrong_operation() -> None:
|
|
147
|
+
task = compile_task("add new function addition 2 matrix 2x2")
|
|
148
|
+
repository = _repo()
|
|
149
|
+
enrich_acceptance_contract(task, repository)
|
|
150
|
+
|
|
151
|
+
pytest_result = _result(("python", "-m", "pytest", "-q"), tests=3)
|
|
152
|
+
diff_check = _result(("git", "diff", "--check"))
|
|
153
|
+
understanding = Understanding(
|
|
154
|
+
problem="Matrix addition is missing.",
|
|
155
|
+
expected_behavior="Add two 2x2 matrices element by element.",
|
|
156
|
+
affected_paths=["calculator.py", "test_calculator.py"],
|
|
157
|
+
root_cause="No matrix addition function exists.",
|
|
158
|
+
evidence=[Evidence("calculator.py contains the current calculator implementation.", ("calculator.py",), 1.0)],
|
|
159
|
+
proposed_solution=["Add matrix behavior and tests."],
|
|
160
|
+
confidence=1.0,
|
|
161
|
+
)
|
|
162
|
+
wrong_review_evidence = DeveloperReviewEvidence(
|
|
163
|
+
changed_symbols=[CodeSymbol("calculator.py", "subtract_matrices_2x2", "function", 5, "ADDED")],
|
|
164
|
+
test_cases=[
|
|
165
|
+
CodeSymbol("test_calculator.py", "test_subtract_matrices_2x2", "function", 8, "ADDED"),
|
|
166
|
+
],
|
|
167
|
+
test_files=["test_calculator.py"],
|
|
168
|
+
)
|
|
169
|
+
_support_acceptance_criteria(
|
|
170
|
+
task,
|
|
171
|
+
repository,
|
|
172
|
+
understanding,
|
|
173
|
+
ChangeMetrics(2, 12, 0, ["calculator.py", "test_calculator.py"]),
|
|
174
|
+
[pytest_result, diff_check],
|
|
175
|
+
[pytest_result, diff_check],
|
|
176
|
+
ReviewDecision(True, [], "approved"),
|
|
177
|
+
wrong_review_evidence,
|
|
178
|
+
["Added a matrix function and tests"],
|
|
179
|
+
"+def subtract_matrices_2x2(a, b):\n+ return [[a[r][c] - b[r][c] for c in range(2)] for r in range(2)]\n",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
user = [item for item in task.acceptance_criteria if item.source is AcceptanceSource.USER]
|
|
183
|
+
assert user[0].status is AcceptanceStatus.UNPROVEN
|
|
184
|
+
assert user[0].evidence == []
|
|
185
|
+
assert "semantically matches" in (user[0].reason or "")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|