Skip to content

Commit b53e571

Browse files
authored
Keep deterministic fallback output evidence-aware (#8)
* fix: soften unsupported certainty in fallback summaries * test: keep fallback language evidence-aware * docs: define the supported assessment generation boundary * test: keep public assessment calls provenance-aware
1 parent a619319 commit b53e571

4 files changed

Lines changed: 87 additions & 1 deletion

File tree

docs/assessment-architecture.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Assessment Generation Boundary
2+
3+
## Supported entry point
4+
5+
Use `src.engine.run_assessment()` for every application, CLI, test, or integration call that produces an assessment.
6+
7+
```python
8+
from src.engine import run_assessment
9+
10+
result = run_assessment(project, mode="dmaic", audience="pm")
11+
```
12+
13+
The engine delegates to `src.assessment_service.run_assessment_with_provenance()`. Every returned `AssessmentResult` records one of two modes:
14+
15+
- `llm`: a live model response was parsed into the required assessment structure.
16+
- `deterministic_fallback`: a predictable local starter assessment was used, with a safe reason explaining why.
17+
18+
Set `require_llm=True` when deterministic fallback would be misleading; the call then raises a concise `AssessmentGenerationError` instead of returning fallback output.
19+
20+
## Boundary rule
21+
22+
The phase module contains low-level prompt, parsing, and deterministic-fallback helpers. It is not an application-facing generation API. New code must not call phase-level model-generation helpers directly, because that would bypass provenance and strict-mode behavior.
23+
24+
## Review rule
25+
26+
A regression test verifies that the supported engine is wired to the provenance-aware service and does not import or call the legacy phase-level generator. This preserves the intended public behavior while allowing the lower-level helper module to be refactored separately.

src/assessment_service.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,39 @@ def _generation_note(mode: str, reason: str | None = None) -> str:
3232
return f"Generation note: deterministic fallback. {suffix}"
3333

3434

35+
def _soften_fallback_certainty(result: AssessmentResult) -> AssessmentResult:
36+
"""Keep deterministic starter output from overstating unvalidated inputs."""
37+
define_items = [
38+
replace(
39+
item,
40+
statement=item.statement.replace(
41+
"Problem confirmed:", "Reported problem statement:"
42+
),
43+
)
44+
for item in result.dmaic_structure.get("define", [])
45+
]
46+
dmaic_structure = {**result.dmaic_structure, "define": define_items}
47+
return replace(
48+
result,
49+
cleaned_problem_statement=result.cleaned_problem_statement.replace(
50+
"has a measurable performance gap:",
51+
"has a reported performance concern:",
52+
),
53+
role_summary=result.role_summary.replace(
54+
"has a confirmed performance gap", "has a reported performance concern"
55+
),
56+
dmaic_structure=dmaic_structure,
57+
)
58+
59+
3560
def _fallback(
3661
project: ProjectInput,
3762
mode: str,
3863
audience: str,
3964
reason: str,
4065
) -> AssessmentResult:
4166
"""Return a deterministic result that records why it was used."""
42-
result = _deterministic_fallback(project, mode, audience)
67+
result = _soften_fallback_certainty(_deterministic_fallback(project, mode, audience))
4368
return replace(
4469
result,
4570
role_summary=f"{_generation_note('deterministic_fallback', reason)}\n\n{result.role_summary}",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from __future__ import annotations
2+
3+
import inspect
4+
5+
import src.engine as engine
6+
from src.assessment_service import run_assessment_with_provenance
7+
8+
9+
def test_engine_delegates_to_provenance_aware_generation_service() -> None:
10+
source = inspect.getsource(engine)
11+
12+
assert engine.run_assessment.__module__ == "src.engine"
13+
assert "run_assessment_with_provenance" in source
14+
assert "run_llm_assessment" not in source
15+
16+
17+
def test_engine_uses_the_supported_generation_service() -> None:
18+
result = engine.run_assessment
19+
20+
assert callable(result)
21+
assert callable(run_assessment_with_provenance)

tests/test_assessment_provenance.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ def test_missing_api_key_is_disclosed_as_deterministic_fallback(monkeypatch) ->
3030
assert result.role_summary.startswith("Generation note: deterministic fallback.")
3131

3232

33+
def test_fallback_does_not_present_unvalidated_input_as_confirmed(monkeypatch) -> None:
34+
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
35+
36+
result = run_assessment(project(), mode="dmaic", audience="executive")
37+
38+
assert "confirmed performance gap" not in result.role_summary
39+
assert "reported performance concern" in result.role_summary
40+
assert "measurable performance gap" not in result.cleaned_problem_statement
41+
assert "reported performance concern" in result.cleaned_problem_statement
42+
assert result.dmaic_structure["define"][0].statement.startswith(
43+
"Reported problem statement:"
44+
)
45+
46+
3347
def test_require_llm_fails_instead_of_silently_falling_back(monkeypatch) -> None:
3448
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
3549

0 commit comments

Comments
 (0)