Files
lda-wf/docs/historical/superpowers/plans/2026-06-23-agent-challenge-report-projections.md
T

453 lines
15 KiB
Markdown

# Agent Challenge Report Projections Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Generate equivalent bounded Markdown and JSON reports for every V2 trial and regenerate both after audit without mutating raw evidence.
**Architecture:** Add strict Pydantic report DTOs, project one `TrialReport` to Markdown and JSON, wire projection generation after raw result persistence, and make V2 audit rebuild both. Preserve referenced V1 behavior.
**Tech Stack:** Python 3.14, Pydantic v2, PyYAML, pathlib, argparse, pytest, Ruff, basedpyright.
---
## File Structure
- Create `examples/agent_challenges/report_models.py`.
- Modify `examples/agent_challenges/reports.py`, `runner.py`, `run_trials.py`, `audit.py`, and `base-prompt.md`.
- Create `tests/examples/test_agent_challenge_reports.py`.
- Modify `tests/examples/test_agent_challenge_harness_v2.py`.
- Update `docs/current_roadmap.md` and archive this plan.
### Task 1: Define The Normalized Report
**Files:**
- Create: `examples/agent_challenges/report_models.py`
- Modify: `examples/agent_challenges/reports.py`
- Create: `tests/examples/test_agent_challenge_reports.py`
- [x] **Step 1: Write a failing bounded-report test**
Build a `_raw_result(tmp_path)` fixture matching the current V2 result shape,
including explicit paths, identity, challenge report, parsed text, metrics,
policy, tool calls, and deliberately large stdout/output previews.
```python
def test_trial_report_is_bounded_machine_projection(tmp_path: Path) -> None:
payload = build_trial_report(_raw_result(tmp_path), audit=None).model_dump(
mode="json"
)
assert payload["schema_version"] == 1
assert payload["identity"]["challenge_id"] == "fixture"
assert payload["outcome"]["task_outcome"] == "success"
assert payload["commands_and_tools"][0]["detail"].endswith(
"workflow.plan.json"
)
serialized = json.dumps(payload)
assert "large raw stream" not in serialized
assert "full tool output" not in serialized
assert payload["manual_audit"]["status"] == "pending"
```
- [x] **Step 2: Run and verify import failure**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py::test_trial_report_is_bounded_machine_projection -q
```
- [x] **Step 3: Implement strict DTOs**
Create `TrialIdentity`, `TrialOutcome`, `CommandToolBrief`, `TokenSummary`,
`AutomaticEvidence`, `ManualAuditSummary`, and `TrialReport` using
`ConfigDict(extra="forbid")`.
```python
class StrictReportModel(BaseModel):
model_config = ConfigDict(extra="forbid")
class TrialIdentity(StrictReportModel):
challenge_id: str
model: str
variant: str
instruction_profile: str
trial_index: int
repository_commit: str | None = None
repository_dirty: bool | None = None
prompt_hashes: dict[str, str] = Field(default_factory=dict)
raw_result_path: str
workspace_path: str
class TrialOutcome(StrictReportModel):
task_outcome: str
evaluation_validity: str
duration_seconds: float
returncode: int | None
assertion_failures: list[str] = Field(default_factory=list)
parse_errors: dict[str, dict[str, str]] = Field(default_factory=dict)
class CommandToolBrief(StrictReportModel):
ordinal: int
tool: str
status: str
title: str
detail: str | None = None
failed: bool
output_chars: int
output_sha256: str
class TokenSummary(StrictReportModel):
total: int = 0
input: int = 0
output: int = 0
reasoning: int = 0
cache_read: int = 0
cache_write: int = 0
class AutomaticEvidence(StrictReportModel):
step_count: int = 0
tool_call_count: int = 0
failed_tool_call_count: int = 0
tool_counts: dict[str, int] = Field(default_factory=dict)
tokens: TokenSummary = Field(default_factory=TokenSummary)
cost: float = 0.0
unknown_event_count: int = 0
reads_by_category: dict[str, list[str]] = Field(default_factory=dict)
escalated_to_product_code: bool = False
disallowed_reads: list[str] = Field(default_factory=list)
opaque_shell_commands: list[str] = Field(default_factory=list)
class ManualAuditSummary(StrictReportModel):
status: Literal["pending", "complete"] = "pending"
official_outcome: str | None = None
auditor: str | None = None
audited_at: str | None = None
corrections: list[str] = Field(default_factory=list)
notes: str = ""
read_flags: dict[str, bool] = Field(default_factory=dict)
evidence: dict[str, Any] = Field(default_factory=dict)
class TrialReport(StrictReportModel):
schema_version: Literal[1] = 1
identity: TrialIdentity
outcome: TrialOutcome
agent_self_report: dict[str, Any] | None = None
final_agent_answer: str | None = None
commands_and_tools: list[CommandToolBrief] = Field(default_factory=list)
automatic_evidence: AutomaticEvidence
policy_findings: list[str] = Field(default_factory=list)
self_report_discrepancies: list[str] = Field(default_factory=list)
manual_audit: ManualAuditSummary = Field(default_factory=ManualAuditSummary)
follow_up_notes: list[str] = Field(default_factory=list)
```
Identity holds challenge/model/variant/profile/index, repository provenance,
prompt hashes, raw path, and workspace path. Outcome holds task outcome,
validity, duration, return code, assertions, and parser errors. Evidence holds
bounded metrics, read categories, disallowed reads, and opaque commands.
- [x] **Step 4: Implement the builder**
```python
def build_trial_report(
result: dict[str, object],
*,
audit: dict[str, object] | None,
) -> TrialReport:
"""Project immutable raw evidence and optional audit into one report."""
```
Use focused helpers. Require explicit paths; bound final text to 8,000 chars and
command detail to 1,000; retain only tool ordinal/name/status/title/detail,
failure, output size/hash; exclude stdout/stderr/previews/metadata/full input.
Flag observable self-report conflicts. Treat example reads plus
`existing_solution=false` as a manual follow-up, not automatic guilt.
- [x] **Step 5: Verify and commit**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py -q
git add examples/agent_challenges/report_models.py examples/agent_challenges/reports.py tests/examples/test_agent_challenge_reports.py
git commit -m "feat: add normalized agent trial report model"
```
### Task 2: Render And Write Both Projections
**Files:**
- Modify: `examples/agent_challenges/reports.py`
- Modify: `tests/examples/test_agent_challenge_reports.py`
- [x] **Step 1: Add failing projection tests**
Assert writes to `workspace/final-report.md` and `results/trial.report.json`,
stable heading order, bounded commands, no raw outputs, pending audit, valid
JSON, and no temporary-file residue.
- [x] **Step 2: Run and verify missing APIs**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py -k projection -q
```
- [x] **Step 3: Implement Markdown renderer**
Use exactly these headings:
```markdown
# Trial Report
## Outcome
## Agent Self-Report
## Commands And Tool Calls
## Automatic Evidence
## Policy Findings
## Self-Report Discrepancies
## Manual Audit
## Follow-Up Notes
```
Render empty sections explicitly and commands as ordered bounded entries.
- [x] **Step 4: Implement atomic writes**
```python
@dataclass(frozen=True, slots=True)
class TrialReportPaths:
markdown: Path
machine: Path
def _atomic_write_text(path: Path, text: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f".{path.name}.tmp")
temporary.write_text(text, encoding="utf-8")
temporary.replace(path)
def write_trial_report_projections(
report: TrialReport,
*,
markdown_path: Path,
machine_path: Path,
) -> TrialReportPaths:
machine = json.dumps(
report.model_dump(mode="json"), indent=2, sort_keys=True
) + "\n"
markdown = render_trial_report_markdown(report).rstrip() + "\n"
_atomic_write_text(machine_path, machine)
_atomic_write_text(markdown_path, markdown)
return TrialReportPaths(markdown=markdown_path, machine=machine_path)
```
- [x] **Step 5: Verify and commit**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py -q
git add examples/agent_challenges/reports.py tests/examples/test_agent_challenge_reports.py
git commit -m "feat: write human and machine trial reports"
```
### Task 3: Generate Reports From The Runner
**Files:**
- Modify: `examples/agent_challenges/runner.py`
- Modify: `examples/agent_challenges/run_trials.py`
- Modify: `tests/examples/test_agent_challenge_harness_v2.py`
- [x] **Step 1: Extend runner integration tests**
For success and timeout, assert explicit `workspace_path`, `result_path`, and
`report_paths`; raw, Markdown, and machine files exist; machine challenge id is
correct; Markdown contains the final answer.
- [x] **Step 2: Run and verify failure**
```powershell
uv run pytest tests/examples/test_agent_challenge_harness_v2.py -k "runner_to_report or timeout" -q
```
- [x] **Step 3: Add explicit paths before the single raw write**
```python
"challenge_id": challenge.manifest.id,
"workspace_path": str(workspace.root.resolve()),
"result_path": str(result_path.resolve()),
"report_paths": {
"markdown": str((workspace.root / "final-report.md").resolve()),
"machine": str(result_path.with_suffix(".report.json").resolve()),
},
```
Include `reads_by_category` in policy. Write raw result once before projection
generation; never rewrite it.
- [x] **Step 4: Generate pending-audit projections**
Call `build_trial_report(result, audit=None)` and
`write_trial_report_projections`. On failure preserve raw evidence and return a
concise `report_generation_error`.
- [x] **Step 5: Print report paths**
Add `result_path` and `report_paths` to each central-runner summary.
- [x] **Step 6: Verify and commit**
```powershell
uv run pytest tests/examples/test_agent_challenge_harness_v2.py -k "runner or timeout" -q
git add examples/agent_challenges/runner.py examples/agent_challenges/run_trials.py tests/examples/test_agent_challenge_harness_v2.py
git commit -m "feat: generate reports after agent trials"
```
### Task 4: Regenerate Reports After Manual Audit
**Files:**
- Modify: `examples/agent_challenges/audit.py`
- Modify: `examples/agent_challenges/save_manual_audit.py`
- Modify: `tests/examples/test_agent_challenge_reports.py`
- [x] **Step 1: Add failing regeneration test**
```python
paths = save_v2_manual_audit(
result_path,
official_outcome="pass",
auditor="reviewer",
audited_at="2026-06-23T00:00:00Z",
read_overrides={"existing_solution": True},
corrections=["Agent inspected a ready-made workflow plan."],
notes="Technical run passed; self-report corrected.",
)
```
Assert YAML, JSON, and Markdown contain the official grade/corrections.
- [x] **Step 2: Add invalid-audit preservation test**
Use outcome `maybe`, expect `ValueError`, and assert previous projections remain
byte-for-byte unchanged.
- [x] **Step 3: Run and verify missing API**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py -k "manual_audit or invalid" -q
```
- [x] **Step 4: Implement V2 audit writer**
Add:
```python
@dataclass(frozen=True, slots=True)
class V2AuditPaths:
audit: Path
markdown: Path
machine: Path
def save_v2_manual_audit(
result_path: Path,
*,
official_outcome: str,
auditor: str = "human",
audited_at: str | None = None,
read_overrides: dict[str, bool] | None = None,
evidence_overrides: dict[str, object] | None = None,
corrections: list[str] | None = None,
notes: str = "",
) -> V2AuditPaths:
"""Write authoritative audit data and regenerate both report projections."""
```
Allow only `pass`, `fail`, `invalid`; require V2; use explicit raw paths;
validate before writes; atomically write audit; rebuild both projections; never
rewrite raw result.
- [x] **Step 5: Route existing CLI by harness version**
For V2, interpret `--manual-classification` as official outcome, reject
`--from-report`, and print JSON containing all three paths. Preserve referenced
V1 behavior.
- [x] **Step 6: Verify and commit**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py -q
uv run python examples/agent_challenges/save_manual_audit.py --help
git add examples/agent_challenges/audit.py examples/agent_challenges/save_manual_audit.py tests/examples/test_agent_challenge_reports.py
git commit -m "feat: regenerate trial reports after audit"
```
### Task 5: Clarify Shared Self-Reporting Rules
**Files:**
- Modify: `examples/agent_challenges/base-prompt.md`
- Modify: `tests/examples/test_agent_challenge_harness_v2.py`
- [x] **Step 1: Add failing assertions**
Assert the prompt mentions `tests/`, `examples/`, `read.product_code: true`,
`read.existing_solution: true`, and `read.adjacent_attempts: true`.
- [x] **Step 2: Add approved paragraph**
```markdown
Files under `tests/` and `examples/` may contain complete or partial solutions.
If you inspect them, report `read.product_code: true`; also report
`read.existing_solution: true` when they provide a ready-made solution, or
`read.adjacent_attempts: true` when they contain prior trial outputs.
```
- [x] **Step 3: Verify and commit**
```powershell
uv run pytest tests/examples/test_agent_challenge_harness_v2.py -k base_prompt -q
git add examples/agent_challenges/base-prompt.md tests/examples/test_agent_challenge_harness_v2.py
git commit -m "docs: clarify challenge self-report rules"
```
### Task 6: Final Verification And Documentation
**Files:**
- Modify: `docs/current_roadmap.md`
- Move this plan to `docs/historical/superpowers/plans/`.
- [x] **Step 1: Run verification**
```powershell
uv run pytest tests/examples/test_agent_challenge_reports.py tests/examples/test_agent_challenge_harness_v2.py tests/examples/test_agent_challenge_skill_bundle.py tests/examples/test_opencode_browser_click_challenge.py tests/examples/test_report_workflow_challenge.py -q
uv run ruff check examples/agent_challenges tests/examples/test_agent_challenge_reports.py tests/examples/test_agent_challenge_harness_v2.py
uv run ruff format --check examples/agent_challenges tests/examples/test_agent_challenge_reports.py tests/examples/test_agent_challenge_harness_v2.py
uv run basedpyright --level error examples/agent_challenges tests/examples/test_agent_challenge_reports.py tests/examples/test_agent_challenge_harness_v2.py
git diff --check
```
- [x] **Step 2: Smoke CLI help**
```powershell
uv run python examples/agent_challenges/run_trials.py --help
uv run python examples/agent_challenges/save_manual_audit.py --help
```
- [x] **Step 3: Update roadmap and archive**
Record completion, then move this plan to
`docs/historical/superpowers/plans/2026-06-23-agent-challenge-report-projections.md`.
- [x] **Step 4: Review boundaries**
Confirm raw result is written once, machine report excludes raw outputs,
Markdown headings are stable, explicit paths are used, audit regenerates both,
V1 remains intact, and no runbook/branching challenge leaked into this slice.
- [x] **Step 5: Commit completion docs**
```powershell
git add docs/current_roadmap.md docs/superpowers/plans/2026-06-23-agent-challenge-report-projections.md docs/historical/superpowers/plans/2026-06-23-agent-challenge-report-projections.md
git commit -m "docs: record agent challenge report projections"
```