Skip to content

Commit 69f3fea

Browse files
Record final artifact fields in execution reports
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 9a63f43 commit 69f3fea

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

src/code2skill/capabilities/reporting.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from pathlib import Path
44
from typing import Protocol
55

6+
from ..domain.artifacts import ArtifactLayout
67
from ..config import DEFAULT_REPORT_FILENAME, ScanConfig
78
from ..models import ExecutionReport, ImpactSummary
89

@@ -23,6 +24,7 @@ def build_execution_report(
2324
effective_mode: str,
2425
repo_path: Path,
2526
output_dir: Path,
27+
report_path: Path,
2628
inventory,
2729
budget,
2830
changed_files: list[str],
@@ -40,6 +42,12 @@ def build_execution_report(
4042
notes: list[str],
4143
generated_at: str,
4244
) -> ExecutionReport:
45+
layout = ArtifactLayout.from_repo_root(repo_path, output_dir)
46+
bundle_files = [*written_files, report_path]
47+
if config.run.write_state:
48+
bundle_files.append(layout.state_path)
49+
all_written_files = _dedupe_paths(bundle_files)
50+
final_products, intermediate_artifacts = layout.partition_bundle_paths(all_written_files)
4351
return ExecutionReport(
4452
generated_at=generated_at,
4553
command=config.run.command,
@@ -58,7 +66,7 @@ def build_execution_report(
5866
selected_count=len(budget.selected),
5967
total_chars=budget.total_chars,
6068
bytes_read=bytes_read,
61-
written_files=[str(path) for path in written_files],
69+
written_files=[str(path) for path in all_written_files],
6270
updated_files=[str(path) for path in updated_files],
6371
impact=ImpactSummary(
6472
changed_files=changed_files,
@@ -71,4 +79,18 @@ def build_execution_report(
7179
incremental_patch_cost=patch_cost,
7280
pricing=cost_estimator.pricing_dict(),
7381
notes=notes,
82+
final_product_files=[str(path) for path in final_products],
83+
intermediate_artifact_files=[str(path) for path in intermediate_artifacts],
7484
)
85+
86+
87+
def _dedupe_paths(paths: list[Path]) -> list[Path]:
88+
unique: list[Path] = []
89+
seen: set[Path] = set()
90+
for path in paths:
91+
resolved = path.resolve()
92+
if resolved in seen:
93+
continue
94+
seen.add(resolved)
95+
unique.append(resolved)
96+
return unique

src/code2skill/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ class ExecutionReport:
346346
incremental_patch_cost: CostEstimateSummary
347347
pricing: dict[str, Any]
348348
notes: list[str] = field(default_factory=list)
349+
final_product_files: list[str] = field(default_factory=list)
350+
intermediate_artifact_files: list[str] = field(default_factory=list)
349351

350352
def to_dict(self) -> dict[str, Any]:
351353
return asdict(self)

tests/test_reporting.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def test_build_execution_report_includes_run_metadata(tmp_path: Path) -> None:
5656
effective_mode="incremental",
5757
repo_path=tmp_path,
5858
output_dir=tmp_path / ".code2skill",
59+
report_path=tmp_path / ".code2skill" / "report.json",
5960
inventory=inventory,
6061
budget=budget,
6162
changed_files=["app.py"],
@@ -79,3 +80,45 @@ def test_build_execution_report_includes_run_metadata(tmp_path: Path) -> None:
7980
assert report.llm_model == "qwen-plus-latest"
8081
assert report.updated_files == [str(tmp_path / ".code2skill" / "skills" / "backend.md")]
8182
assert report.notes == ["reused incremental state"]
83+
84+
85+
def test_build_execution_report_partitions_final_products_from_intermediates(tmp_path: Path) -> None:
86+
output_dir = tmp_path / ".code2skill"
87+
skill_path = output_dir / "skills" / "backend.md"
88+
plan_path = output_dir / "skill-plan.json"
89+
report_path = output_dir / "report.json"
90+
state_path = output_dir / "state" / "analysis-state.json"
91+
config = ScanConfig(repo_path=tmp_path, output_dir=output_dir)
92+
inventory = SimpleNamespace(discovery_method="git", candidates=[object()])
93+
budget = SimpleNamespace(selected=[object()], total_chars=42)
94+
95+
report = build_execution_report(
96+
config=config,
97+
effective_mode="full",
98+
repo_path=tmp_path,
99+
output_dir=output_dir,
100+
report_path=report_path,
101+
inventory=inventory,
102+
budget=budget,
103+
changed_files=[],
104+
affected_files=[],
105+
affected_skills=[],
106+
generated_skills=["backend"],
107+
written_files=[plan_path, skill_path],
108+
updated_files=[skill_path],
109+
head_commit="deadbeef",
110+
bytes_read=128,
111+
cost_estimator=FakeCostEstimator(),
112+
first_generation_cost=_zero_cost(),
113+
rewrite_cost=_zero_cost(),
114+
patch_cost=_zero_cost(),
115+
notes=[],
116+
generated_at="2026-04-07T00:00:00+00:00",
117+
)
118+
119+
assert report.final_product_files == [str(skill_path)]
120+
assert report.intermediate_artifact_files == [
121+
str(plan_path),
122+
str(report_path),
123+
str(state_path),
124+
]

0 commit comments

Comments
 (0)