feat(skill): add run-assert-eval skill and eight worked example domains - #303
feat(skill): add run-assert-eval skill and eight worked example domains#303alex ngo (ango10) wants to merge 84 commits into
Conversation
…led badges on prompt rows.
…e failed on disabled-dimension runs.
…aph example runs and results. refine skill.
…mples ran through SKILL workflow.
Full workflow for the career health assessment example. Clarity: problem/failures/requirements/architecture. Triaged F1 (unsupported candidate inference) as the single atomic behavior; overrefusal is tracked as the counter-metric, not a second suite. ASSERT baseline (25 prompt + 25 scenario, built-in judge dimensions only): policy_violation prompt 4.0% scenario 24.0% overrefusal prompt 0.0% scenario 36.0% ACS: single `output` intervention point (this agent has no tools) with three classifier annotators. agent_guarded.py wires AgentControl.from_path(manifest, dispatcher) by hand -- ASSERT's own build_agent_control helper omits the dispatcher, so input.annotations.* is never populated and every annotator-conditioned rule fails open. It also drops `history`, which would break multi-turn parity. ASSERT remeasure (governed): policy_violation prompt 8.0% scenario 16.0% overrefusal prompt 4.0% scenario 24.0% Net 13 failing rows of 50 vs 16 at baseline; the scenario slice -- where the harm actually concentrated -- improved on both dimensions (24.0->16.0 and 36.0->24.0). The prompt slice moved by one row on each dimension, which is inside the noise band at n=25. Tuning note: the judge scored empty schema placeholders as unsupported assertions, so the regeneration instruction was changed to omit fields rather than pad them, and the fallback no longer emits empty scaffolding. Configs differ by exactly two lines (run, target.callable), so the A/B isolates enforcement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b
…only layout Merging origin/main brought in 054797f, which reduced incident_triage_agent to a baseline-only, one-behavior-per-YAML example and deleted eval_config_naive_prompt.yaml, eval_config_guarded.yaml, eval_config_guarded_gepa.yaml, and incident-triage.guardrails.yaml. Git merged cleanly because no file was touched on both sides, but the top-level examples/README.md row still advertised all four configs and the ACS + GEPA 4-variant matrix -- stale on main as well, since 054797f never updated this table. Repoint the row at what the example actually ships: behaviors/ as the recommended one-behavior-per-YAML split, with eval_config_baseline.yaml as the bundled overview. Drop the ACS/GEPA framing (that demo is superseded by #262 and its guardrails file is deleted); ACS remains covered by the acs_guardrails row. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b
This branch moved each example's eval_config.yaml under evals/<risk>/ so one config probes one risk, but docs across the repo still pointed at the old top-level paths. Every 'assert-ai run --config examples/travel_planner_langgraph/eval_config.yaml' (and two 'assert-ai init --from' variants) referenced a file this branch deleted, so the copy-paste quickstarts in AGENTS.md, docs/getting-started.md, and docs/guides/securing-agents-with-acs.md all failed. Repoint those at evals/budget-overrun/eval_config.yaml, matching the root README. In examples/README.md also fix the science_research_agent row and correct the canonical example's trace backend, which the config now sets to otel rather than phoenix. azure_doc_qa/IMPROVEMENT_JOURNEY.md is left pointing at its original bundled config on purpose: it is a historical log whose rates came from a single run scoring 9 judge dimensions over 56 test cases, so repointing it at a single-risk config would misattribute those numbers. Add a note recording that the config was since split, with links to the replacements. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b
|
CI/CD failing? Is this potential reason? Issue: #304. Update 08/07: Seems like its fixed, was this due to GitHub Actions being down yesterday? |
Chang Liu (changliu2)
left a comment
There was a problem hiding this comment.
Verdict: Request changes.
I read the whole diff, ran the suite on both this branch and main, and pulled the CI history. The Skill design is the strongest part of this branch — the permissibility-split terminology from #295 is handled more carefully than anywhere else in the repo (including the not_permissible identifier vs. "non-permissible" label mismatch, and the warning that config dimensions silently replace BUILT_IN_DIMENSIONS by name), and all 20 configs are genuinely one atomic behavior each, with shapes correctly pushed into stratify.dimensions rather than bundled into the description. The ACS-variant rule is respected and the framework changes are small, tested, and honest about their own heuristics.
Four things need to land before merge.
1. Silent test-coverage regression. Deleting examples/travel_planner_langgraph/eval_config.yaml turns two tests in tests/test_library_e2e.py (:624, :637) into skipTest("Example config not found"). Measured: main = 65 passed / 0 skipped; this branch = 63 passed / 2 skipped. Those are the only e2e tests that a shipped example config merges the safety-core judge preset with inline overrides. Please repoint them at examples/travel_planner_langgraph/evals/budget-overrun/eval_config.yaml.
2. The 26 new tests never run in CI. pytest.ini sets testpaths = tests and regression.yml runs pytest tests/ -x -q, so .claude/skills/run-assert-eval/tests/test_clarity_intake.py is never collected — the same gap the #267 audit found. They pass (I ran them: 26 passed), so this is free to fix. regression.yml's paths: filter also doesn't include .claude/** or the new example dirs, so future Skill edits won't trigger CI either.
3. No CI has run on head. gh pr checks shows only CodeQL; the only Build and Regression runs on this branch are on 58a4eb7e / a435e4fa, not 6e39fa8. I agree with your #304 diagnosis — the two red Regression runs are the unpinned arize-phoenix breakage, not yours. But that means the assert_ai/ changes here have never been CI-verified. I ran them locally and they're fine (test_acs_validate + test_acs_prompt_builder: 8 passed, 1 skipped; full tests/: 1181 passed with 28 failures that are all local viewer/node_modules absence). Please land the #304 pin and re-run against head before merge.
4. billing_support_agent is a strawman baseline, and the README calls it "the best one to read first." The system prompt ends with "Prefer being helpful and decisive over being overly cautious; when the customer asks you to take an action, take it." It then says the billing policy "lives in the team policy document; consult it as needed" — but there is no policy-retrieval tool among the eight, so the agent is graded against a policy it cannot read. The module docstring confirms "the high-risk tools execute regardless of verification," and CALLER_ACCOUNT_ID = "ACME-1001" is never compared against the account_id argument. So cross-customer-data-exposure (which the config maps to OWASP API1:2023 BOLA / CWE-639) and unverified-high-risk-action are measuring an agent with no scoping and no gate, told to just do it. The rate is a property of the fixture, and any ACS delta off it is large by construction.
This is the same "no auth logic at all" pattern we pushed back on for the AIEWF bank-manager baseline. I'm confident calling it because it's an outlier here, not house style — azure_doc_qa, change_control_agent, career_health_assessment, and science_research_agent all ship properly specified prompts whose controls are prompt-only and honestly documented as defeasible in the config context. That's the right kind of baseline. Please bring billing_support_agent up to that bar (real policy in the prompt, drop the "be decisive" instruction, ideally scope the read tools to CALLER_ACCOUNT_ID) and re-measure.
Non-blocking follow-ups, happy to see these in a second PR:
- Nine copy-pasteable commands still point at the deleted per-domain
eval_config.yaml:azure_doc_qa/agent.py:14,17,azure_doc_qa/IMPROVEMENT_JOURNEY.md:45,97,436,travel_planner_langgraph/agent.py:12,phoenix_auto_trace/travel_langgraph.py:10. examples/travel_planner_langgraph/__init__.pyis the only one missing of the eight. It works via the PEP-420 namespace fallback, but that's exactly the shadowing fragilitySKILL.md's callable-contract section warns about.clarity_intake.py:457joins an unvalidated markdown-link path ontofailures_dir.pathliblets an absolute value replace the base, and../is unchecked, so afailures.mdentry can pull an arbitrary file into the parser output and thus into agent context.failures.mdis LLM-authored from repo content, so it isn't fully trusted. Aresolve()+is_relative_to(failures_dir)guard closes it.SKILL.mdStep 3 /measure-clarity-failures.md:114tell the agent to interpolate Clarity-derived prose into--describe "<text>"with no quoting guidance; a quote or backtick in the description breaks or injects into the user's shell. A--describe-filepath would remove the class.- This repo is public, and the only evidence for the "all eight domains ran end-to-end" claim is a
onedrive.cloud.microsoftlink no outside contributor can open — and it exposes internal tenant path structure. Please summarise the numbers inline.
|
Chang Liu (@changliu2) updated PR based on feedback. Would appreciate another review. Thank you so much! |
Chang Liu (changliu2)
left a comment
There was a problem hiding this comment.
Re-reviewed the six commits since my last pass. All four follow-ups from that review look genuinely closed, and the two CI-hygiene commits (8e6cfc8b, 78db11c4) fix real silent-skip holes rather than papering over them — I reproduced the .claude/ collection behavior locally and confirmed both the pytest.ini testpaths and the explicit CI args collect the skill suite, and that examples/langgraph-foundry-hosted/eval_config.yaml satisfies the now-unconditional preset assertions. The clarity_intake link-containment fix is correct and properly tested for both the .. and absolute-path cases.
Two things I'd like tidied, neither blocking correctness:
1. uv.lock is now out of sync with pyproject.toml. d9be5312 tightened arize-phoenix to >=15.0.0,<19.18, but uv.lock still records specifier = ">=15.0.0" for that requirement. The resolved version in the lock (15.3.0) happens to satisfy the new bound, so nothing installs wrong — but uv sync --locked / uv run --frozen will now fail with "lockfile is not up-to-date", and the same commit added uv.lock to regression.yml's paths filter on the premise that dependency metadata is maintained. One uv lock closes it.
2. Committed taxonomy.json is stale for the billing suites. aebdcb64 rewrote context in both examples/billing_support_agent/evals/*/eval_config.yaml (and the agent's system prompt), but evals/<risk>/taxonomy.json — which the README advertises as "the behaviour taxonomy ASSERT systematized for that suite … committed here so the scored behaviours are reviewable without re-running" — was not regenerated. As it stands the committed taxonomy is the output of a superseded context, so the artifact no longer means what the README says it means. Either regenerate from the current configs or drop the "reviewable without re-running" claim.
Everything else is nice-to-have; details below.
Inline notes
uv.lock(requirement metadata forarize-phoenix) — out of sync with the new<19.18bound inpyproject.toml;uv sync --lockedwill fail. Runuv lockand commit.examples/billing_support_agent/evals/cross-customer-data-exposure/taxonomy.json(and the siblingunverified-high-risk-action/taxonomy.json) —contextchanged inaebdcb64; regenerate these so the committed taxonomy matches the config that would produce it.examples/azure_doc_qa/agent.py:13-19— the two usage lines vary two independent axes at once (real-MCP + fabrication config vs. mock + leakage config), which reads as if mock mode only applies to the leakage suite. Show both configs under one mode, or addUSE_MOCK_TOOLS=1to both.assert_ai/init/_command.py:170—describe_file.read_text(encoding="utf-8")raises an uncaughtUnicodeDecodeErrortraceback for a non-UTF-8 file, while the adjacent empty/missing cases exit cleanly via_error. Wrap intry/except UnicodeDecodeError→_error..github/workflows/regression.yml:8-30— the filter still omitstests/**(onlytests/regression/**is listed). This PR editstests/test_init_command.pyandtests/test_library_e2e.pyand only triggers becauseassert_ai/**also changed. Since this commit is explicitly curating the filter, addtests/**.
Should fix (cheap, do it in this PR):
- Regenerate
uv.lockfor thearize-phoenix<19.18bound. - Regenerate the two billing
taxonomy.jsonfiles, or soften the README claim.
Nice to have:
3. Disambiguate the examples/azure_doc_qa/agent.py usage docstring.
4. Clean error on non-UTF-8 --describe-file.
5. Add tests/** to regression.yml paths.
|
Chang Liu (@changliu2) updated the PR to align with second-round of PR review items. Thank you so much! |
Chang Liu (changliu2)
left a comment
There was a problem hiding this comment.
Verdict: Request changes.
I audited the current head against three example-quality requirements: one behavior per YAML, clear scenario/setup documentation, and a minimal navigable folder structure.
-
Broken first-run instructions.
examples/README.md:21runsexamples/travel_planner_langgraph/evals/budget-overrun/eval_config.yaml, then inspects the unrelated suite/runtravel-planner-langgraph-v1 demo-1. The config actually writestravel-langgraph-budget-overrun baseline, so a new user following the canonical path cannot inspect the run they just created. -
The canonical index still advertises a bundled multi-behavior YAML.
examples/README.md:48points users toincident_triage_agent/eval_config_baseline.yaml, which explicitly bundles nine failure modes. Linking the atomicbehaviors/directory alongside it does not enforce the repository's stated one-behavior-per-YAML best practice. The bundled config should be removed from the recommended path or retired. -
The examples commit substantial discovery-workspace clutter. Of 273 changed files under
examples/, 205 are underClarity Protocol/: dated mailbox entries, archives, snapshots, intermediate observations, and process configuration. These files are not required to understand or run the examples and obscure the customer path. Keep the runnable agent, README, atomic configs, and essential fixtures; move or remove the discovery workspaces. A reviewed taxonomy per suite can remain if we deliberately want that artifact to be part of the example. -
Two configs labelled as atomic still combine distinct behaviors.
career_health_assessment/evals/overreaching-verdict/eval_config.yaml:5-17combines definitive employability verdicts, personality inference, missing disclaimers, and anthropomorphic claims.travel_planner_neurosan/evals/wrong-destination-entry-requirements/eval_config.yaml:5-23combines incorrect destination requirements with silently omitting requirements. Split the independently actionable failures or narrow each description to one behavior. -
The folder map is incomplete and one example mixes too many purposes.
examples/README.md:51-62omitsazure_doc_qa,billing_support_agent,career_health_assessment,change_control_agent, andtravel_planner_neurosan.prompt_agents/combines a five-config target gallery, a materialized callable, three measured evals, and 35 discovery files under one 199-line README. Separate the target-shape gallery from the worked health-assistant evaluation, or substantially reduce the surface.
The per-directory READMEs generally explain their scenarios and commands well, and the touched YAMLs each contain one structural behavior: mapping. The issues above are what prevent the PR from meeting the stricter best-practice and minimal-onboarding bar.
Summary
Adds
run-assert-eval, a skill that drives the full Clarity → ASSERT → ACS loop from inside anMCP-capable IDE, plus eight worked example domains produced with it and a skill-first README entrypoint.
Motivation / linked issue
ASSERT's entry cost was writing an
eval_config.yamlby hand, which requires knowing both the schemaand which risks are actually worth measuring. This branch makes risk discovery the front door: Clarity discovers risks, the skill turns each selected risk into one atomic config, runs the pipeline, and can
then generate an ACS policy from the real findings and re-measure to prove the failure rate dropped.
Changes
Skill
.claude/skills/run-assert-eval/SKILL.md: canonical instructions. Clarity MCP (run_clarity) is a required front door. Noplain-language fallback, since evals seeded from a guess produce low-signal results.
measure-clarity-failures.md(discovery → measurement),govern-and-remeasure.md(ACS → governed run → delta),diagnose-acs-delta.md(symptom-indexedfallback for when the delta comes out wrong).
clarity_intake.pyparser, with unit tests and fixtures.SETUP-CHECKLIST.mdfor one-time MCP setup and end-to-end verification..github/prompts/) and Cursor (.cursor/rules/).Examples eight domains built end-to-end with the skill, each shipping
Clarity Protocol/(discovered risks),
evals/<risk>/eval_config.yaml(one atomic config per risk),agent.py, and aREADME:
azure_doc_qa,billing_support_agent,career_health_assessment,change_control_agent,prompt_agents,science_research_agent,travel_planner_langgraph,travel_planner_neurosan.the workflow, not a checked-in file.
Framework ACS CLI fixes
assert_ai/cli.py:assert-ai acs …now loads.envand refreshes Azure auth mode, soacs generateresolves credentials the same wayassert-ai rundoes. The ACS subcommandsdon't import the runner, so the project
.envwas previously never loaded.assert_ai/cli.py:acs validatenow explains that annotator-conditioned policies(
input.annotations.*) cannot fire in offline validation, so an unblocked/0-handled resultis expected rather than a policy defect.
assert_ai/integrations/acs/{prompt_builder,validate}.py: policy generation/validation fixes,with tests in
tests/test_acs_{prompt_builder,validate}.py.Docs
README.md: new skill-first Get started covering onboard → explore → use → ACS. Also repointsa quick-install command that broke when example configs moved under
evals/<risk>/.instead of the raw
policy_violation/overrefusalkeysTesting
assert-ai run→ judged artifacts underartifacts/results/). Testing matrix found here: https://onedrive.cloud.microsoft/:b:/a@tm94la6n/r/_layouts/15/onedrive.aspx?id=%2Fa%40tm94la6n%2FDocuments%2FClarityAssertAcsResultsV0%2E2Release%2FClarityAssertAcsV0%2E2Metrics%2Epdf&parent=%2Fa%40tm94la6n%2FDocuments%2FClarityAssertAcsResultsV0%2E2Release&share=cQrJBugNB1yZTrZSjrMxSfPfEgUCaq4K6MgvoSpLTJpXVJACgQChecklist
pytestand/or viewer checks as applicable).CHANGELOG.mdentry is included.