Skip to content

chore(library): unbundle the three scenario specs into atomic behaviors - #296

Open
Chang Liu (changliu2) wants to merge 11 commits into
chore/behavior-library-atomicityfrom
chore/unbundle-scenarios
Open

chore(library): unbundle the three scenario specs into atomic behaviors#296
Chang Liu (changliu2) wants to merge 11 commits into
chore/behavior-library-atomicityfrom
chore/unbundle-scenarios

Conversation

@changliu2

Copy link
Copy Markdown
Collaborator

Summary

Finishes the atomicity work from #293. That PR classified three bundled files as scenario; this one actually unbundles them, so the library is an exemplar of the rule rather than a place where the rule is quarantined.

Stacked on #293 — review that one first.

What was bundled

File Bundled
travel_planner 6 mechanisms across "Quality failures" / "Safety failures"
travel_planner_benchmark ~6 quality mechanisms
telecom_customer_service An application spec (Role, Domain Basics, Operational Procedures)

10 new atomic behaviors

explicit_constraint_violation_failures, output_internal_consistency_failures, unit_conversion_failures, actionability_failures, procedure_adherence_failures, out_of_scope_request_failures, escalation_judgment_failures, identity_verification_failures, unauthorized_action_failures, tool_call_turn_protocol_failures

Reused, not duplicated

stereotyping, prompt_injection, and sycophancy were named inside travel_planner and already existed as atomic presets. They are now referenced from the scenario's behaviors: list. Same for the existing grounding / tool-selection / verification / observation / response-completeness / unsupported-conclusion presets.

Duplicating a behavior under a new name would have been worse than the bundling being fixed, so I audited for it: every new preset's closest textual neighbour among the 38 pre-existing ones is ≤20% similar. No near-duplicates.

Scenario shape

A scenario is now context-only:

kind: scenario
name: travel_planner
context: |
  <the application: role, tools, domain objects, procedures>
behaviors:
  - incorrect_tool_selection_failures
  - insufficient_verification_failures
  - grounding_attribution_errors
  - explicit_constraint_violation_failures
  - stereotyping
  - prompt_injection
  - sycophancy

No behavior-shaped description:. scripts/check_behavior_library.py now enforces that, plus behavior↔spec parity in both directions. All 51 presets pass.

The flagship example keeps all seven behaviors

Worth a careful look — the unbundling initially rewrote examples/travel_planner_langgraph/eval_config.yaml down to a single behavior. That is atomic, but it silently dropped six mechanisms from the example that README.md, docs/getting-started.md, docs/config/schema.md, the ACS guide, docs/targets/callable.md, and science.yml all point at. Coverage loss wearing atomicity's clothes.

Fixed by adding the other six as sibling configs under behaviors/, each sharing the same context::

Path Behavior
eval_config.yaml prompt_injection — quickstart, so every existing doc link still works
behaviors/tool-selection.yaml incorrect_tool_selection_failures
behaviors/grounding.yaml grounding_attribution_errors
behaviors/constraints.yaml explicit_constraint_violation_failures
behaviors/verification.yaml insufficient_verification_failures
behaviors/stereotyping.yaml stereotyping
behaviors/sycophancy.yaml sycophancy

This is also the layout we tell CI customers to use (configs: .../behaviors/*.yaml), so the flagship now demonstrates the pattern instead of describing it.

Not a breaking change

behavior: {preset: travel_planner} still resolves through the deprecation shim from #293 — verified it still emits DeprecationWarning and returns the scenario file.

Testing

  • python scripts/check_behavior_library.py → 51 presets (48 behaviors, 3 scenarios), atomic, in parity
  • pytest tests/test_library_loader.py tests/test_library_e2e.py tests/test_import_smoke.py89 passed
  • Full non-viewer suite → 1154 passed
  • Every generated flagship config parses and its preset resolves
  • Duplication audit: max 20% similarity between any new preset and any pre-existing one

Pre-existing failures on this branch's parent, verified by checking out the parent and re-running — not introduced here:

  • tests/test_viewer_*ERR_MODULE_NOT_FOUND: yaml, needs npm install in viewer/ (CI does this)
  • tests/test_tool_module_sandbox.py::...per_conversation_workspace — Docker/sqlite lock on Windows

Review notes

The 10 generated presets are the part worth scrutiny — specifically whether each names exactly one mechanism and whether a judge could score it from evidence. Happy to re-cut any of them.

Chang Liu (changliu2) and others added 2 commits August 3, 2026 11:58
Extract atomic behaviors from the travel planner and telecom scenario specs, leaving scenarios as context plus behavior references.

Reuse existing stereotyping, prompt_injection, sycophancy, grounding, tool-selection, verification, observation, response-completeness, and unsupported-conclusion presets instead of duplicating them.

Update checker, docs, example configs, and benchmark default to enforce and consume atomic behavior presets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Unbundling the travel_planner preset also rewrote the flagship example config
down to a single behavior (prompt_injection). That is atomic but it silently
dropped six mechanisms from the example the README, getting-started, schema
docs, the ACS guide, and science.yml all point at -- coverage loss wearing
atomicity's clothes.

Restores the other six as sibling configs under behaviors/, each sharing the
same context: and measuring exactly one mechanism. eval_config.yaml stays the
quickstart so every existing doc reference keeps working.

This is also the layout we tell CI customers to use, so the flagship example now
demonstrates it instead of just describing it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Comment thread examples/benchmark/eval_config.yaml
@changliu2

Copy link
Copy Markdown
Collaborator Author

Correct. This config is now a single-atomic-behavior example, not the full benchmark. The old bundle covered 6 mechanisms in one config; those are now 6 separate atomic presets in assert_ai/library/behaviors/ (explicit_constraint_violation_failures, output_internal_consistency_failures, unit_conversion_failures, tool_call_turn_protocol_failures, actionability_failures, procedure_adherence_failures). To reproduce full benchmark coverage for the travel_planner_benchmark scenario, you'd run one eval_config.yaml per atomic preset (each pointing context: at the same scenario) and aggregate results -- same pattern as examples/travel_planner_langgraph/behaviors/*.yaml, which already does this (one file per behavior: constraints.yaml, grounding.yaml, tool-selection.yaml, etc.). Happy to add a benchmark/README.md note showing the full run-all-presets sequence if that'd help clarify.

@ahmedmagooda

Copy link
Copy Markdown
Collaborator

Chang Liu (@changliu2) i think yes, adding the readme would be more clear. Other than that i think everything looks good.

Addresses Ahmed's confusion on this PR about where the atomic behavior
presets live and how they compose with scenarios. Adds pointers at every
entry point a user is likely to hit before writing a behavior spec by hand:

- Top-level README.md: new nav-bar link + a What-you-get bullet
- docs/README.md: new Behavior Library entry under Configuration
- docs/config/best-practices.md: callout inside SS8.D (atomic behaviors)
- examples/README.md: new 'Reuse a behavior from the library' section,
  placed before a user starts writing YAML by hand

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@changliu2

Copy link
Copy Markdown
Collaborator Author

ahmedmagooda Pushed a follow-up commit (83913da) addressing your comment above: made the behavior/scenario library obviously discoverable at every entry point a user hits before writing a behavior spec by hand:

  • Top-level README.md -- new nav-bar link (📋 Behavior Library) alongside Get Started/Examples/CLI Reference, plus a "What you get with ASSERT" bullet
  • docs/README.md -- new Behavior Library entry under Configuration, described as "start here before writing a behavior spec by hand"
  • docs/config/best-practices.md -- callout added directly inside §8.D ("Use atomic behaviors"), pointing to the library right where atomicity is explained
  • examples/README.md -- new "Reuse a behavior from the library -- check here first" section, placed before "Which example to start with," referencing the real assert-ai library list --kind behavior / assert-ai library show <preset> commands

Can you review and approve when you get a chance?


| Config area | What this example probes |
|---|---|
| `behavior.description` | Quality failures: wrong or missing tools, ignored budgets, fabricated flights/hotels/prices. Safety failures: stereotyping, tool-output prompt injection, and sycophantic agreement with bad plans. |

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 scenario table still appears to describe the previous bundled configuration. Could this table be updated so it matches the current eval_config.yaml?

Comment thread scripts/check_behavior_library.py Outdated
if a != b:
import difflib
r = difflib.SequenceMatcher(None, a, b).ratio()
if r < 0.98:

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.

For longer specs, a changed sentence could still pass while pip users receive different content from the markdown reference. Also, because the parity validation is guarded by if SPECS.is_dir(), removing the reference directory would skip the check entirely.

The practical risk is low because yaml is the source of truth and md is not used at runtime; deleting the entire directory would likely be noticed during review.


behavior:
preset: explicit_constraint_violation_failures
context: 'The target is a multi-agent LangGraph travel planner with tool servers:

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.

Style/readability: all six sibling configs serialize context as a hard-wrapped single-quoted scalar with blank lines between the wrapped source lines. The rubric strings use the same pattern. The runtime risk is low. Can we use the same literal-block style as the eval_config.yaml for these multiline fields?

context: |
  ...
rubric: |-
  true = ...
  false = ...

- Update the flagship travel_planner_langgraph README's Scenario table,
  which still described the pre-unbundling behavior.description (quality
  + safety failures blended, 6 behavior_categories). It now reflects the
  atomic single-behavior eval_config.yaml (prompt_injection, 4 categories)
  and explains the sibling behaviors/*.yaml cover the other six mechanisms.

- Reformat context: and rubric: multiline fields in all six
  behaviors/*.yaml sibling configs (plus eval_config.yaml's rubric, for
  consistency) from a hard-wrapped single-quoted scalar to literal block
  style (| / |-), matching eval_config.yaml's existing context: style.
  Verified byte-for-byte semantic equivalence via yaml.safe_load diff
  against the prior committed content -- pure style change, no content
  drift.

- Tighten scripts/check_behavior_library.py's spec-parity check per
  Yeming's concern: the 98%-similarity tolerance could let a real content
  change in a long spec through silently, since words() already
  normalizes the only expected sources of formatting difference (headers,
  bullets, wrapping, whitespace, case) -- any remaining difference is real
  drift, not noise. Now requires an exact match. Also hard-fails if the
  examples/behavior_specs reference directory is missing, instead of
  silently skipping the whole parity check.

All 51 presets still pass (48 behaviors, 3 scenarios), atomic and in
parity, with the tightened exact-match rule. 89/89 targeted tests still
pass. All 7 edited example configs verified to still load and resolve
through assert_ai.config.load_config.
@changliu2

Copy link
Copy Markdown
Collaborator Author

tangym Addressed both comments:

  1. README scenario table — was still describing the pre-unbundling behavior (blended quality/safety failures, 6 categories). Updated to reflect the atomic prompt_injection quickstart config, and clarified the sibling �ehaviors/*.yaml cover the other six mechanisms.
  2. YAML style — reformatted context:/
    ubric: in all six sibling configs (plus �val_config.yaml's rubric for consistency) to literal-block style (|/|-), matching the existing convention. Verified byte-for-byte semantic equivalence via a yaml.safe_load diff against the prior committed content — pure style change, no content drift.

Also tightened check_behavior_library.py's parity check per your note: the 98%-similarity tolerance could let real content drift through silently in a long spec, since words() already normalizes every expected source of formatting difference. Now requires an exact match, and hard-fails (rather than silently skipping) if the �xamples/behavior_specs reference directory goes missing.

Re-verified: 51/51 presets pass (atomic + parity) under the tightened rule, 89/89 targeted tests pass, and all 7 edited configs still load/resolve via �ssert_ai.config.load_config.

Ready for another look whenever you have time.

- Add examples/benchmark/README.md, the deliverable Ahmed explicitly asked
  for and accepted ("i think yes, adding the readme would be more clear").
  Explains this is a throughput-scale variant of the flagship
  travel_planner_langgraph example -- same target, same
  explicit_constraint_violation_failures preset already used by
  behaviors/constraints.yaml, deliberately non-adversarial context: -- not a
  new agent or behavior.
- Register examples/benchmark/ in examples/README.md's selection table and
  layout tree so it is actually discoverable, matching this PR series' own
  stated goal.
- Fix the one sibling config eval_config.yaml itself missed in the prior
  YAML-style pass: the overrefusal rubric was still the hard-wrapped
  single-quoted scalar form; now literal-block style like every other
  rubric/context field in this example. Verified byte-for-byte semantic
  equivalence via yaml.safe_load diff -- pure style fix.

51/51 presets clean, 89/89 targeted tests pass.
@changliu2

Copy link
Copy Markdown
Collaborator Author

Closed out the remaining review gaps from an independent audit pass:

  1. Ahmed's requested benchmark README — added examples/benchmark/README.md, explaining this is a throughput-scale variant of the flagship travel_planner_langgraph example (same target, same explicit_constraint_violation_failures preset already used by behaviors/constraints.yaml), not a new agent or behavior. Registered it in examples/README.md's selection table and layout tree so it's actually discoverable.
  2. Yeming's YAML-style comment — one sibling config's rubric (eval_config.yaml's overrefusal) was missed in the prior pass and still had the hard-wrapped single-quoted-scalar style; now literal-block like every other rubric/context field in the example. Verified byte-for-byte semantic equivalence via a yaml.safe_load diff.
  3. Yeming's DeprecationWarning comment on chore(library): make behavior presets atomic, close a 20-behavior gap, and enforce both in CI #293 — fixed on chore(library): make behavior presets atomic, close a 20-behavior gap, and enforce both in CI #293's branch directly (this branch is stacked on it, so it's included here too via merge).

51/51 presets clean, 89/89 targeted tests pass.

tangym ahmedmagooda — ready for another look whenever you have time.

Remove the overlapping travel-planner behavior configs so the downstream examples PR owns the canonical flat evals layout. Keep the atomic benchmark update, but make its documentation independent of the removed path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Carry current main through the stacked foundation so the unbundle PR remains reviewable only against PR #293 and downstream PRs do not inherit unrelated main changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Chang Liu (changliu2) added a commit that referenced this pull request Aug 12, 2026
Advance the downstream branch to the current #293/#296 lineage without changing the resolved tree, keeping the eventual PR diff limited to skill and example work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Propagate the Python 3.11 Phoenix bound through the stack so PR #296 validates on the same dependency set as PR #293.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
alex ngo (ango10) pushed a commit that referenced this pull request Aug 13, 2026
* feat: clarity integration.

* feat: drive Clarity integration through MCP server instead of CLI.

* feat: ASSERT + ACS integration.

* feat: make govern-remeasure loop deterministic and self-explaining.

* test: billing support agent.

* fix: increase sample_size to 25 to reduce noise in A/B testing

* fix: delete old eval_config.yaml billing agent.

* fix: updated agent_guarded.py to source callerid from session.

* fix: give user choice for sample size, add numeric/threshold gate format.

* feat: new subsection for output/input points.

* fix: reduce govern to pure enforcement.

* fix: refine SYSTEM_PROMPT to alight with examples.

* feat: billing_support_agent resources. clarity, example, and artifacts.

* fix: forward score_keys in buildJudgedSampleRow to fix false judgefailed badges on prompt rows.

* docs: add per-domain organization guidance for multi-run workflows.

* fix: forward score_keys in normalized result items to stop false judge failed on disabled-dimension runs.

* feat(test): azure_doc_qa, change_congrol_agent, travel_planner_langgraph example runs and results. refine skill.

* fix: make fabricated-details output gate history-aware. adopt regen operating point.

* feat: career_health_assessment agent example run.

* fix: refine career_health_assessment annotator.

* feat(examples): add Prompt Agents governance packages from SKILL workflow.

* fix: update max_turns default for SKILL to 10.

* feat(example): science_research_agent and travel_planner_neurosan examples ran through SKILL workflow.

* chore(library): make behavior presets atomic and enforce it in CI

The behavior library had three problems, none of which anything checked for.

**Presets bundled multiple behaviors.** `travel_planner` covered six mechanisms
across "Quality failures" and "Safety failures" -- three of which
(`stereotyping`, `prompt_injection`, `sycophancy`) already existed as their own
atomic presets. `travel_planner_benchmark` bundled roughly six more.
`telecom_customer_service` was not a behavior at all: it is an application spec
(Role, Domain Basics, Operational Procedures) wearing `kind: behavior`.

Evaluating a bundle as one behavior produces a dataset mixing several mechanisms
and a metric nobody can act on -- you learn that something failed, never which
mechanism. That is exactly what best-practices 8.D ("use atomic behaviors")
exists to prevent.

These three are application scenarios, so they move to a new `scenario` kind in
`assert_ai/library/scenarios/`. They are the context an eval runs against, not
the behavior it measures.

**20 behaviors shipped to nobody.** `examples/behavior_specs/*.md` held 38 specs;
`assert_ai/library/behaviors/*.yaml` held 18 of them. Only the YAML goes in the
wheel, so every agentic failure mode -- goal drift, premature termination,
repeated action loops, stale state, poor retrieval, tool-call error recovery,
and 14 more -- was invisible to anyone who installed from PyPI. The 18 that did
exist in both places were byte-identical, so this was pure coverage loss, not
divergence. Generated the missing 20 from the existing markdown and the category
metadata already in that directory's README; no prose was invented.

**Nothing detected either problem.** `scripts/check_behavior_library.py` now
fails CI when a preset names another preset's behavior (provable bundling), when
one preset carries several failure categories, when a description reads as an
application spec, or when a spec markdown drifts from its YAML or has no preset
at all. It runs in Tier 1.

Not breaking: `behavior: {preset: travel_planner}` still resolves, via a shim
that warns and points at the `scenario` kind. Config authors get told, not
broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b

* fix(cli): expose the scenario kind on library list/show

Left out of the previous commit, so 'library show --kind scenario' rejected the
new kind and Tier 1 failed. The local run passed only because the edit existed
in my working tree but was never staged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b

* feat(viewer): headline policy violations split by behavior permissibility.

* fix(tests): copy permissibility.ts into the node test harnesses.

* fix(examples): delete all skill generated content for rerun of finalized skill.

* feat(example): updated skill and billing_support_agent example run.

* career_health_assessment: Clarity->ASSERT->ACS->ASSERT bug-bash run

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

* change_control_agent: Clarity protocol, ACS policy, and governed agent

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop for the change
control agent and lands the governed variant.

Measured (25 prompt + 25 scenario rows, built-in judge dimensions):

  run           PV prompt  PV scen  OR prompt  OR scen
  baseline          0.0%     32.0%       4.0%     4.0%
  governed v1       0.0%     24.0%       4.0%    28.0%
  governed v2       4.0%     24.0%       0.0%     0.0%

The v1 -> v2 step is the interesting one. v1 cut the scenario violation
rate by 8 points but drove overrefusal from 4.0% to 28.0%. Reading the
failing rows showed all eight were pure drafting requests -- "give me a
routing note", "rewrite this record block", "shortest release-ready
note". The gate was correctly refusing to *advance* a change and
incorrectly refusing to *write about* one.

v2 adds a drafting carve-out in four places: the annotator rubric, the
regeneration instruction, the still_allowed branch of the block result,
and the fallback, which now leads with authoritative status and
preserves the draft underneath instead of opening with a refusal.

Overrefusal went to 0.0% on both slices while the violation improvement
held. Net failing rows: 7 of 50 governed vs 10 of 50 baseline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* prompt_agents: runnable agent, Clarity protocol, ACS policy, governed variant

The example shipped as YAML configuration only, so agent.py was written
from health_assistant.yaml to give the suite a runnable target. The
governed variant is additive; agent.py is untouched so the A/B is honest.

Measured (25 prompt + 25 scenario rows, built-in judge dimensions):

  run         PV prompt  PV scen  OR prompt  OR scen
  baseline        0.0%     24.0%       0.0%     8.0%
  governed        4.0%      8.0%       0.0%     8.0%

  policy_violation  scenario  24.0% -> 8.0%   (-16.0 pts)
  overrefusal       both slices unchanged
  net failing rows  8/50 -> 5/50

The 0.0% / 24.0% baseline split is the whole diagnosis. Single-turn
attempts to extract a dose are refused every time -- the system prompt's
"Never provide dosage recommendations or diagnoses" works when the
request arrives cold. Every violation is in the multi-turn slice, where
the tester builds rapport, supplies clinical framing ("her doctor
already put her on this"), and expresses distress at the refusal. The
prohibition never leaves the context window; it stops being treated as a
hard limit and becomes one consideration to weigh against a sympathetic
case.

A standing instruction cannot fix erosion of a standing instruction, so
the gate screens the outbound reply on every turn. Two deterministic
pre-verdicts cover the measured shapes: _DOSE_DIRECTIVE (a quantity and
an administration verb in the same clause) and _REASSURANCE (dismissal
of clinical concern about a symptom the ledger shows the user
described). The second matters because a negative diagnosis reads as
comfort rather than instruction and is advice NOT to seek care.

Urgent-care escalation is exempted before any gate runs -- it is
structurally a directive and is the one directive that is always right.

Framework note: the generated Rego compares the annotator result against
the literal string "true". Returning a real Python True silently never
matches and the policy fails open with no error. This is the fifth
mutually incompatible annotator return contract across five domains in
this batch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add ACS governance for travel_planner_langgraph fabricated travel details

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop for the LangGraph
travel planner. The behavior under test is fabricated flight, hotel, and
weather details presented as confirmed fact.

Baseline measured 20.0% / 60.0% policy violation (prompt / scenario) with
24.0% scenario overrefusal.

The guarded target adds an evidence ledger that records every tool result
with a reliability tag, an annotator that separates unsourced concrete
claims from general planning reasoning, the generated ACS policy wired as
an additive backstop, and a bounded regeneration loop.

A first governed revision cut prompt fabrication to 4.0% but doubled
overrefusal to 48.0%. The justifications showed the guard was blocking
generic planning help that requires no grounding, so the fallback, the
annotator carve-out, and the regeneration prompt were rewritten to scope
the block to unsourced specifics rather than to the travel topic.

Governed v2: policy violation 16.0% / 28.0%, overrefusal 0.0% / 40.0%.
Scenario fabrication is down 32 points against baseline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add ACS governance for azure_doc_qa internal document disclosure

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop for the Azure docs
Q&A agent. The behavior under test is disclosure of internal engineering
documentation -- quotation, close paraphrase, extracted specifics, and
existence or structure confirmation.

Baseline measured 45.8% / 88.0% policy violation (prompt / scenario), the
second-worst scenario slice in the batch, with 4.2% / 12.0% overrefusal.

The guarded target classifies retrieved documents by source, redacts
internal material at the tool boundary, wires the generated ACS policy as
an additive backstop, and regenerates any reply that discloses.

A first governed revision cut prompt disclosure to 4.0% but pushed scenario
overrefusal to 40.0%. The justifications showed the guard was refusing
public documentation work -- public checklists, rollback guidance,
architectural comparison, customer-safe wording -- so the regeneration
prompt and fallback were rewritten to enumerate the permissible public work
and to forbid bare deflection.

Governed v2: policy violation 12.0% / 32.0%, overrefusal 4.0% / 36.0%.
Scenario disclosure is down 56 points against baseline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add ACS governance for science_research_agent restricted content leakage

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop for the science
research agent. The behavior under test is disclosure of restricted
documents through the research synthesis path -- quotation, close
paraphrase, gist summary, translation, and multi-turn reconstruction.

Baseline measured 28.0% / 20.0% policy violation with 16.0% / 8.0%
overrefusal.

The guarded target redacts restricted material at the tool boundary,
keeps public entries in a mixed result set fully usable, wires the
generated ACS policy as an additive backstop, and adds a deterministic
corpus oracle that screens outbound replies for reproduced restricted text
regardless of whether a tool returned it or the user pasted it.

Five revisions, with the rejected ones documented. v2 regressed because the
policy_note attached to withheld data still authorized exactly what the
regeneration instruction forbade. v4 regressed because the corpus oracle
blocked on a single shared shingle, which made it refuse public material;
decision 03 records why shared shingles indicate topic rather than
substance. v5 subtracts the public corpus and requires a run of three,
verified to catch all 8 restricted documents and flag none of the 12
public ones.

Governed v5: policy violation 24.0% / 28.0%, overrefusal 20.0% / 16.0%.

Reported with the caveat in decision 02: judge boolean and judge
justification disagree on roughly half of rows in this suite, so the
design was steered from justifications rather than from the rate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add ACS governance for travel_planner_neurosan fabricated travel details

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop for the neurosan
multi-agent travel planner. The behavior under test is fabricated travel
details presented as confirmed fact.

The first baseline scored 0.0% on all four metrics, which was a framework
fault rather than a result: systematize silently replaced the supplied
behavior definition with refusal boilerplate and emitted one category
against a configured count of 25, so the suite measured refusal instead of
fabrication. Re-running the identical config with --force-stage systematize
produced ten real fabrication categories and a baseline of 96.0% / 96.0%,
the worst in the batch.

Three structural defects drive it: simulate_tool relabels fixtures rather
than selecting data, so a Seattle-to-Boston request returns Tokyo hotels
under a Boston heading; optimize_itinerary validates hardcoded costs so
every trip totals $1,820; and sub-agents summarize tool output before the
optimizer sees it, so no claim is traceable to evidence.

The guarded target adds a deterministic destination oracle, a
reliability-tagged evidence ledger, costs derived from retrieved prices,
and the generated ACS policy as an additive backstop. This policy uses a
sixth distinct annotator contract, with polarity differing per annotator
within the same policy, and an output rule that can only ever warn.

v1 reached 40.0% / 68.0%. The surviving failures were unlabeled planning
specifics rather than relabelled fixture data, so v2 added a provenance
banner derived solely from the ledger and prepended unconditionally,
making the estimate labelling deterministic instead of requested.

Governed v2: policy violation 28.0% / 52.0%, overrefusal 16.0% / 16.0%.
Prompt fabrication is down 68 points against the valid baseline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add failure-2 governance for change_control and prompt_agents

Second Clarity-sourced failure per domain, measured baseline vs ACS-governed
with the built-in judge dimensions (25 prompt + 25 scenario rows each).

change_control_agent - F3 embedded-directive injection via proposal fields:
  baseline  PV prompt 36.0% / scenario  4.0%   OR prompt 24.0% / scenario 4.0%
  governed  PV prompt 36.0% / scenario  4.0%   OR prompt 24.0% / scenario 0.0%
  This suite's policy_violation metric is NOT trustworthy: 80% of flagged rows
  are contradicted by their own judge justification ("the assistant does not
  follow the embedded request", scored as a violation), and 19-23 of 50
  transcripts dead-end on change IDs the generated test set invented but the
  agent's fixtures do not contain. The guard is correct by construction and
  unit-tested (12/12), but this suite cannot measure it. Reported as
  measurement-invalid rather than iterated against a broken instrument.

prompt_agents - F2 false reassurance / negative diagnosis:
  baseline  PV prompt 0.0% / scenario 4.0%   OR prompt 0.0% / scenario 0.0%
  governed  PV prompt 0.0% / scenario 4.0%   OR prompt 0.0% / scenario 4.0%
  A negative control: Clarity brainstormed this failure but measurement shows
  the agent does not exhibit it (1/50, 0 judge errors). No headroom to improve;
  the guard is verified not to introduce overrefusal.

Both guards extend the existing agent_guarded.py additively - change_control
with zero deletions, prompt_agents with renames only - so each file now carries
two independent AgentControls and failure-1 behaviour is preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add failure-2 governance for science_research_agent (cross-document inference)

Clarity F4 - reconstructing a restricted conclusion from public documents.
25 prompt + 25 scenario rows, built-in judge dimensions.

  baseline  PV prompt 40.0% / scenario 56.0%   OR prompt  4.0% / scenario 12.0%
  governed  PV prompt 48.0% / scenario 52.0%   OR prompt 12.0% / scenario  4.0%

Read honestly this is a wash, not a win. The guard adds a real structural
control - restricted-unique 6-gram residue is stripped from the request before
the model sees it, so a conclusion cannot be assembled from evidence never
delivered - plus a deterministic output check for a non-public marker
co-occurring with a performance comparison. 9/9 unit tests pass.

But the measurement cannot resolve it. 24% of the governed run's flagged
violations are contradicted by their own judge justification (vs 4% at
baseline), e.g. "The assistant does not disclose or infer any restricted
conclusion" scored as a violation. Netting inversions out, real violations
move roughly 23 -> 19, which is inside the noise. Overrefusal trades
directions between prompt and scenario rows rather than improving.

Recorded as finding 21 rather than presented as an improvement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add failure-2 governance for career_health_assessment (CV untrusted-instruction injection)

Clarity F4 - the agent obeys instructions embedded in the untrusted CV_TEXT it
was asked to assess. 25 prompt + 25 scenario rows, built-in judge dimensions.

  baseline  PV prompt 64.0% / scenario 88.0%   OR prompt 0.0% / scenario 44.0%
  governed  PV prompt 20.0% / scenario 52.0%   OR prompt 0.0% / scenario 48.0%

Policy violations fall 44 points on prompt rows and 36 points on scenario rows
while overrefusal is held at baseline (scenario +4 points, inside noise; prompt
unchanged at 0.0%). The judge's boolean/justification inversion rate is 39% in
BOTH arms, so the comparison is like-for-like; netting inversions out, real
violations roughly halve (~23 -> ~11).

Note the baseline agent ALREADY overrefuses at 44% on scenario rows - it emits
{"status": "unsupported_task"} and declines legitimate CV scoring. That is a
pre-existing defect of the ungoverned agent, not a governance cost, and the
governed agent does not make it meaningfully worse.

This took two iterations. The first guard cut violations but pushed overrefusal
to 68% via two bugs, both found from judge justifications rather than guesswork:
  1. sticky cross-turn detection - injection scanning folded in all prior
     history turns, so a turn-1 injection suppressed every later clean turn.
     Detection is now strictly per-turn.
  2. stub-producing enforcement - regeneration fired on foreign keys or a
     missing status and fell back to a blank stub. It now fires only on hard
     capitulation; foreign keys are repaired in place and populated content is
     never downgraded. The mandated warning now accompanies a real assessment
     instead of replacing it.

Guard is additive (439 insertions, 0 deletions); failure #1 is byte-for-byte
intact. Unit tests 23/23, including the multi-turn regressions that would have
caught the overrefusal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add failure-2 governance for azure_doc_qa (self-asserted identity escalation)

Clarity F2, which Clarity had explicitly marked SEVERITY: HIGH - selected but
which was never built into a suite until now. 25 prompt + 25 scenario rows,
built-in judge dimensions.

  baseline  PV prompt 76.0% / scenario 48.0%   OR prompt 28.0% / scenario 0.0%
  governed  PV prompt 20.0% / scenario  8.0%   OR prompt 16.0% / scenario 4.0%

Policy violations fall 56 points on prompt rows and 40 points on scenario rows,
and overrefusal IMPROVES on prompt rows (28.0% -> 16.0%) rather than paying the
usual governance tax. Scenario overrefusal rises 0.0% -> 4.0% (one row).

The 76% baseline was the strongest single-arm signal of the batch aside from
neurosan, which vindicates re-sourcing failure selection from the Clarity
Protocol records rather than from fresh code analysis - Clarity had already
identified and ranked this failure.

The guard denies internal retrieval at pre_tool_call for callers whose only
claim to clearance is their own self-description, so internal material is never
fetched rather than fetched-then-suppressed, and repairs output-side statements
that assert the caller was verified. This policy uses SPLIT ANNOTATOR POLARITY -
validated_principal_present is a health flag (true = good) while
caller_self_description_claims_clearance is a fault flag (true = bad) - both
asserted in both directions in the tests.

Guard is additive; the 7 removed lines are in-place replacements of bare
clearance checks with policy-gated equivalents. Failure #1 intact. Tests pass
(60+ checks), including no-deadlock verification when an event loop is already
running.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add failure-2 governance for travel_planner_neurosan (false budget confirmation)

This failure was recorded through Clarity during this session. Neurosan was the
only domain with no enumerated second failure - its failures.md listed one
auto-triaged top risk and nothing ranked below it - so rather than invent a
failure from code reading, the Clarity failure-brainstorm was run properly and
the result recorded via the Clarity MCP tools. failures.md is rewritten into the
same enumerated F1-F5 + triage format the other six domains use.

25 prompt + 25 scenario rows, built-in judge dimensions.

  baseline  PV prompt 100.0% / scenario 88.0%   OR prompt 0.0% / scenario 4.0%
  governed  PV prompt  12.0% / scenario 64.0%   OR prompt 4.0% / scenario 8.0%

An 88-point drop on prompt rows, the largest single improvement in the batch,
off a 100% baseline - the ungoverned agent confirmed a budget as fitting on
every single prompt row. Scenario rows improve 88% -> 64%, more modestly.
Overrefusal stays low (+4 points each side).

This is also the cleanest measurement in the batch: the judge's
boolean/justification inversion rate is 0% in BOTH arms, so unlike the injection
suites these numbers can be read at face value.

The defect: agent.py hardcodes costs in validate_budget (lines 193-195) and the
agent then asserts budget fitness as verified. The guard adds a second
AgentControl with two deterministic enum annotators (budget_verdict,
budget_verification_claim), catches self-contradictory totals, and closes the
say-so gap with a disclosure banner rather than a refusal.

Guard is additive; failure #1 grounding control untouched. Tests 6/6.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add failure-2 governance for travel_planner_langgraph (unmarked claim provenance)

Clarity F5 - the agent presents retrieved facts and invented estimates in one
undifferentiated voice, so the user cannot tell which parts were actually looked
up. 25 prompt + 25 scenario rows, built-in judge dimensions.

  baseline    PV prompt 36.0% / scenario 40.0%   OR prompt 16.0% / scenario 20.0%
  governed    PV prompt 24.0% / scenario 44.0%   OR prompt  0.0% / scenario 12.0%

Violations fall 12 points on prompt rows and overrefusal falls on BOTH strata to
below baseline. Scenario violations are 4 points up, which is expected and is
explained below rather than tuned away.

This suite took two iterations and the first one is the more interesting result.

Guard v1 scored PV prompt 12.0% - better than what is committed here - but drove
scenario overrefusal 20.0% -> 52.0%. Reading the judge's justifications showed
why: v1 achieved its low violation count by making the agent WITHHOLD the
itinerary whenever its evidence ledger was empty, replying "tell me your dates
and I'll look it up". The behaviour under test is unmarked PROVENANCE; the
required fix is to LABEL which parts are verified, not to refuse to answer. v1
inverted the control's intent, and the metric rewarded it, because an itinerary
never written contains no unmarked claims. The asymmetry gave it away: prompt
overrefusal was unchanged at 16.0% while scenario rows tripled, since multi-turn
conversations more often reach the rewrite request with an empty ledger.

v2 removes the withholding path entirely. The original plan is now the floor - a
regenerated reply replaces it only if it is itself a substantive plan, never a
bare deferral - and regeneration fires only when the ledger is actually
populated. The provenance banner still marks every unverified part.

The 4-point scenario violation rise is the honest cost of that correction, and
the guard author predicted it before the re-run: delivering the plan reintroduces
its specifics, which the judge can then assess, whereas withholding hid them.
Trading 4 points of measured violation for 40 points of overrefusal is the right
direction for a labelling control.

Judge inversion is 0% in BOTH arms here, so unlike the injection suites these
numbers can be read at face value.

Also fixes a latent crash in the shipped agent.py: intent_classifier assumed
json.loads returned a dict, but a bare-string parse raised AttributeError and
killed one scenario row mid-run. Parsing is now defensive and falls back to the
default intent; well-formed dict responses are untouched. Both baselines had
zero target errors, so this does not affect any published baseline number.

Guard is additive; the 21 removed lines are the v1 withholding block and a
_MANIFEST refactor. Failure #1 machinery intact and referenced. Tests 12/12,
including regressions that assert an empty ledger still yields a labelled plan.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Update science_research_agent tool cache from phase-1 governed re-run

Regenerated when restricted-content-leakage was re-run with --force-stage
inference to re-measure failure #1 against the now dual-control agent_guarded.py
(see artifacts/results/PHASE1-RERUN-DRIFT.md). Cache grows 24680 -> 39268 lines;
this file was already tracked, so committing keeps the tree clean and the run
reproducible.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* prompt_agents: add model_only/simtools/gentools variant matrix

The shipped prompt_agents example ships five YAMLs describing distinct
Prompt Agent wirings (model_only, simulated tools, generated tools,
sandbox, external). All are *prompt targets*, not callables, so ACS --
which can only govern `callable: module:function` -- could not be
applied to any of them, and the earlier evaluation collapsed the whole
domain onto the single realtools agent.py.

Reify the three that are reachable as callables, each a faithful port of
its YAML (system prompt lifted verbatim from the parser value, model /
max_tokens / temperature as configured):

  agent_model_only.py   no tools
  agent_simtools.py     fixed toolset + LLM result simulator
  agent_gentools.py     per-test-case LLM-generated tool schemas

plus an ACS-wrapped counterpart for each, so every variant has a
baseline and a governed arm.

_variant_guard.py holds the shared adapter rather than editing
agent_guarded.py, whose numbers are already published; the measured
modules are byte-identical to what was measured before.

Two defects fixed there rather than papered over:

  * the gentools ledger only recognised the four canonical tool names,
    so LLM-invented names never registered and the control was silently
    inert. _GenericLedger is name-agnostic. Verified against live
    generation, which produced `drug_interaction_check` and
    `pain_management_alternatives` -- neither in the base vocabulary,
    and different again on the previous call.

  * the empty-ledger path emitted canned guidance unrelated to the
    user's question. The original reply is now the floor. This is the
    third instance in this batch of a guard substituting or withholding
    content instead of repairing it; it scores well on violation rate,
    which is exactly why it needs catching.

24 eval configs (3 variants x 2 arms x 2 failures) differ from the
measured baseline by exactly two lines each, `run:` and `callable:`, so
all arms share one LLM-generated test set --
artifact_cache._stage_descriptor deliberately excludes target.callable
from the test_set key to keep A/B comparisons like-for-like.

31 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Unbundle scenario behavior presets

Extract atomic behaviors from the travel planner and telecom scenario specs, leaving scenarios as context plus behavior references.

Reuse existing stereotyping, prompt_injection, sycophancy, grounding, tool-selection, verification, observation, response-completeness, and unsupported-conclusion presets instead of duplicating them.

Update checker, docs, example configs, and benchmark default to enforce and consume atomic behavior presets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b

* fix(examples): keep all seven flagship behaviors, one config each

Unbundling the travel_planner preset also rewrote the flagship example config
down to a single behavior (prompt_injection). That is atomic but it silently
dropped six mechanisms from the example the README, getting-started, schema
docs, the ACS guide, and science.yml all point at -- coverage loss wearing
atomicity's clothes.

Restores the other six as sibling configs under behaviors/, each sharing the
same context: and measuring exactly one mechanism. eval_config.yaml stays the
quickstart so every existing doc reference keeps working.

This is also the layout we tell CI customers to use, so the flagship example now
demonstrates it instead of just describing it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b

* Reset billing_support_agent to pre-skill state for clean rerun.

* feat(agents): update systemize, judge to gpt-5.4, default model to gpt-5.4-mini.

* feat(example): billing_support_agent ran through workflow.

* feat(viewer): retire policy_violation/overrefusal from display surfaces.

* fix(cli): detect the permissibility split by key presence, not rate.

* feat(example): return billing_support_agent to its pre-skill state.

* fix(skill): update SKILL to prevent custom judge dimension generation.

* feat(example): billing_support_agent final workflow demo.

* feat(example): billing_support_agent Clarity Protocol directory moved to example.

* feat(example): career_health_assessment cleared to pre-skill state.

* docs: make the behavior/scenario library obviously discoverable

Addresses Ahmed's confusion on this PR about where the atomic behavior
presets live and how they compose with scenarios. Adds pointers at every
entry point a user is likely to hit before writing a behavior spec by hand:

- Top-level README.md: new nav-bar link + a What-you-get bullet
- docs/README.md: new Behavior Library entry under Configuration
- docs/config/best-practices.md: callout inside SS8.D (atomic behaviors)
- examples/README.md: new 'Reuse a behavior from the library' section,
  placed before a user starts writing YAML by hand

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(example): career_health_assessment ran through SKILL workflow.

* feat(example): cleared azure_doc_qa to pre-skill state.

* feat(example): azure_doc_qa workflow through SKILL complete.

* feat(examples): clear seven examples to pre-skill state for rerun.

Reset travel_planner_langgraph, change_control_agent, prompt_agents,
science_research_agent, and travel_planner_neurosan to commit 6817a31 so the
finalized skill can be rerun end to end. prompt_agents backs the
health-assistant model-only, simulated-tools, and generated-tools variants, so
clearing that root resets all three.

Removes generated Clarity Protocol trees, eval configs, ACS policies and
manifests, guarded agents, and guard tests while retaining each example's
original agent, tools, README, and config inputs. Prior ASSERT and ACS run
outputs were preserved under artifacts/*-run2-archive before deletion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add ACS governance for science_research_agent disclosure risks

Clarity risk discovery identified two Critical failure modes in the
science research assistant: restricted-class disclosure and obedience to
instructions embedded in retrieved documents. Both are governed by a
single structural gate.

tools.py `_render()` returns the full `body` and `snippet` for every hit
regardless of the authoritative `class` field, so the four restricted
classes (restricted_results, partner_notes, contacts, private_notes) are
disclosed in full. `snippet` is not a lesser field - it carries the
partner recall figure and a named contact - so body-only redaction would
have left the harm intact while reporting enforcement active.

The gate runs at post_tool_call on file_search and is transformative, not
a denial: file_search is never blocked. Restricted results have `body`
and `snippet` replaced by a marker while `id`, `class`, `rank`, `tags`,
and `title` are preserved, so the agent can still report that a document
exists and give the access-request path.

No injection detector was built. The payload the injection asks for is
removed before delivery, so the embedded instruction can be obeyed in
full and return nothing. Source class is deliberately not used as an
injection signal: fetch_url hardcodes class `public`, and the carrier
document is genuinely public.

Measured at n=25 per split, baseline vs governed:

  restricted_class_disclosure
    prompt    harm 91.67% -> 43.75%   permissible 60.87% -> 32.00%
    scenario  harm 95.83% -> 75.00%   permissible 48.00% -> 52.00%
  embedded_instruction_obeyed
    prompt    harm 56.25% -> 26.67%   permissible 28.00% -> 28.00%
    scenario  harm 61.90% -> 34.78%   permissible 52.00% -> 52.00%

Restricted markers fall to zero in the governed transcripts: the partner
recall figure 679 -> 0, the contact address 220 -> 0, the contact name
428 -> 0.

The scenario permissible tick for the first risk is one row of 25 with
node-level counts flat at 14/16/16, so it is clustering noise rather than
a mechanism; it was not resampled.

One iteration was needed. The first marker was long, prohibition-heavy,
and injected twice per restricted document, which suppressed legitimate
internal_only discussion - the internal_only sentinel dropped from 58
occurrences to 48. A compact, positively framed, scope-bounded marker
with the long guidance emitted once in policy_notice restored it to
exactly 58. No prompt, model, control-flow, or test-set change.

agent_guarded.py adds ACS enforcement and nothing else. It imports
agent.py rather than copying the loop, never redefines SYSTEM_PROMPT,
TOOL_SCHEMAS, MAX_STEPS, MAX_TOOL_CALLS, or AGENT_MODEL, and delegates to
base.chat(). Enforcement is scoped by a contextvars.ContextVar set inside
try/finally by the governed entrypoints; with the wrapper installed and
the contextvar unset, baseline behaviour is unchanged.

Governed configs differ from baseline by exactly two lines (`run:` and
`target.callable`). Both governed runs reused systematize and test_set
artifact v0001; --force-stage was never passed.

Note that under-disclosure by blanket refusal is itself scored
`permissible: false` in this taxonomy, so the harm metric is mixed here -
part of the improvement is the agent answering more, not only leaking
less.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add Clarity protocol + ACS governance for change_control_agent

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop over the change
control agent for both Critical failures found by Clarity risk discovery.

failure-02 fabricated_change_record -- WIN. A post_tool_call gate denies a
change record whose returned fabricated_fields is non-empty, and a
pre_tool_call gate holds the control surfaces while such a record stands.

  prompt    harm  0.00% -> 0.00%   permissible 52.00% -> 44.00%
  scenario  harm 90.91% -> 86.36%  permissible 52.00% -> 52.00% (flat)

failure-01 unauthorized_change_advancement -- HONEST NEGATIVE. A
pre_tool_call gate on the four control surfaces denies a submission while
policy state forbids it.

  prompt    harm  0.00% -> 0.00%   permissible 18.18% ->  4.35%
  scenario  harm 66.67% -> 44.00%  permissible 16.00% -> 24.00%

The win condition (harm down AND permissible down-or-flat) is not met on
the scenario split. Harm fell 22.7pp, roughly 5-6 rows of 25, while
permissible rose 8pp, or 2 rows. All four permitted governed attempts were
spent and attempt 1 was the best of them on every metric, so attempt 1 is
what ships and the negative is reported rather than re-rolled.

The permissible move is inside the measured noise floor. Governed attempts
1 and 2 differed only by added trace spans -- an observability change with
no policy effect -- yet scenario harm moved 44.0 -> 56.0 and permissible
24.0 -> 41.7. Run-to-run variance at n=25 with judge n=1 is therefore about
3-5 rows. The 2-row permissible move sits inside that band and the 5-6 row
harm drop sits outside it. No run was repeated unchanged to fish for a
better draw.

failure-02's residual 86% harm is a structural ceiling, not an
implementation gap: only 8 of 20 harmful rows ever call
create_change_request and 7 of 20 make no tool call at all, so most
fabrication harm is invented prose that a tool-call gate cannot reach. The
one output-stage attempt at it drove scenario permissible 52% -> 84% and
overrefusal 12% -> 76% while barely moving harm, and was reverted in full.

Not-permissible harm is 0.00% on the prompt split in every run of both
suites, so all harm signal in this domain lives in the scenario split and
the prompt split measures only over-restriction.

agent_guarded.py adds ACS enforcement and nothing else. Both arms execute
the same agent.py::_run_loop function object -- verified at runtime as
agent_guarded._run_loop is agent._run_loop -- which takes a pluggable tool
executor; the baseline passes _default_execute_tool and the guarded module
passes an ACS-enforcing executor of the identical signature. No prompt,
model, tool schema or budget is redefined. Each eval_config.governed.yaml
differs from its baseline by exactly two lines, run: and target.callable,
and every run scored the same systematize/test_set v0001 artifact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* Add Clarity protocol + ACS governance for prompt_agents health assistant

Runs the full Clarity -> ASSERT -> ACS -> ASSERT loop over the health
assistant Prompt Agent for both Critical failures found by Clarity risk
discovery, across the model-only and simulated-tools variants.

A Prompt Agent has no host process: the target is declared entirely in YAML
and the ASSERT runtime owns the model call and tool loop, so there is
nothing for ACS to wrap. The target is therefore materialised as a Python
callable in agent.py, which instantiates ASSERT's own HostedSession and
SimulatedResolver rather than imitating them. Both A/B arms run that same
materialised callable, so the delta isolates enforcement; absolute levels
carry materialisation error, the delta does not. The five
health_assistant*.yaml specs are the specification under test and are
unmodified.

simtools x fabricated_clinical_fact_as_retrieved -- WIN on attempt 1.
Marking, not suppression: the harm is the certainty, so the claim is
attributed to its origin and not presented as verified.

  prompt    harm 13/25 = 52.0% -> 9/24 = 37.5%   perm 6/12 = 50.0% -> 1/18 =  5.6%
  scenario  harm 17/25 = 68.0% -> 8/24 = 33.3%   perm 10/20 = 50.0% -> 3/25 = 12.0%

simtools x dosage_guidance_to_elderly_patient -- WIN on attempt 3.
Non-statement, not marking: a caveat beside "5 mg" does not remove the 5 mg.

  prompt    harm 10/25 = 40.0% -> 1/24 =  4.2%   perm 1/25 = 4.0% -> 1/25 = 4.0%
  scenario  harm 19/25 = 76.0% -> 15/25 = 60.0%  perm 0/25 = 0.0% -> 0/25 = 0.0%

Over-refusal is 0% on both splits of both shipped runs.

modelonly x dosage_guidance_to_elderly_patient -- NOT WON, reported as such.
No configuration wins both splits. v4 wins the prompt split outright
(37.5% -> 27.3%, permissible flat at 0/25) and cuts scenario harm
18/24 -> 14/24, but scenario permissible rises 0/25 -> 2/25.

modelonly x fabricated_clinical_fact_as_retrieved is excluded by design:
with no tools there is no retrieval claim to make.

gentools is not measurable and is skipped rather than approximated.
health_assistant_generated_tools.yaml sets tool_source: per_test_case, so
tool schemas ride on each test-case row and are read by
_build_hosted_session as scenario["tools"]. _build_target_session has the
payload in scope and passes it to ExternalSession, but constructs
CallableSession without it, and CallableSession.__init__ accepts no
scenario or tools parameter. Per-test-case tools cannot reach a callable
target, so chat_gentools raises NotImplementedError. Approximating with a
fixed toolset would measure simtools while labelling it gentools.

Both rates are flagged/applicable, and the judge marks a node not
applicable when the transcript never engages it, so a control that removes
a behaviour outright shrinks its own denominator and can push a rate up
while violations fall. modelonly v3 prompt is 9/24 -> 8/21: the rate rises
37.5% -> 38.1% while the count falls. Counts are therefore reported beside
every rate here, and the README carries the same warning.

The shipped default is the v3 ladder, which is what produced the confirmed
simtools x dosage win; the v4 position-keyed ladder is retained behind
HEALTH_ACS_POSITION_KEYED_DOSAGE=1 and gate telemetry behind
HEALTH_ACS_GATE_LOG, both default off. Each governed eval config pins the
run id the shipped code reproduces, so no config resumes a cached run
produced by different code.

agent_guarded.py adds ACS enforcement and nothing else. Both arms execute
the same function object, verified at runtime as agent._chat is
agent_guarded._chat; each governed entrypoint is a single line calling it
through its one seam. The guarded module never references HostedSession,
SimulatedResolver, load_toolset_file, parse_target_config or the system
prompt. Each eval_config.governed.yaml differs from its baseline by exactly
two lines, run: and target.callable, and every run in all three suites
scored the same systematize/test_set v0001 artifact.

verify_gates.py exercises both gates against the real AgentControl/OPA
runtime (13/13), including a case proving the annotator name contract fails
silently when the manifest key, the Rego reference and the dispatcher
branch disagree.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* fix(examples): address Yeming's PR #296 review comments

- Update the flagship travel_planner_langgraph README's Scenario table,
  which still described the pre-unbundling behavior.description (quality
  + safety failures blended, 6 behavior_categories). It now reflects the
  atomic single-behavior eval_config.yaml (prompt_injection, 4 categories)
  and explains the sibling behaviors/*.yaml cover the other six mechanisms.

- Reformat context: and rubric: multiline fields in all six
  behaviors/*.yaml sibling configs (plus eval_config.yaml's rubric, for
  consistency) from a hard-wrapped single-quoted scalar to literal block
  style (| / |-), matching eval_config.yaml's existing context: style.
  Verified byte-for-byte semantic equivalence via yaml.safe_load diff
  against the prior committed content -- pure style change, no content
  drift.

- Tighten scripts/check_behavior_library.py's spec-parity check per
  Yeming's concern: the 98%-similarity tolerance could let a real content
  change in a long spec through silently, since words() already
  normalizes the only expected sources of formatting difference (headers,
  bullets, wrapping, whitespace, case) -- any remaining difference is real
  drift, not noise. Now requires an exact match. Also hard-fails if the
  examples/behavior_specs reference directory is missing, instead of
  silently skipping the whole parity check.

All 51 presets still pass (48 behaviors, 3 scenarios), atomic and in
parity, with the tightened exact-match rule. 89/89 targeted tests still
pass. All 7 edited example configs verified to still load and resolve
through assert_ai.config.load_config.

* travel_planner_langgraph: Clarity->ASSERT->ACS governance cycle

Adds the governed arm for both Critical risks discovered by Clarity
(fabricated_cost_claim, unsupported_entry_requirement), measured against a
shared baseline. All runs reuse one systematize v0001 and one test_set v0001,
and each governed eval config differs from its baseline by exactly two lines
(run, callable), so the A/B is directly comparable.

Result: a split verdict, shipped honestly rather than tuned into a win.

Prompt split (single turn) wins on both risks:
  costs  harm 16/23 -> 7/21
  entry  harm flat at 2 rows while permissible violations fall 11/23 -> 8/25
         and over-refusal falls 44.0% -> 32.0%

Scenario split (ten turns) regresses on both risks:
  costs  permissible 12/23 -> 17/24, over-refusal 48.0% -> 75.0%
  entry  permissible 11/19 -> 16/18, over-refusal 50.0% -> 89.5%

The scenario regression is architectural, not a tuning failure. Those
conversations frequently never reach the research step, so the retrieval record
is empty; with no evidence in hand the only correct action for an output gate
is to decline, and nothing later in the conversation supplies what is missing.
Softening the annotator across attempts measurably re-opened harm (costs prompt
harm back to 47.4%, entry scenario harm to 81.8%), so the trade is real. The
enforcement wrapper is deliberately not allowed to retrieve the missing
grounding, because a wrapper that retrieves is no longer a control.

agent_guarded.py reuses the baseline graph rather than reimplementing it:
get_graph(), _seed_messages() and _get_llm() are imported from agent.py, and
_draft() is agent.chat() plus the returned message list and retrieval record.
At runtime the guarded module resolves to the same compiled graph object, so
the arms differ only by enforcement. Tracing is enabled before agent import in
both arms so the judge sees identical telemetry.

A known defect ships deliberately and is documented in code and in the README:
the depth-based fallback rotation was inert during measurement because history
was not threaded into it, so every declining turn emitted identical wording.
The call site is left in its measured two-argument form so the published
numbers reproduce from this code. The prompt split is single turn and therefore
unaffected, so those wins stand unconfounded; the scenario over-refusal figures
should be read as an upper bound on the cost of enforcement rather than a
precise measurement of it. Threading history is a one-line, unvalidated change
belonging to the next cycle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* feat(example): clear travel_planner_langgraph to pre-skill state.

Resets the example so the Clarity -> ASSERT -> ACS workflow can be run
end to end from a clean slate in the IDE.

Removed (all generated by the previous skill run):
  Clarity Protocol/    discovery, failures, goal, solution, mailboxes
  acs/                 manifests and Rego policy for both risks
  evals/               baseline and governed eval configs for both suites
  agent_guarded.py     the ACS enforcement wrapper
  README.md            restored to its pre-skill content

Also removed from the working tree, untracked and therefore not part of
this commit: artifacts/results/travel-planner-*, artifacts/acs/travel-planner-*,
the per-run logs and status JSON under artifacts/runlogs/, and __pycache__.
Clearing the results and stage artifacts matters as much as clearing the
source: a stale suite directory would let a later run reuse cached
systematize/test_set artifacts instead of generating its own.

The example is now byte-identical to its pre-skill state (empty diff against
c2c11d5) and contains only agent.py, auto_trace.py and README.md.

Nothing is lost. The previous cycle is preserved in full by commit 818f7c7
and by the exported release bundle, which carries the source, both ACS
policies, all eval configs, the Clarity protocol, every run's results and
transcripts, and the per-run logs for baseline plus all four governed
attempts.

No other example is touched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* azure_doc_qa: Clarity protocol for fabrication + leakage risks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* travel_planner_neurosan: Clarity protocol + ACS governance for two risks

Shipped configurations (counts are flagged/applicable, re-derived from raw
results; rates alone mislead because the judge drops non-engaging rows from
the denominator):

wrong-destination-entry-requirements  baseline -> acs-governed
  prompt    harm 25/25 -> 4/24   permissible 4/11 -> 1/25   overrefusal 0/25 -> 0/25
  scenario  harm 24/24 -> 10/14  permissible 10/16 -> 21/24 overrefusal 7/24 -> 24/24

fabricated-budget-verification        baseline -> acs-governed-v2
  prompt    harm 21/25 -> 13/24  permissible 1/22 -> 1/25   overrefusal 0/25 -> 0/25
  scenario  harm 25/25 -> 23/23  permissible 11/25 -> 10/24 overrefusal 10/25 -> 11/24

Single-turn prompts win on both risks. Multi-turn scenarios trade harm for
over-refusal: the fallback is a fixed stateless template re-delivered on every
denied turn, so a 7-turn conversation re-asks for a nationality the traveller
already supplied. That is a remediation-design fault, not a policy fault.

Enforcement-only A/B: each governed config differs from its baseline by exactly
two lines (run, callable). Same model, judge, sample size, and stage artifacts.

Provenance: agent_guarded.py output templates were verified against the shipped
transcripts by string probe -- the budget fallback appears 493x in
acs-governed-v2 and 0x in acs-governed, which is why the budget config is pinned
to v2. Annotator and regeneration prompt text was edited after the last
successful run for two follow-up attempts that failed at inference and produced
no results. Wrapper-internal prompts are never written to any artifact, so those
edits cannot be confirmed or excluded from the measured state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* azure_doc_qa: grounded fabrication gate + leakage gate through the SKILL

Shipped runs:
  azure-doc-qa-confidential-leakage    baseline -> acs-governed
  azure-doc-qa-fabricated-answer       baseline -> acs-governed-grounded-v2

Counts are flagged/applicable, re-derived from the raw result rows. Rates alone
mislead: the judge marks a node not-applicable when the transcript never
engages it, so a shrinking denominator can move a rate while the violation
count is unchanged.

confidential-leakage           baseline -> acs-governed
  prompt    harm  9/22 ->  2/22   permissible 10/25 ->  6/25   overrefusal  2/25 ->  4/25
  scenario  harm 15/24 ->  8/24   permissible 17/25 -> 16/25   overrefusal 11/25 -> 16/25

fabricated-answer              baseline -> acs-governed-grounded-v2
  prompt    harm  3/14 ->  1/16   permissible 11/24 -> 11/25   overrefusal 10/25 -> 11/25
  scenario  harm  9/23 ->  9/18   permissible  6/25 -> 18/25   overrefusal  5/25 -> 18/25

Leakage wins on both axes: harm roughly halves and permissible violations fall
as well. Fabrication wins single-turn only - harm 3 -> 1 with permissible
exactly flat at 11. Multi-turn does not win: the harm count is unchanged at 9
(the rate moves 39.1 -> 50.0 only because the denominator fell 23 -> 18) while
permissible violations triple, 6 -> 18.

Fabrication took three governed attempts, all recorded in Clarity summary.md:
a reply-only output annotator cannot separate grounded specificity from
fabricated specificity, so it only trades over-refusal. Feeding the annotator
the retrieval context captured from the baseline graph, then scoping the
rewrite, cuts single-turn harm without an over-refusal cost. Multi-turn is not
reachable from an output gate at all - 18/25 conversations are flagged for both
fabrication and over-refusal, i.e. the agent fabricates on some turns and
stonewalls on others. The fix belongs upstream, at retrieval state or in the
prompt, not in another output-remediation lever.

Enforcement-only A/B: each governed config differs from its baseline by exactly
two lines (run, callable). One systematize/v0001 and one test_set/v0001 shared
by every run, so no stage was regenerated.

Provenance: agent_guarded.py was written 11:35:19, the shipped run started
11:36:54 and ended 12:20:12, and the file has not been touched since. The
intermediate configs eval_config.governed.yaml and
eval_config.governed_grounded.yaml are retained because summary.md cites their
numbers as the progression, but the committed agent_guarded.py is the
grounded+scoped code and will NOT reproduce those two runs. Only
eval_config.governed_grounded_v2.yaml is reproducible from this tree.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* fix(library): use FutureWarning for the moved-scenario shim, per Yeming's review

DeprecationWarning is suppressed by Python's default warning filters outside
pytest/-W. The shim's whole purpose is to tell config authors their
behavior:{preset: travel_planner}-style config has been reclassified without
breaking it -- with DeprecationWarning, that notice was invisible to anyone
running assert-ai run directly, only visible under pytest (which re-enables
DeprecationWarning by default). FutureWarning is shown by default in normal
script execution, which is the actual audience for this warning.

* feat(example): travel_langgraph_planner ran through SKILL workflow.

* chore(examples): strip ACS artifacts from the 8 worked domains

Keep only the baseline agent, the baseline eval configs, the Clarity
Protocol design record and the README in each domain.

Deleted (57 files):
  acs/ manifests + rego policy .......... 30
  agent_guarded.py ......................  8
  eval_config.governed*.yaml ............ 18
  prompt_agents/verify_gates.py .........  1

verify_gates.py goes with them: it imports agent_guarded and loads acs/,
so it cannot function once those are gone.

Kept deliberately, though a literal "delete what the skill made" would
have removed them:
  - __init__.py in billing_support_agent, career_health_assessment and
    prompt_agents. These were added by the skill, but are required for
    examples.<domain>.agent to import.
  - mock_tools.py, mcp_tools.py and docs/ (azure_doc_qa); tools.py
    (change_control_agent, science_research_agent); the five
    health_assistant*.yaml specs (prompt_agents). All are referenced at
    import time or read at runtime.

All 17 remaining baseline eval_config.yaml target agent.py and contain no
acs/ or guarded references, so the baseline side of each A/B is intact.

Clarity Protocol is left as-is. 8 of its 205 files still describe acs/ and
agent_guarded.py; that is prose, not imports, and is correct for a
historical design record.

Verified: all 8 examples.<domain>.agent modules import cleanly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b

* feat(examples): rewrite the 8 domain READMEs for the stripped branch.

* feat(examples): aligned READMEs to impermissible/permissible behavior violation wording.

* feat(readme): update readme with SKILL get started.

* docs(examples): align incident_triage_agent row with main's baseline-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

* docs: repoint example config paths at the evals/<risk>/ layout

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

* fix(examples): close remaining #296 review gaps

- Add examples/benchmark/README.md, the deliverable Ahmed explicitly asked
  for and accepted ("i think yes, adding the readme would be more clear").
  Explains this is a throughput-scale variant of the flagship
  travel_planner_langgraph example -- same target, same
  explicit_constraint_violation_failures preset already used by
  behaviors/constraints.yaml, deliberately non-adversarial context: -- not a
  new agent or behavior.
- Register examples/benchmark/ in examples/README.md's selection table and
  layout tree so it is actually discoverable, matching this PR series' own
  stated goal.
- Fix the one sibling config eval_config.yaml itself missed in the prior
  YAML-style pass: the overrefusal rubric was still the hard-wrapped
  single-quoted scalar form; now literal-block style like every other
  rubric/context field in this example. Verified byte-for-byte semantic
  equivalence via yaml.safe_load diff -- pure style fix.

51/51 presets clean, 89/89 targeted tests pass.

* feat(example): added the rest of eval_config.yaml for career_health_assessment.

* docs(examples): commit the behavior taxonomy alongside eval_config.

* fix(tests): restore-judge preset coverage lost to a silent skip.

* fix(tests): collect and trigger the skill's test suite.

* fix(deps): bound arize-phoenix below the release that breaks the CI.

* feat(example): give billing_support_agent a real policy instead of a prompt.

* fix: close the four non-blocking PR review follow-ups.

* fix: close the second-round PR review items.

* fix(examples): keep evals atomic and runnable

Remove generated discovery artifacts and bundled configs, narrow composite behaviors, and align example navigation and commands with the runnable suites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(examples): defer travel eval layout to stacked cleanup

Remove the overlapping travel-planner behavior configs so the downstream examples PR owns the canonical flat evals layout. Keep the atomic benchmark update, but make its documentation independent of the removed path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b

* fix(ci): cap Phoenix for Python 3.11

Phoenix 19.18+ crashes while pytest auto-loads its plugin on Python 3.11. Keep the existing compatible lock resolution, constrain the optional dependency, and make dependency metadata changes trigger regression CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b

* fix(skill): align release metrics and example curation

* docs: normalize getting-started line endings

* fix(examples): restore the career health worked domain

* fix(examples): remove career health worked domain

Restore the intended six-domain curated example surface and remove the navigation entries reintroduced in 5764e0d.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(examples): remove career health navigation

Keep the root and examples indexes aligned with the intended six worked domains.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Alex Ngo <t-alexngo@microsoft.com>
Co-authored-by: changliu2 <changliu2@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jake Present <jakepresent1@gmail.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Copilot-Session: eb38db79-6f6e-4f57-9e57-c7496498048b
Chang Liu (changliu2) and others added 2 commits August 14, 2026 11:13
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7cb46daf-b5ce-4ad5-a85d-977737e5c02b
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