Skip to content

feat(dataset-quality): count skills in trajectory coverage - #568

Merged
omkargaikwad23 merged 12 commits into
mainfrom
dq-skills-coverage
Aug 14, 2026
Merged

feat(dataset-quality): count skills in trajectory coverage#568
omkargaikwad23 merged 12 commits into
mainfrom
dq-skills-coverage

Conversation

@omkargaikwad23

@omkargaikwad23 omkargaikwad23 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

trajectory_coverage only looked at MCP tools. So a product that ships skills got
graded against a surface its CUJs never name, and a skills-only product fell out of
the grade entirely.

The catalog is now every channel the product declares:

  • MCP tools and skill scripts, matched against expected_trajectory
  • skills themselves, matched against expected_skills

New generators/models/skills_catalog.py reads setup.skills — clones a git URL or
scans a local path, and takes the skill name from SKILL.md frontmatter. An entry
that resolves to nothing raises instead of being skipped, since a short catalog
quietly inflates coverage.

Also renamed the sub-scorer metric keys — dropped the dq_ prefix and fixed names
that no longer matched what they count (total_toolscapabilities_total). They
live inside a JSON blob, so nothing downstream changes.

Test plan

  • pytest test/dataset_quality_test.py -q — 88 passed
  • Live Cloud SQL run: 63 (C), graded on 100/100 weight, no excluded scorers
  • Tested all internal MCP servers and skills agaist available CUJs
  • Config declaring both channels: coverage went 0/155/63

The bump to 30 came from an earlier design with two activation scorers
sharing a 30 budget, which we did not build. Covering more channels makes
the measurement more complete, not more important than error recovery or
composition, and upweighting the lowest-scoring metric lowers every
dataset's grade for no stated reason.

Restores the registry budget to 100.
@omkargaikwad23

Copy link
Copy Markdown
Collaborator Author

/gcbrun

@prernakakkar-google

Copy link
Copy Markdown
Collaborator

The denominator quadruples (0/15 → 5/63 in your own test), so dataset_quality scores for skills-bearing products aren't comparable across this change. What's the plan for the step change on the trends page — new series, or annotate the boundary? Same question for the dq_* rename: code is fine (the viewer renders metrics generically), but historical BigQuery rows keep the old keys, so any saved query over the blob breaks at the boundary.

Comment thread datasets/dataset_quality/example_run_config.yaml Outdated
Comment thread datasets/dataset_quality/example_run_config.yaml Outdated
Comment thread evalbench/generators/models/skills_catalog.py
Comment thread evalbench/generators/models/skills_catalog.py Outdated
raise SkillCatalogError(f"clone failed for {url}: {last_error}")


def _scan_dir(root: str, wanted: set[str] | None = None) -> list[Skill]:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This raises only when zero wanted names match. With skills: ["alpha", "typo", "gamma"] against a root holding alpha/beta/gamma, it returns 2 and stays silent — which is exactly the shrunken denominator the module docstring says it prevents. Can we raise on any unmatched name in wanted, and extend test_a_narrowing_key_matching_nothing_is_fatal to the partial-miss case?

@omkargaikwad23 omkargaikwad23 Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Any unmatched name now raises with all the missing ones listed, and added a test for the partial-miss case.

),
)]
tools = self._fetch_tools(setup)
skills = resolve_skills(setup)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A skills-resolution failure now nulls the entire report, including the five judge sub-scorers that never touch skills. gemini_cli_skills_model.yaml:12 hardcodes an absolute path under one person's home dir — on any other machine that config goes from "grades the tools" to "ungraded." Should a skills failure degrade skill coverage rather than the whole grade?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no separate skills sub-scorer to degrade, trajectory_coverage grades tools, scripts and skills as one union catalog, so a skills failure would shrink denominator and inflate the score rather lower it.
On gemini_cli_skills_model.yaml: that absolute path is worth fixing but it's separate file config change.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that silently shrinking the catalog is worse than failing. But the degradable unit is trajectory_coverage, not a separate skills scorer: _run_scorers already catches a sub-scorer exception, marks it applicable=False, and grading.py reports it in excluded_scorers with the remaining weight. Moving resolve_skills into TrajectoryCoverageScorer.run would drop just that scorer's 20 weight on a bad catalog and still grade the five judges — no inflated denominator, no lost report. As it stands a bad setup.skills path nulls vague_examples, cuj_diversity, error_recovery, composition, and parameter_coverage, none of which touch the catalog.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Split the try. Tool discovery still nulls the report since every sub-scorer reads tool_names, but a SkillCatalogError now just marks trajectory_coverage inapplicable, so it lands in excluded_scorers and the other 80 weight grades as normal. Added a test asserting exactly that.

@omkargaikwad23

Copy link
Copy Markdown
Collaborator Author

The denominator quadruples (0/15 → 5/63 in your own test), so dataset_quality scores for skills-bearing products aren't comparable across this change. What's the plan for the step change on the trends page — new series, or annotate the boundary? Same question for the dq_* rename: code is fine (the viewer renders metrics generically), but historical BigQuery rows keep the old keys, so any saved query over the blob breaks at the boundary.

Thanks for the review @prernakakkar-google!
Only 2 products declare skills today, and one each, so the denominator barely moves for them. Tools-only products are unaffected -> same catalog, same score.

For the dq_ rename, nothing consumes those keys by name; the viewer renders whatever the blob holds.

# standard agent pipeline: a no-op generator does nothing, and the `dataset_quality`
# scorer grades the whole dataset holistically (each sub-scorer computes a 0-100
# sub-score; grading rolls them into a weighted global score + letter grade).
# Grades a product's CUJ configs, not a model: no agent runs and nothing is

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify this to say It helps with Datset CUJ grading.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

stem, suffix = os.path.splitext(filename)
if filename.startswith(".") or filename == "__init__.py":
return False
if suffix.lower() in _NON_SCRIPT_SUFFIXES:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more accurate to check in given list of .js, .ts, .py, .sh

),
)]
tools = self._fetch_tools(setup)
skills = resolve_skills(setup)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that silently shrinking the catalog is worse than failing. But the degradable unit is trajectory_coverage, not a separate skills scorer: _run_scorers already catches a sub-scorer exception, marks it applicable=False, and grading.py reports it in excluded_scorers with the remaining weight. Moving resolve_skills into TrajectoryCoverageScorer.run would drop just that scorer's 20 weight on a bad catalog and still grade the five judges — no inflated denominator, no lost report. As it stands a bad setup.skills path nulls vague_examples, cuj_diversity, error_recovery, composition, and parameter_coverage, none of which touch the catalog.

@prernakakkar-google

Copy link
Copy Markdown
Collaborator

Few minor comments, otherwise LGTM

@omkargaikwad23
omkargaikwad23 merged commit 2768d7c into main Aug 14, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants