feat(doctor): change attribution, explain what changed since the last comparable run
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start by reviewing the proposed flow in pipeline/orchestrator.py, pipeline/azd_runner.py, results_history.py, and agent/checks/change_attribution.py. Read the new tests in tests/unit/test_agent_checks_change_attribution.py and the Cockpit tests first. Done means git SHAs and prompt files are recorded, the deterministic Doctor check and /api/version-timeline share the diff summary, and the documented tests pass.
Written by the indexing model from the issue text.
Description
Summary
Doctor can tell you that a metric regressed, but it gives you no way to see why. Today the only path is to manually dig through git history and guess which prompt or agent change lined up with the drop.
This issue proposes a change-attribution feature: record the git commit behind every eval run, then use that to diff the prompt file and agent target between the latest run and its most recent comparable baseline. The result surfaces in two places:
- A new deterministic Doctor check (
change_attribution.prompt_diff) that reports the change in plain language. - A new Cockpit page (
/api/version-timeline) showing the same story across the whole run history, newest first.
No LLM in the loop — the whole thing is deterministic, cheap, and unit-testable without a m
Motivation
- A regression finding wiser to leave the tool toinvestigate.
- We already store enough this possible; we justdon't record which commit produced the run.
- Both Doctor and Cockpitnged" means, so they needto share one diff implementation.
Proposed design
1. Record the git SHA on every eval run
New helper agentops/core/git_info.py::resolve_git_sha(workspace=None):
- Checks CI env vars first (
GITHUB_SHA,BUILD_SOURCEVERSION,
Build.SourceVersion) siys correct in a pipeline. - Falls back to
git rev-parse HEADfor local development. - Never raises: missing
or timeout all return""`.
Every run writer adds two→ config:
| Field | Value |
|---|---|
git_sha |
commit behinknown |
prompt_file |
the prompt file path from the agent config, or null |
Touched writers: pipeline/orchestrator.py (local + cloud snapshots),
pipeline/azd_runner.py,.py.pipeline/prompt_deploy.py::_git_sha()` is refactored to reuse the same helper
(it previously only read
2. Expose the new fie
RunSummary gains git_s, and a newdataset_evaluators_fingerprint.
The new fingerprint hashes (dataset, evaluators) only — deliberately
excluding the agent targethodology_fingerprint`. The regression check needs the target to match; the attribution check needs to
compare runs across an e explaining that bump isthe whole point. It still refuses to compare runs that used a different
dataset or evaluator set, be misleading.
_methodology_fingerprint_dataset_and_evaluators()and _hash_fingerprint_payload() with the new one — no behaviour change.
3. New Doctor check: change_attribution.prompt_diff
agentops/agent/checks/change_attribution.py:
- Takes the latest run and its most recent baseline with the same `dataset_evaluators_finge
- Bails out silently (no finding) when: fewer than
min_runs, no compbaseline, either run has ourced runs), or both runsshare the same SHA. - Runsgit diff <old_shahs>(best-effort, 10 stimeout, returnsNoneon any failure so a Doctor run can never crashgit is unavailable or his - Emits one
INFOfinding underCategory.QUALITYwith a plain-langusummary, e.g.:
> Between these runs, t(s) added, 4 removed) andthe agent target changed: my-agent:1.2.0 -> my-agent:1.3.0.
Evidence carrieslatest_run_id,baseline_run_id,old_git_sha,new_git_sha,diff_summs to the exactgit diffcommand to run.summarize_prompt_diff()is a pure function so it's testable without a real git repo. Config (agentops.agent.cattribution`):
```yaml
checks: change_attribution:
enabled: true min_runs: 2
prompt_paths: [] # files to diff, relative to workspace; falls back to the run's own prompt_file
Registered in checks/cataults_history", "workspace") and wired into analyzer.analyze() right after the regression check.
- New Cockpit page: version timeline
GET /api/version-timeline renders the last 50 runs, newest first. Each row
shows pass/fail badge, ru), timestamp, target, short SHA, and a one-line change summary vs. the run immediately before it:
- "First recorded run."
- "Same commit as the pre
- "No commit recorded for one of these runs - re-run agentops eval run to
start tracking changes. - Otherwise the same summarize_prompt_diff() sentence as the Doctor check.
It reuses _git_diff_stat / summarize_prompt_diff from the check so the two
surfaces never disagree. mplate gets a heading slotand a "version timeline" link in the header; _project_run() now also projects
git_sha, prompt_file, and
Scope of the spike
Files touched (≈730 inser
- src/agentops/core/git_i
- src/agentops/agent/checks/change_attribution.py (new) src/agentops/agent/checzer.py, agent/config.py
- src/agentops/agent/sources/results_history.py src/agentops/agent/cock
- src/agentops/pipeline/{orchestrator,azd_runner,azd_eval_runner,prompt_d}.py
- tests/unit/test_agent_checks_change_attribution.py (new), tests/unit/test_cockpit
t of scope / follow-ups
LLM-generated explanatif means semantically (thischeck is intentionally deterministic; an llm_assist variant could layertop later). - Diffing anything beyond the prompt file and agent target (datasets, evaluator configs, thre
- Attribution for cloud-sourced runs that carry no git_sha. Rendering the actual dintly only the line-countsummary).
Plan - Split the spike into reviewable commits (git SHA recording → resulthistory → Doctor check
- Docs: new check in the Doctor checks reference, change_attribution config block, Cockpit t
- Decide whether prompt_paths should also accept globs
- Open PR against dev
- Dominant language
- Python
- Stars
- 13
- Forks
- 11
- Avg merge
- 3h 42m
- Merged PRs (30d)
- 33
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Azure/agentops
-
Difficulty 5/5 Over a week Newbie friendliness 45/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
Similar issues
-
triage/confirmed
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
agentscope-ai/agentscope#2775 ·
-
comp/desktop P3 type/bug
Difficulty 1/5 Under an hour Newbie friendliness 92/100
NousResearch/hermes-agent#118866 ·
-
bug
Difficulty 1/5 Under an hour Newbie friendliness 90/100
apache/cloudstack#14222 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100