
Ax Audit
- 217 installs
- 74 repo stars
- Updated August 5, 2026
- mblode/agent-skills
Run accessibility audits on pages or components to find WCAG violations, keyboard traps, and contrast issues before public launch.
About
ax-audit guides Claude Code through accessibility reviews of web or app UI: WCAG-oriented checks, keyboard and screen-reader risks, contrast failures, and prioritized fixes so teams ship inclusive experiences and pass pre-release quality bars.
- WCAG violation detection
- keyboard navigation checks
- color contrast analysis
- remediation prioritization
- pre-launch a11y review
Ax Audit by the numbers
- 217 all-time installs (skills.sh)
- +22 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #920 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mblode/agent-skills --skill ax-auditAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 217 |
|---|---|
| repo stars | ★ 74 |
| Last updated | August 5, 2026 |
| Repository | mblode/agent-skills ↗ |
What it does
Run accessibility audits on pages or components to find WCAG violations, keyboard traps, and contrast issues before public launch.
Files
AX Audit
Feature-level reviewer for applications where an agent acts on the user's behalf. Answers one question: does this agent earn trust, and where does it break?
- IS: a rules-based audit of agentic surfaces (agent chat, tool execution panels, agent config, agent dashboards) across two layers (architecture correctness in
rules-arch/, trust and relationship design inrules-ax/), ending in a ship-readiness verdict and an AX Relationship Summary. - IS NOT: traditional UX auditing: forms, states, focus, async, microcopy (use
ux-audit); broad web UI quality: accessibility, layout, typography, performance (useui-audit); agent instruction-file quality (useagents-md).
If the scope contains no agentic features (only forms, lists, modals), stop and route to ux-audit. Running AX rules against traditional UI produces only noise.
Contents
- Audit workflow
- Two rule layers
- Tiers and verdict
- AX Relationship Summary
- Reference files
- Gotchas
- Audit self-check
- Related skills
Audit workflow
Copy and track this checklist:
AX Audit progress:
- [ ] Step 1: Scope, via `git diff --name-only main` (PR mode) or explicit path (full sweep)
- [ ] Step 2: Detect agentic features per references/feature-playbooks.md
- [ ] Step 3: Run each detected feature's playbook in order, plus the diff-wide checks
- [ ] Step 4: For each check, load the rule file and follow its detection recipe
- [ ] Step 5: Tier each finding per references/ship-readiness.md (rule override table wins)
- [ ] Step 6: Render verdict + findings + AX Relationship Summary per references/output-format.md
- [ ] Step 7: Run the audit self-check and report its evidence countsStep notes:
1. Scope. Default is the PR diff plus the tool definitions and orchestrator code it touches. Pre-existing findings in untouched files belong in a full sweep, not a PR verdict. 2. Detect. Detection heuristics (component names, hooks, routes) live in references/feature-playbooks.md. Four feature types: agent chat/copilot, agent tool execution, agent config, agent dashboard. 3. Playbooks. Each feature has 5-9 ordered checks. Run all of them even when you expect a pass. A pass with evidence is part of the report. One diff-wide check (parity-orphan-ui-action) runs on every PR-mode audit regardless of detected features. 4. Rules. Each rule file carries its own detection commands, false-positive guards, tier override table, and suppression syntax. The rule file is authoritative; playbook annotations are a convenience copy. 5. Tier. Three tiers; precedence rules below. 6. Render. Group findings by surface; verdict block first, AX Relationship Summary last. 7. Self-check. Evidence or it didn't happen, see below.
Two rule layers
| Layer | Folder | Rules | Question it answers | Category index |
|---|---|---|---|---|
| 1: Agent-native architecture | rules-arch/ | 11 | Can the agent do what the user can do? Are tools atomic? Does the agent know what exists? Is completion explicit? | rules-arch/_sections.md |
| 2: Agentic experience | rules-ax/ | 12 | Does the agent earn trust? Can the user interrupt, undo, push back? Is memory visible? | rules-ax/_sections.md |
Load rules-arch/<category>-<slug>.md or rules-ax/<category>-<slug>.md when a playbook check names it. Categories: arch = parity, granularity, context, comm; ax = trust, control, context, comm. The layers share the comm and context prefixes but the rules are distinct: rules-arch/comm-no-approval-gate.md (orchestrator code has no gate logic) is not rules-ax/control-no-approval-gate.md (approval UI doesn't match the stakes).
Tiers and verdict
Every finding gets exactly one tier (full trigger lists in references/ship-readiness.md):
release-blocker, fix before merge: no escape hatch, silent execution, heuristic completion, broken parity, ungated high-stakes actionsfix-this-sprint, merge with a tracked issue: no confidence cues, no intent handshake, opaque memory, bundled config toolsbacklog, ship and track: static canvas, no generative momentum, static API mapping, no checkpoint/resume
Tier precedence: a rule's own surface-override table > the generic surface bump in references/ship-readiness.md > the rule's defaultTier. Apply at most one adjustment, never stack the generic bump on top of a rule's explicit override.
Verdict: ✅ READY (0 blockers, ≤3 sprint) · ⚠️ READY WITH FOLLOW-UP (0 blockers, ≥4 sprint) · ❌ NOT READY (≥1 blocker) · 🚫 INCOMPLETE (self-check failed).
AX Relationship Summary
Rendered after findings whenever any agentic feature was detected. Findings are for engineers; this summary is for designers and PMs, so never skip it. Four fields:
- Evolution stage: behavior description, not a label (see
references/ax-evolution-curve.md) - Trust signal: high / moderate / low, one-sentence reasoning from trust-critical rule results
- Key gap: the single most important gap, one actionable sentence
- Trust question: one question only prototyping or research can answer
Reference files
| File | Read when |
|---|---|
references/feature-playbooks.md | Steps 2-3: detection heuristics, per-feature ordered checks, diff-wide checks |
references/ship-readiness.md | Step 5: tier triggers, precedence, verdict logic |
references/output-format.md | Step 6: findings JSON schema, summary schema, terminal rendering |
references/agent-native-principles.md | A Layer 1 finding needs deeper grounding: parity, granularity, CRUD completeness, context patterns, approval matrices, checkpoint/resume |
references/ax-evolution-curve.md | Writing the evolution-stage field of the AX Relationship Summary |
rules-arch/_sections.md | Orienting in Layer 1 categories and their default tiers |
rules-ax/_sections.md | Orienting in Layer 2 categories, default tiers, and co-firing rule pairs |
Gotchas
- Scope before rules. Running all 23 rules repo-wide on a 3-file PR buries the one new release-blocker under pre-existing backlog noise, and the verdict stops meaning "can this PR merge."
- The rule's override table is authoritative.
comm-no-intent-handshakedefaults tofix-this-sprintbut its own table saysrelease-blockeron tool execution. Stacking the generic "+1 tier on tool execution" bump on top of explicit overrides double-upgrades backlog findings into blockers. - A stop button not wired to `AbortController.abort()` is a false affordance.
control-no-escape-hatchstill fails: verify theabort()call, not the button label, or the audit passes a UI that lies to users. - Absence checks need a recorded file list. "Find components lacking X" greps return nothing both when everything passes and when nothing was scanned. List candidate files first (
rg -l <feature-pattern>), then check each for the counter-pattern, and cite the file list as evidence. - `detection: observational` rules cannot fail on grep evidence alone.
granularity-static-api-mapping,trust-no-uncertainty-markers,control-over-conversational, andcomm-no-generative-momentumrequire interaction-flow judgment; with static evidence only, returnunknownwith a reason instead offail. - `ax-audit-ignore:<slug>` comments count as `suppressed`, not `pass`. Report the suppressed count in the verdict block; a suppression with no trailing reason is itself worth a
warn. - Don't duplicate ux-audit findings. "Missing loading state" and "form clears on error" are
ux-auditterritory; duplicating them teaches engineers to dismiss the whole AX report. - Don't inflate tiers.
comm-no-generative-momentumandgranularity-static-api-mappingdefault tobacklog. Promoting cosmetic findings to blocker trains the team to ignore ❌ verdicts.
Audit self-check
Self-flag the audit INCOMPLETE if any of these are true, and include the counts as evidence in the report (planned vs. run rules per playbook, unknown rate, suppressed count):
- Fewer rules ran than the playbooks planned
- More than 30% of rules returned
unknown - Any
fail/warnfinding lacksfile:lineevidence or a fix snippet - Every finding landed in the same tier (suspect blanket assignment)
- AX Relationship Summary is missing despite detected agentic features
Related skills
ux-audit: traditional UX quality on the same surfaces (run both on agentic features; ax-audit covers the agent layer, ux-audit the rest)ui-audit, broad web UI quality: accessibility, layout, typography, performanceagents-md: audit CLAUDE.md / AGENTS.md agent instruction filesdefine-architecture: repo structure and module boundaries
Agent-Native Principles (Condensed)
<!-- TOC -->
<!-- /TOC -->
---
Core Principles
Parity, Whatever the user can do through the UI, the agent must achieve through tools. When adding any UI capability, ask: can the agent achieve this outcome? If not, add the necessary tools.
Granularity: Tools are atomic primitives; decision logic lives in prompts. One conceptual action per tool. To change behavior, you edit prompts, not refactor code.
Composability: With atomic tools and parity, new features are new prompts. No code written. The agent uses primitives and judgment to pursue an outcome.
Emergent Capability: Agents accomplish things you did not explicitly design for. Build atomic tools, observe what users request, let the agent compose solutions or reveal gaps, then add domain tools for common patterns.
Improvement Over Time: Agent-native apps improve without shipping code. Accumulated context persists in files; prompts are refined at developer, user, or agent level. Self-modification requires audit logs and rollback.
---
Tool Design
Atomic primitives first. Start with bash, file operations, basic storage. Prove the architecture before adding domain tools.
CRUD completeness. For every entity, verify the agent has create, read, update, and delete. Common failure: create_note + read_notes exist but update_note and delete_note are missing.
| Entity | Create | Read | Update | Delete |
|---|---|---|---|---|
| (each) | required | required | required | required |
Domain tools. Add deliberately as patterns emerge. Three reasons: vocabulary anchoring (teaches the agent your domain), guardrails (validation that should not be left to judgment), efficiency (common multi-step operation bundled).
Dynamic capability discovery. Instead of one tool per endpoint, expose list_available_types() + read_data(type). New API capabilities are discovered at runtime, not hard-coded.
MCP. Model Context Protocol formalizes discover + access into a client-server standard. MCP servers expose typed tool schemas; clients discover tools at runtime. Prefer MCP servers over hand-coded wrappers for external services.
Graduation. Operations can move from agent-orchestrated loops to optimized code for hot paths. Even after graduation, the agent should be able to trigger the operation and fall back to primitives for edge cases.
---
Context Patterns
Entity-scoped directories. Structure data as {entity_type}/{entity_id}/ with primary content, metadata, and related materials. Separate ephemeral (AgentCheckpoints/, AgentLogs/) from durable (Research/) directories.
The `context.md` pattern. A file the agent reads at session start and updates as state changes. Contains: who the agent is, what it knows about the user, what exists, recent activity, guidelines, and current state. Portable working memory without code changes.
Context injection. System prompts include three sections: 1. Available resources: what data exists and where 2. Capabilities: what the agent can do 3. Recent activity: what happened since last session
Context engineering. Context windows are finite; long-running agents actively manage what they carry.
| Technique | When to use |
|---|---|
| Compaction: summarize old messages, drop raw history | Context >70% full |
Structured note-taking: agent maintains notes.md of learnings and decisions | Multi-step research/planning |
| Just-in-time retrieval: load files/schemas only when the current step needs them | Large data sets, many tools |
---
Agent-UI Communication
Completion signals. Always explicit via stop_reason, never heuristic. The LLM API drives continuation: stop_reason: "tool_use" means loop, stop_reason: "end_turn" means stop. App-level orchestrators can add richer signals: pause, escalate, retry.
Partial completion tracking. Track progress per task (pending, in_progress, completed, failed, skipped). Show 3/5 tasks complete (60%) with per-task status and error notes.
Agent event types. Emit typed events: thinking, toolCall, toolResult, textResponse, statusChange. Use an ephemeralToolCalls flag to hide noisy internal operations from the UI.
Shared workspace. Agents and users work in the same data space, not separate sandboxes. Users can inspect and modify agent work; agents build on what users create. Sandbox only when security or data integrity requires it.
Approval gates. Match approval requirements to stakes and reversibility:
| Stakes | Reversibility | Pattern |
|---|---|---|
| Low | Easy | Auto-apply |
| Low | Hard | Quick confirm |
| High | Easy | Suggest + apply (show diff) |
| High | Hard | Explicit approval |
When the user explicitly requests an action, that is already approval. Self-modification always requires explicit approval + audit log + rollback.
---
Mobile
Checkpoint/resume. Save full message history, iteration count, task status, and custom state to a AgentCheckpoint on backgrounding and after each tool result. On launch, scan for valid checkpoints (default TTL: 1 hour), offer resume, delete on completion.
iCloud-first storage. Use iCloud container with local fallback. Data syncs across devices without server infrastructure. Use a storage abstraction layer, not raw FileManager. Monitor NSMetadataQuery for cloud file state and conflict copies.
Background execution. iOS gives ~30 seconds. Priority: (1) complete the current tool call, (2) checkpoint session state, (3) transition to .backgrounded status. For truly long-running agents, use a server-side orchestrator with the mobile app as viewer/input.
AX Evolution Curve
A 4-stage model for evaluating how deep the relationship between user and agent is in a given design. Used during audit to calibrate expectations: a Conversational agent missing memory features is fine; a Personally Intelligent agent missing memory visibility is a finding.
The Four Stages
1. Conversational
Every interaction starts from scratch. No memory, no context from prior sessions. User must re-explain everything each time.
Behavior: a chatbot that forgets everything on page refresh. User says "like I mentioned earlier" and the agent has no idea what they mean.
2. Task-Aware
Watches and adjusts in the moment. Understands current task state, tracks multi-step progress, reacts to what is happening now. Forgets between sessions.
Behavior: an agent that sees your current document and makes suggestions, but does not know your preferences or recall past decisions.
3. Personally Intelligent
Remembers preferences and history across sessions. Accumulates context over time, adapts to user patterns, gets better with use.
Behavior: an agent that knows you prefer concise answers, remembers your project conventions, recalls decisions from three weeks ago.
4. Socially Embedded
Understands role, team, and cultural context. Speaks on behalf of the user to others, manages cross-team communication, navigates organizational dynamics.
Behavior: an agent that drafts messages to your team in your voice, understanding who needs what context and how to frame requests for different audiences.
The Defensibility Line
The defensibility line sits between Task-Aware and Personally Intelligent. Below it, features are commoditized: anyone can build a stateless chatbot or a task tracker. Above it, accumulated context creates a moat. Switching costs increase because the agent knows the user. The longer someone uses the product, the harder it is to leave.
When auditing, note where a product sits relative to this line. Products below it need differentiation through execution quality. Products above it need to make accumulated context visible and portable (or risk trust erosion when users feel locked in).
Mapping to Rules
Which ax-audit rules matter most at each stage:
| Stage | Key rules |
|---|---|
| Conversational | control-over-conversational, comm-no-progress-signal |
| Task-Aware | comm-no-intent-handshake, control-no-escape-hatch, control-no-approval-gate, trust-no-escalation-path |
| Personally Intelligent | context-memory-not-visible, context-under-contextual, trust-no-confidence-cues, trust-no-uncertainty-markers |
| Socially Embedded | context-no-adaptive-canvas, comm-no-generative-momentum |
Rules from earlier stages still apply at later stages. A Socially Embedded agent that lacks an escape hatch is still a finding.
Assessment
To determine which stage a design sits at:
- What persists between sessions? Nothing = Conversational. Task state only = Task-Aware. User preferences and history = Personally Intelligent. Relationships and organizational context = Socially Embedded.
- Does the agent adapt to individual users? If two different users get identical responses in identical situations, the agent is at most Task-Aware.
- Does the agent interact with other people or systems on behalf of the user? If yes, evaluate whether it understands enough social context to do so without causing harm.
Describe behaviors in audit output, not labels. Write "the agent remembers preferences across sessions" rather than "this is a Stage 3 product." The framework is for reasoning about depth, not for vocabulary.
Feature Playbooks
Detect each agentic feature from element + filename + route signals, then run its checks in order. Every check names a rule file in rules-ax/ (Layer 2, agentic experience) or rules-arch/ (Layer 1, architecture, marked explicitly).
The tier in parentheses is the rule's override for that surface, copied here for scanning. The rule file's override table is authoritative: if they ever disagree, the rule file wins.
Table of contents
- Feature detection
- Diff-wide checks
- Agent Chat / Copilot
- Agent Tool Execution / Action Panel
- Agent Configuration / System Prompt Editor
- Agent Dashboard / Status
- Coverage
Feature detection
| Feature | Detect by |
|---|---|
| agent chat / copilot | <Chat>, <Assistant>, <Copilot>, role="assistant", isStreaming, aiResponse, completion, useChat, useCompletion, route /chat, /assistant, /copilot |
| agent tool execution / action panel | <ToolCall>, <Action>, tool_use, function_call, executeAction, agentAction, component *ToolPanel*, *ActionLog* |
| agent configuration / system prompt editor | <SystemPrompt>, <AgentConfig>, <PromptEditor>, route /agent/settings, /configure, systemPrompt |
| agent dashboard / status | <AgentStatus>, <TaskList>, <RunHistory>, <RunLog>, component *AgentDashboard*, route /agent, /runs |
No agentic features detected → stop; this skill does not apply. Route to ux-audit.
Diff-wide checks
Run on every PR-mode audit, regardless of which features were detected:
1. `parity-orphan-ui-action` (rules-arch, fix-this-sprint): the diff adds a UI capability (button, form action, route handler) with no corresponding tool in the same PR. Each orphan widens the user/agent capability gap.
Agent Chat / Copilot
User need: get help from the agent, trust its output, control what it does.
Checks (in order):
1. `comm-no-progress-signal` (release-blocker): streaming/thinking indicator visible during agent response; the user must never stare at a frozen UI. 2. `control-no-escape-hatch` (release-blocker): agent actions triggered from chat are interruptible mid-execution and reversible after completion. 3. `context-no-injection` (rules-arch, release-blocker): sessions initialize with dynamic context (preferences, recent activity, project state), not a bare static prompt. 4. `trust-no-confidence-cues` (fix-this-sprint): agent output includes rationale or sources so the user can verify correctness. 5. `trust-no-uncertainty-markers` (fix-this-sprint): agent hedges when uncertain rather than presenting guesses as fact. 6. `comm-no-intent-handshake` (fix-this-sprint): non-trivial or destructive actions are confirmed with the user before execution. 7. `control-over-conversational` (fix-this-sprint): parallel direct-manipulation controls exist for common actions; users are not forced into chat. 8. `context-memory-not-visible` (fix-this-sprint): the user can see and edit what the agent remembers across sessions. 9. `comm-no-generative-momentum` (backlog): blank-canvas entry points offer an agent-generated draft when the agent has the context to produce one.
Agent Tool Execution / Action Panel
User need: understand what the agent is doing, stop it if wrong, trust the outcome.
Checks (in order):
1. `trust-no-escalation-path` (release-blocker): high-stakes actions (deletes, payments, external calls) can hand off to a human before proceeding. 2. `control-no-approval-gate` (release-blocker): the approval UI matches the stakes and reversibility of the action. 3. `comm-no-approval-gate` (rules-arch, release-blocker): the orchestrator code path actually gates high-stakes tools (stakes × reversibility logic), not just the UI. 4. `control-no-escape-hatch` (release-blocker): every completed action has undo or revise; the user is never locked into an agent decision. 5. `comm-no-progress-visibility` (rules-arch, release-blocker): multi-step agent tasks show step-level progress, not just a spinner. 6. `comm-no-completion-signal` (rules-arch, release-blocker): completion is explicitly signalled (stop_reason, completion tool), never inferred from idle time. 7. `comm-no-intent-handshake` (release-blocker on this surface): ambiguous or multi-interpretation requests get a playback/confirmation before the agent acts. 8. `context-under-contextual` (fix-this-sprint on this surface): the agent uses available context (current page, selection, recent actions) instead of asking redundant questions. 9. `granularity-static-api-mapping` (rules-arch, backlog): evolving APIs use discover + access tools rather than one hard-coded tool per endpoint.
Agent Configuration / System Prompt Editor
User need: customize agent behavior without breaking it, understand what changed.
Checks (in order):
1. `parity-no-tool-parity` (rules-arch, release-blocker): every UI config option has an agent-accessible equivalent; no GUI-only settings. 2. `granularity-workflow-shaped-tool` (rules-arch, fix-this-sprint): config tools are atomic primitives, not bundled workflows that hide individual options. 3. `context-starvation` (rules-arch, fix-this-sprint): the system prompt injects available resources, tools, and constraints so the agent knows what it can do. 4. `context-memory-not-visible` (fix-this-sprint): the user can see the full context the agent receives, including injected system prompts. 5. `context-no-adaptive-canvas` (backlog): the UI surfaces downstream effects when config changes alter agent behavior.
Agent Dashboard / Status
User need: see what the agent has done, what it's doing now, and what went wrong.
Checks (in order):
1. `comm-no-completion-signal` (rules-arch, release-blocker): completed tasks are explicitly marked done, not left in an ambiguous state. 2. `parity-crud-incomplete` (rules-arch, release-blocker), tasks have full CRUD: create, view, cancel, retry, and delete. 3. `comm-no-progress-visibility` (rules-arch, fix-this-sprint on this surface): running tasks show live step-level progress. 4. `trust-no-confidence-cues` (fix-this-sprint): completed task results include reasoning or a summary of what was done and why. 5. `context-memory-not-visible` (backlog on this surface): the agent's accumulated context is viewable and editable. 6. `context-no-checkpoint-resume` (rules-arch, backlog): interrupted or failed tasks resume from the last checkpoint, not from scratch.
Coverage
All 23 rules are reachable: 9 via chat, 9 via tool execution, 5 via config, 6 via dashboard, 1 diff-wide (rules repeat across playbooks; unique total = 23). If you add a rule file, add it to at least one playbook or it will never run.
Output Format
Defines the output structure for ax-audit results. Two sections: findings table, then AX relationship summary.
Table of contents
- Findings table
- Field reference
- AX relationship summary
- AX relationship summary, field descriptions
- Terminal rendering
Findings table
Each finding is a JSON object (schema is deliberately compatible with ux-audit findings so the two reports can be merged):
{
"rule": "trust-no-confidence-cues",
"layer": "ax",
"category": "trust",
"feature": "agent-chat",
"surface": "ChatPanel",
"file": "src/chat/ChatPanel.tsx",
"line": 42,
"result": "fail",
"defaultTier": "fix-this-sprint",
"assignedTier": "fix-this-sprint",
"tierReason": "Default tier; agent chat surface.",
"observed": "Agent output rendered in <AssistantMessage> with no citation, source, or reasoning child components.",
"evidence": ["src/chat/ChatPanel.tsx:42, <AssistantMessage content={message.content} /> with no children"],
"fix": "Add a <Sources> or <Reasoning> component inside agent message rendering.",
"suppressed": false
}Field reference
| Field | Values / notes |
|---|---|
rule | Rule slug, matches the rule filename without .md |
layer | arch (Layer 1) or ax (Layer 2) |
category | arch: `parity \ |
feature | One of the 4 agentic playbooks: agent-chat, agent-tool-execution, agent-config, agent-dashboard |
surface | Component or page name the finding sits on (groups the report) |
file, line | Evidence location; required on every fail/warn |
result | `pass \ |
defaultTier, assignedTier, tierReason | Tier from the rule file, tier after surface override, and one-sentence justification. The tier is the single ship-impact signal; there is no separate severity field |
observed | What the code actually does, in one sentence |
evidence | Array of file:line: excerpt strings backing the finding |
fix | Concrete change; a snippet or one-sentence instruction |
suppressed | true when an ax-audit-ignore:<slug> comment covers the match, report suppressed counts, never silently drop |
AX relationship summary
Produced after findings, only when agentic features are detected. Four fields naming the user-agent relationship in behavioral terms.
{
"axSummary": {
"evolutionStage": {
"stage": 2,
"label": "Task-Aware",
"behavior": "Agent tracks current task state and adjusts in the moment, but starts fresh each session with no memory of user preferences or history."
},
"trustSignal": {
"level": "moderate",
"reasoning": "Escape hatches present for all agent actions. Confidence cues missing: agent output has no rationale or source attribution."
},
"keyGap": "Agent accumulates no session context; every interaction starts cold. Users re-explain preferences and constraints each time.",
"trustQuestion": "Will users accept inline rationale (sources, reasoning steps) on every agent response, or will it feel like noise?"
}
}AX relationship summary: field descriptions
| Field | Description |
|---|---|
evolutionStage | Which of the 4 stages (see ax-evolution-curve.md). Describe the behavior, not the label. The label is for JSON; the behavior is for the reader. |
trustSignal | `high \ |
keyGap | Single most important architectural or trust gap. One sentence. Specific enough to act on. |
trustQuestion | One question for the designer/developer to answer before the next round. Should be a question only prototyping or research can resolve. |
Terminal rendering
When rendering to terminal (not JSON), use this format:
═══════════════════════════════════════════════════════════
AX VERDICT: ⚠️ READY WITH FOLLOW-UP (0 blockers, 4 fix-this-sprint)
Surfaces: 2 (ChatPanel, ToolExecutionPanel)
Findings: 6
Release blockers: 0
Fix this sprint: 4 ⚠️
Backlog: 2 📋
AX Relationship:
Stage: Task-Aware (2 of 4)
Trust: Moderate: escape hatches present, confidence cues missing
Key gap: No session context; every interaction starts cold
Question: Will users accept inline rationale on every response?
Cross-reference: Run ux-audit for traditional UX findings
═══════════════════════════════════════════════════════════Ship Readiness: Three-Tier Verdict for Agentic Surfaces
Every finding gets exactly one of three tiers. The tier determines whether the PR can ship, must wait, or merges with follow-up.
Table of contents
The three tiers
⛔ release-blocker: fix before merge
Findings in this tier must be fixed before the PR is merged. They cause user harm, unsafe autonomous behavior, or unrecoverable agent actions in production.
Tier triggers:
- No escape hatch: agent takes actions user cannot interrupt, undo, or override; user is locked into an autonomous workflow with no way out
- No approval gate on high-stakes actions: agent autonomously performs destructive, financial, or external actions (deleting records, sending emails, charging cards) without confirmation
- No escalation path: agent handles high-stakes decisions with no way to hand off to a human; failures cascade without intervention
- Silent execution: agent runs a multi-step task with no progress indication; user cannot tell if it is working, stalled, or failed
- Heuristic completion: agent completion detected by idle time rather than an explicit signal; creates race conditions where downstream steps fire too early or too late
- Broken tool parity: user can do something the agent cannot, or vice versa; breaks the mental model of what the agent is capable of
- Missing CRUD: entity has create but no delete, or read but no update; agent gets stuck mid-workflow with no way to correct or clean up
⚠️ fix-this-sprint: merge but log issue
Findings in this tier degrade the agentic experience but don't block shipping. They must have a tracking issue created before merge; the issue should be resolved within the current sprint.
Tier triggers:
- Agent output with no confidence cues or reasoning (functional but trust-eroding)
- No intent handshake before non-trivial actions (functional but risky, agent acts without confirming it understood the request)
- Chat-only interface for button-worthy actions (inefficient; common tasks buried in free-text input)
- Agent uses context but user cannot see or edit what is remembered (opaque but not dangerous)
- System prompt missing resource injection (agent works but is under-informed for the task)
- Config tools bundled instead of atomic (works but inflexible; user cannot grant fine-grained permissions)
📋 backlog: track, ship
Findings in this tier are real but low-stakes. Ship the PR, log a backlog issue, prioritize by frequency or impact later.
Tier triggers:
- Interface does not reshape with agent task progression (static but functional)
- Agent does not leverage all available context (underperforms but does not break)
- No generative momentum on blank-canvas surfaces (missed opportunity for proactive suggestions)
- Static API mapping instead of dynamic discovery (works but less flexible when tools change)
- No checkpoint/resume for long-running tasks (risky on interruption but rare in practice)
Tier assignment rules
Precedence, highest first, apply exactly one:
1. The rule's own surface-override table (in the rule file). Most rules carry one; it is authoritative. 2. The generic surface adjustment below: only for rules with no override row for the surface in question. 3. The rule's `defaultTier`.
Never stack adjustments: a rule whose table already says release-blocker on tool execution does not get bumped again.
| Surface context | Generic adjustment |
|---|---|
| Agent tool execution / action panel | Bump 1 tier (sprint → blocker; backlog → sprint): autonomous actions demand higher safety |
| Agent chat / copilot | Same: conversational surfaces tolerate slightly more friction |
| Agent config / system prompt editor | Same |
| Agent dashboard / status | Down 1 tier (blocker → sprint; sprint → backlog): monitoring surfaces are less critical than action surfaces |
Verdict logic
Aggregate the per-finding tiers into a top-level verdict:
| Verdict | Condition |
|---|---|
| ✅ READY | 0 release-blockers AND ≤3 fix-this-sprint |
| ⚠️ READY WITH FOLLOW-UP | 0 release-blockers AND ≥4 fix-this-sprint |
| ❌ NOT READY | ≥1 release-blocker |
| 🚫 INCOMPLETE | Audit-self-check failed; re-run |
Verdict shows in the summary block at the top of every audit report.
Anti-patterns
- ❌ Tier inflation: assigning every finding
release-blocker. Kills signal. Reserve the tier for genuine ship-blockers. - ❌ Tier deflation: moving everything to
backlogto make a verdict look greener. Catches up at the next production incident. - ❌ Tier per rule, not per finding: a rule's default tier is a starting point. Surface context can bump it up or down.
- ❌ Skipping the override step, every finding's tier is justified in the audit output: "release-blocker because agent action panel." Don't render bare tiers without context.
Examples
{
"rule": "control-no-approval-gate",
"surface": "AgentActionPanel",
"defaultTier": "release-blocker",
"assignedTier": "release-blocker",
"tierReason": "Rule's own override table: release-blocker on agent tool execution. Agent deletes user records without a confirmation dialog, a high-stakes action with no approval gate."
}{
"rule": "context-memory-not-visible",
"surface": "AgentStatusDashboard",
"defaultTier": "fix-this-sprint",
"assignedTier": "backlog",
"tierReason": "Rule's own override table: backlog on agent dashboard. Opaque memory is less critical on a read-only monitoring surface."
}Cross-reference
For traditional UX findings on agentic surfaces (form data loss, focus management, loading states), run ux-audit alongside ax-audit. The two skills are complementary: ax-audit covers agent-specific safety and interaction patterns while ux-audit covers general frontend quality.
Sections: Agent-Native Architecture (Layer 1)
This file defines the 4 categories of agent-native architecture audit rules. Each rule file uses one of these category prefixes.
---
1. Parity (parity)
Default tier: mostly release-blocker Why critical: If the agent can't do what the user can do, the agent is a second-class citizen. Parity gaps surface as "why can't the agent do X?": and there's no workaround. Missing CRUD operations strand agents mid-workflow.
2. Granularity (granularity)
Default tier: mostly fix-this-sprint Why critical: Tools that bundle decision logic force the agent to accept or reject an entire workflow. Atomic primitives let the agent apply judgment at each step. When behavior changes require code refactoring instead of prompt editing, granularity is too low.
3. Context (context)
Default tier: mostly fix-this-sprint Why critical: An agent that doesn't know what exists, what the user has done, or what's available will ask redundant questions, miss relevant data, and feel unintelligent. Context starvation is the most common reason an agent underperforms despite having capable tools.
4. Communication (comm)
Default tier: release-blocker for completion/progress; fix-this-sprint for approval gates Why critical: Silent agents feel broken. Heuristic completion detection creates race conditions. Missing progress indicators make users kill and restart tasks unnecessarily. Approval gates that don't match stakes either block users on trivial actions or auto-execute dangerous ones.
---
Rule index
parity-no-tool-parity parity-crud-incomplete parity-orphan-ui-action
granularity-workflow-shaped-tool granularity-static-api-mapping
context-starvation context-no-injection context-no-checkpoint-resume
comm-no-completion-signal comm-no-progress-visibility comm-no-approval-gateTotal: 11 rules.
<Rule title>
One paragraph explaining the architectural failure mode in plain language. Why it breaks agents. What principle it violates.
What goes wrong
A concrete, observable scenario. What the user or agent experiences, what the code does, why they diverge.
Detection
Surfaces: <which playbooks invoke this: agent-chat, agent-tool-execution, agent-config, agent-dashboard>
Static signals: 1. Concrete grep / Read step. Use rg / find / file-extension filters. 2. Each step produces evidence: a file path, a line number, a presence/absence boolean, a count. 3. Last step compares evidence to a threshold.
Concrete commands:
# Inline grep recipes the agent can run. Note: ripgrep has no 'tsx' type: '--type=ts' covers *.ts and *.tsx.
rg 'pattern' --type=ts src/False-positive guards:
- Skip files that already have the expected pattern.
- Skip files with
// ax-audit-ignore:<this-slug>near the match. - Skip test and Storybook fixtures.
Fix
Concrete change with the architectural pattern:
// before: the anti-pattern
// after: the corrected patternDefault tier and overrides
Defaults to: <tier>
Surface overrides:
| Surface | Tier |
|---|---|
| Agent tool execution | <usually one tier higher> |
| Agent chat | <same or one tier lower> |
| Agent config | <same> |
| Agent dashboard | <usually one tier lower> |
Examples
Anti-pattern (fails):
// Real-world example showing the bug.Applied (passes):
// Same component with the fix applied.Cross-reference
If a finding overlaps with ux-audit rules, link out:
ux-auditrule<slug>for the traditional UX dimension
Suppression
To intentionally ignore this rule on a specific component:
{/* ax-audit-ignore:<slug>, reason */}
<Component />High-stakes irreversible action with no approval step
Agent autonomously sends email, deletes files, or publishes content with no confirmation. User discovers the action after the fact. Trust destroyed. Violates Parity: stakes and reversibility must determine oversight level, not agent confidence.
What goes wrong
User says "clean up my inbox." Agent archives 200 emails including an unread message from the CEO. Or: "deploy the fix" and the agent pushes to production without showing what changed.
Detection
Surfaces: agent-tool-execution, agent-chat
Static signals: 1. Find tool execution handlers. 2. Identify destructive/financial/external operations: send, delete, publish, deploy, charge, transfer. 3. Check for confirmation dialog or approval handler between decision and execution. 4. Flag direct execution of high-stakes operations with no gate.
Runtime signals: Agent executes destructive tools with no preceding user confirmation.
Concrete commands:
rg '(name|toolName).*["'"'"'](send|delete|remove|publish|deploy|charge|transfer)' --type=ts src/
rg '(executeTool|callTool|invokeTool)' --type=ts -A 10 src/ | rg -v '(confirm|approve|requireApproval)'
rg '(requireApproval|confirmBefore|approvalGate|stakesLevel)' --type=ts src/False-positive guards:
- Skip files with
// ax-audit-ignore:comm-no-approval-gate. - Skip read-only operations (get, list, search, fetch).
- Skip operations marked safe/reversible in tool metadata.
- Skip test files and fixtures.
Fix
Stakes x reversibility matrix: low+easy = auto, low+hard = quick confirm, high+easy = suggest with diff, high+hard = explicit modal.
// before
async function executeTool(tc: ToolCall) {
return tools[tc.name].execute(tc.args);
}
// after: approval gate based on stakes and reversibility
async function executeTool(tc: ToolCall, onApproval: ApprovalHandler) {
const tool = tools[tc.name];
switch (getApprovalLevel(tool.stakes, tool.reversibility)) {
case "auto": return tool.execute(tc.args);
case "quick": await onApproval({ type: "toast", timeout: 5000 }); return tool.execute(tc.args);
case "suggest": await onApproval({ type: "diff", diff: await tool.preview(tc.args) }); return tool.execute(tc.args);
case "explicit": await onApproval({ type: "modal", requireConfirm: true }); return tool.execute(tc.args);
}
}Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | release-blocker |
| Agent config | fix-this-sprint |
| Agent dashboard | fix-this-sprint |
Examples
Anti-pattern (fails): execute: async (args) => emailClient.send(args): no confirmation.
Applied (passes): Tool declares stakes: "high", reversibility: "hard": gate applied automatically.
Suppression
// ax-audit-ignore:comm-no-approval-gate, internal cleanup, operates only on temp files
const cleanupTool = { execute: (args) => fs.rm(args.tempDir) };Agent completion detected by heuristic instead of explicit signal
Orchestrator detects "done" by counting idle iterations, checking output files, or waiting for a timeout. Agent pausing to think looks like completion; slow API calls trigger premature termination. Violates Parity: the UI must receive an explicit signal, not guess.
What goes wrong
Agent researches a complex question. Makes 3 tool calls, then pauses 8 seconds composing a response. Orchestrator counts 2 idle iterations, hits maxIdleIterations: 2, terminates. User sees a truncated answer.
Detection
Surfaces: agent-tool-execution, agent-dashboard
Static signals: 1. Find the orchestrator control loop that decides continue/stop. 2. Check for idle-counting, timeout-based completion, or file-existence as termination. 3. Flag any heuristic used as the primary completion signal.
Concrete commands:
rg '(consecutiveIdle|noToolCall|idleCount|maxIdle)' --type=ts src/
rg '(setTimeout|setInterval)' --type=ts -A 5 src/ | rg '(done|complete|finish|terminate)'
rg '(stop_reason|end_turn|shouldContinue)' --type=ts src/False-positive guards:
- Skip files with
// ax-audit-ignore:comm-no-completion-signal. - Skip timeout logic alongside an explicit signal (both
stop_reasonANDsetTimeout). - Skip test files and fixtures.
Fix
// before: heuristic completion
let idle = 0;
while (idle < 3) {
const res = await llm.chat(messages);
if (!res.toolCalls.length) { idle++; continue; }
idle = 0;
await executeTools(res.toolCalls, messages);
}
// after: explicit signal via stop_reason or completion tool
while (true) {
const res = await llm.chat(messages);
if (res.stopReason === "end_turn") return { status: "complete", content: res.content };
for (const tc of res.toolCalls) {
if (tc.name === "task_complete") return { status: "complete", summary: tc.args.summary };
messages.push({ role: "tool", content: await executeTool(tc) });
}
}Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent dashboard | release-blocker |
| Agent chat | fix-this-sprint |
| Agent config | backlog |
Examples
Anti-pattern (fails): while (noToolCalls < 2): thinking pause triggers premature termination.
Applied (passes): if (res.stopReason === "end_turn") return res.content: explicit model signal.
Suppression
// ax-audit-ignore:comm-no-completion-signal, timeout is safety net, primary signal is stop_reason
const SAFETY_TIMEOUT = 120_000;Agent actions produce no UI feedback during execution
Agent runs for 30 seconds with no visible progress. User sees a spinner or nothing. They kill the session and restart. "Silent agents feel broken." Violates Parity: the UI must communicate what the agent is doing, not just what it finished.
What goes wrong
Agent makes 12 tool calls over 45 seconds analyzing a codebase. UI shows "Thinking..." the entire time. User assumes it froze, refreshes, agent starts over. With events they'd see "Reading src/index.ts... Found 3 issues."
Detection
Surfaces: agent-chat, agent-tool-execution, agent-dashboard
Static signals: 1. Find execution handlers: tool call loops, streaming handlers. 2. Check whether they emit typed events or update UI during execution. 3. Check whether text streams incrementally or batches until completion. 4. Flag handlers that only surface results at the end.
Concrete commands:
rg '(toolCall|tool_use|function_call)' --type=ts -A 10 src/ | rg '(for|while|map)'
rg '(emit|dispatch|onProgress|onToolCall|publish)' --type=ts src/
rg '(stream|onChunk|onToken|onDelta)' --type=ts src/False-positive guards:
- Skip files with
// ax-audit-ignore:comm-no-progress-visibility. - Skip sub-second operations.
- Skip backend-only code with no UI surface.
- Skip test files and fixtures.
Fix
// before: silent execution
async function handleChat(msg: string) {
const response = await agent.run(msg); // 30s silence
setMessages((prev) => [...prev, response]);
}
// after: progressive event emission
async function handleChat(msg: string) {
for await (const event of agent.stream(msg)) {
switch (event.type) {
case "thinking": setStatus("Reasoning..."); break;
case "toolCall": setStatus(`Running ${event.toolName}...`); break;
case "textDelta": appendToCurrentMessage(event.text); break;
case "done": setStatus("idle"); break;
}
}
}Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | release-blocker |
| Agent config | backlog |
| Agent dashboard | fix-this-sprint |
Examples
Anti-pattern (fails): const result = await agent.run(msg): 30s silence, then result.
Applied (passes): for await (const e of agent.stream(msg)): progressive events.
Suppression
{/* ax-audit-ignore:comm-no-progress-visibility, instant lookup, <500ms */}
<QuickLookupAgent />Long-running agent with no checkpoint/resume
Agent runs a multi-step task with no durability. Browser closes, network drops, session times out, all progress lost. User starts from scratch. Violates Improvement Over Time: completed work should survive interruption.
What goes wrong
User asks the agent to refactor 15 files. Agent completes 12 over 4 minutes. Laptop sleeps. On reconnect the session is gone, with no record of what was done. Agent redoes all 15, possibly making different choices.
Detection
Surfaces: agent-tool-execution, agent-dashboard
Static signals: 1. Find execution loops or multi-step task handlers. 2. Check whether they persist state between iterations. 3. Check whether resume/recovery logic exists. 4. Flag multi-step agents with no checkpoint writes.
Runtime signals: Agent runs >60s with no state persistence. Reconnect restarts from scratch.
Concrete commands:
rg '(for\s*\(|while\s*\(|for await)' --type=ts -A 5 src/ | rg -B 1 '(toolCall|executeStep|runTool)'
rg '(checkpoint|saveState|persistSession|saveProgress)' --type=ts src/
rg '(resume|recover|restoreSession|loadCheckpoint)' --type=ts src/False-positive guards:
- Skip files with
// ax-audit-ignore:context-no-checkpoint-resume. - Skip single-step agents (no loop, single tool call).
- Skip agents reliably under 10 seconds.
- Skip test files and fixtures.
Fix
// before
async function refactorFiles(files: string[]) {
for (const file of files) await agent.refactor(file);
}
// after: checkpoint after each step, resume on reconnect
async function refactorFiles(sessionId: string, files: string[]) {
const cp = await loadCheckpoint(sessionId);
const done = new Set(cp?.completed ?? []);
for (const file of files) {
if (done.has(file)) continue;
await agent.refactor(file);
done.add(file);
await saveCheckpoint(sessionId, { completed: [...done], updatedAt: Date.now() });
}
}Default tier and overrides
Defaults to: backlog
| Surface | Tier |
|---|---|
| Agent tool execution | fix-this-sprint |
| Agent chat | backlog |
| Agent config | backlog |
| Agent dashboard | backlog |
Examples
Anti-pattern (fails): for (const t of tasks) await agent.execute(t) -- tab closes at task 8, all lost.
Applied (passes): Loop resumes from loadCheckpoint(id) index, calls saveCheckpoint after each step.
Suppression
// ax-audit-ignore:context-no-checkpoint-resume, sub-second operation
await agent.formatSingleFile(filePath);Agent session starts without knowing what data exists
Session initializes with a static system prompt and no dynamic context. Every session starts ignorant of projects, preferences, or prior work, even when this data exists. Violates Improvement Over Time: each session should build on the last.
What goes wrong
User opens a design review agent for the third time today. Agent has no memory of earlier sessions, the 5 files reviewed, or the user's preference for accessibility-first feedback. It asks "What would you like me to review?" again.
Detection
Surfaces: agent-chat, agent-tool-execution
Static signals: 1. Find session initialization: agent constructors, chat init, session start handlers. 2. Check whether initialization loads dynamic context (context files, preferences, recent activity). 3. Flag sessions that use only static/hardcoded prompt content.
Concrete commands:
rg '(new Agent|createAgent|initSession|startChat)' --type=ts -A 15 src/
rg 'messages\s*[:=]\s*\[' --type=ts -A 5 src/ | rg 'role.*system' | rg -v 'await|fetch|load|get'
rg '(context\.md|loadContext|getContext|sessionContext)' --type=ts src/False-positive guards:
- Skip files with
// ax-audit-ignore:context-no-injection. - Skip test files and fixtures.
- Skip constructors where context is injected by a parent orchestrator.
Fix
// before: static initialization
function createSession(userId: string) {
return { messages: [{ role: "system", content: STATIC_PROMPT }] };
}
// after: read context.md at session start
async function createSession(userId: string) {
const ctx = await readContextFile(userId);
const prefs = await getUserPreferences(userId);
return {
messages: [{ role: "system", content: `${STATIC_PROMPT}\n\n${ctx}\n\n${prefs.summary}` }],
};
}Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent chat | release-blocker |
| Agent tool execution | fix-this-sprint |
| Agent config | backlog |
| Agent dashboard | backlog |
Examples
Anti-pattern (fails): private messages = [{ role: "system", content: "You review code." }]
Applied (passes): static async create(uid) { const ctx = await loadProjectContext(uid); ... }
Suppression
// ax-audit-ignore:context-no-injection, stateless utility agent, no user context needed
const agent = new StatelessAgent(STATIC_PROMPT);System prompt missing resource injection
System prompt says "You are a helpful assistant" with zero dynamic context. Agent asks "What files do you have?" instead of working with them. Violates Improvement Over Time: agents should accumulate context, not start blind.
What goes wrong
User opens a project management agent. System prompt has role instructions but nothing about the user's 3 active projects or 12 unread notifications. Agent's first message: "What would you like to work on today?"
Detection
Surfaces: agent-chat, agent-tool-execution, agent-config
Static signals: 1. Find system prompt assembly: string templates, prompt builders, message arrays. 2. Check whether the prompt injects: (a) available resources, (b) capabilities, (c) recent activity. 3. Flag prompts missing any of the three.
Concrete commands:
rg 'role:\s*["\x27]system["\x27]' --type=ts -A 10 src/ | rg -v '\$\{|concat|join|append'
rg '(availableResources|recentActivity|capabilities|context\.md)' --type=ts src/False-positive guards:
- Skip files with
// ax-audit-ignore:context-starvation. - Skip test files and fixtures.
- Skip prompts that delegate context loading to a separate init step.
Fix
// before
const messages = [{ role: "system", content: "You are a helpful assistant." }, ...userMessages];
// after: inject Available Data, What You Can Do, Recent Context
const ctx = await loadProjectContext(session.userId);
const messages = [
{ role: "system", content: `You are an assistant.\n\n## Available Data\n${ctx.resources}\n\n## Capabilities\n${ctx.capabilities}\n\n## Recent Context\n${ctx.recent}` },
...userMessages,
];Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent chat | release-blocker |
| Agent tool execution | fix-this-sprint |
| Agent config | fix-this-sprint |
| Agent dashboard | backlog |
Examples
Anti-pattern (fails):
const messages = [{ role: "system", content: "You are a helpful assistant." }];Applied (passes):
const ctx = await loadProjectContext(userId);
const messages = [{ role: "system", content: `You assist with code.\n\n${ctx.format()}` }];Suppression
// ax-audit-ignore:context-starvation, bootstrapping prompt, context injected by middleware
const basePrompt = "You are a helpful assistant.";One tool per API endpoint instead of dynamic discovery
50 tools for 50 API endpoints. Adding a new endpoint requires a code change and redeployment. Agent can only access what was anticipated at build time. For evolving APIs, a discover-and-access pattern keeps agent capabilities in sync automatically.
What goes wrong
CMS has 30 content types, 90 tools total. Content editor adds "Press Release" in the CMS admin. Agent can't access it -- no read_press_release tool exists yet.
Detection
Surfaces: agent-tool-execution
Static signals: 1. Count tool definitions. High counts (>20) with repetitive patterns suggest static mapping. 2. Check whether the data source supports dynamic type discovery.
Concrete commands:
rg 'name:\s*["\x27]' --type=ts src/tools/ -c | awk -F: '{sum+=$2} END {print "Total tools:", sum}'
rg 'name:\s*["\x27](read|get|list|create|update|delete)_' --type=ts -o --no-filename src/tools/ | awk -F'_' '{print $1}' | sort | uniq -c | sort -rnFalse-positive guards:
- Skip small stable APIs (<10 types), tools with genuinely different params, and
// ax-audit-ignore:granularity-static-api-mapping.
Fix
Replace static tools with discover + access.
// before: read_blog_post, read_landing_page ... 30 identical tools
// after: two tools cover the entire surface
export const listContentTypes = tool({
name: "list_content_types",
execute: async () => api.get("/content/types"),
});
export const readContent = tool({
name: "read_content",
parameters: { type: { type: "string" }, id: { type: "string" } },
execute: async ({ type, id }) => api.get(`/content/${type}/${id}`),
});Default tier and overrides
Defaults to: backlog: scaling problem, not correctness. Works fine for small, stable APIs.
Examples
Anti-pattern (fails):
export const readContact = tool({ name: "read_contact", execute: ({ id }) => api.get(`/crm/contact/${id}`) });
export const readDeal = tool({ name: "read_deal", execute: ({ id }) => api.get(`/crm/deal/${id}`) });
// ... 48 more: new custom "Partner" object added in CRM, agent can't access itApplied (passes):
// Two tools: discover + access. New "Partner" type works immediately.
export const listObjectTypes = tool({ name: "list_crm_object_types", execute: () => api.get("/crm/objects") });
export const readObject = tool({ name: "read_crm_object", execute: ({ objectType, id }) => api.get(`/crm/${objectType}/${id}`) });Suppression
// ax-audit-ignore:granularity-static-api-mapping, stable API with <10 types
export const readUser = tool({ name: "read_user", /* ... */ });Tool bundles decision logic instead of being atomic
A tool like analyze_and_organize(folder) bundles judgment into code. To change what "organize" means, you refactor code instead of editing a prompt. The agent can't apply its own judgment to intermediate steps.
What goes wrong
analyze_and_organize_inbox scans emails, decides importance, files them. User says "Why did you archive that?" Agent can't change the logic -- it's hardcoded. With atomic primitives, the agent decides itself.
Detection
Surfaces: agent-tool-execution, agent-config
Static signals: 1. Grep tool definitions for compound names (_and_, _then_, _with_). 2. Check implementations for branching logic making domain decisions. 3. Count distinct API calls per tool -- >1 suggests bundling.
Concrete commands:
rg 'name:\s*["\x27]\w+_(and|then|with)_\w+' --type=ts src/tools/
rg 'name:\s*["\x27](process|handle|manage|analyze|organize|auto)_' --type=ts src/tools/
rg -l 'tool\(|defineTool' --type=ts src/tools/ | xargs rg -c 'if\s*\(|switch\s*\(' | awk -F: '$2>3'False-positive guards:
- Skip atomic transactions (e.g.,
transfer_funds) and// ax-audit-ignore:granularity-workflow-shaped-tool.
Fix
Split into atomic primitives. Let the agent decide what to move and where.
// before: analyze_and_organize_inbox: after: atomic primitives
export const listEmails = tool({ name: "list_emails", /* ... */ });
export const readEmail = tool({ name: "read_email", /* ... */ });
export const moveEmail = tool({ name: "move_email", /* ... */ });Default tier and overrides
Defaults to: fix-this-sprint: works until the user disagrees with a bundled decision.
Examples
Anti-pattern (fails):
export const processNewUser = tool({
name: "process_and_configure_new_user",
execute: async ({ email, name }) => {
const user = await api.post("/users", { email, name });
await api.post(`/users/${user.id}/roles`, { role: "member" }); // can't choose role
await api.post("/emails/send", { to: email, template: "welcome" }); // can't skip
},
});Applied (passes):
export const createUser = tool({ name: "create_user", /* ... */ });
export const assignRole = tool({ name: "assign_role", /* ... */ });
export const sendEmail = tool({ name: "send_email", /* ... */ });
// Agent decides: skip welcome email, assign admin roleSuppression
// ax-audit-ignore:granularity-workflow-shaped-tool, atomic transaction
export const transferFunds = tool({ name: "transfer_funds", /* ... */ });Entity with incomplete CRUD tool coverage
Entity has create and read tools but is missing update or delete. Agent creates a note but can't fix a typo. Agent lists tasks but can't mark one complete.
What goes wrong
Agent creates a note. User spots a typo. No update_note tool exists. Agent says "I can't edit it." The agent generated work instead of completing it.
Detection
Surfaces: agent-tool-execution, agent-config, agent-dashboard
Static signals: 1. List all entity types from tool definitions. 2. For each entity, verify create/read/update/delete tools exist. 3. Flag entities with <4 CRUD operations.
Concrete commands:
rg 'name:\s*["\x27](create|get|list|update|edit|delete|remove)_(\w+)' \
--type=ts -o --no-filename src/tools/ | awk -F'_' '{print $2}' | sort | uniq -c | sort -n
rg 'name:\s*["\x27]create_' --type=ts -o --no-filename src/tools/ | \
sed 's/.*create_//' | sed 's/["\x27]//' | while read e; do
rg -q "update_${e}|edit_${e}" src/tools/ || echo "MISSING update: $e"; doneFalse-positive guards:
- Skip immutable entities (audit logs, event streams) and
// ax-audit-ignore:parity-crud-incomplete.
Fix
Add the missing CRUD tools. Every entity needs all four.
// before: [createNote, listNotes]: after: add update and delete
export const updateNote = tool({
name: "update_note",
execute: async ({ noteId, ...fields }) => api.patch(`/notes/${noteId}`, fields),
});
export const deleteNote = tool({
name: "delete_note",
execute: async ({ noteId }) => api.delete(`/notes/${noteId}`),
});Default tier and overrides
Defaults to: release-blocker: incomplete CRUD strands agents mid-workflow.
Examples
Anti-pattern (fails):
export const createTask = tool({ name: "create_task", /* ... */ });
export const listTasks = tool({ name: "list_tasks", /* ... */ });
// No update_task, no delete_task: agent can't mark tasks completeApplied (passes):
// All four CRUD operations present
export const tools = [createTask, listTasks, getTask, updateTask, deleteTask];Suppression
// ax-audit-ignore:parity-crud-incomplete, audit_log is intentionally immutable
export const listAuditLogs = tool({ name: "list_audit_log", /* ... */ });UI action with no agent tool equivalent
A route or UI handler exists that performs an operation the agent cannot achieve through any available tool. User asks the agent to do it, agent says "I can't do that." Parity means the agent can do everything the user can do.
What goes wrong
UI has an "Archive" button calling POST /api/projects/:id/archive. No tool exposes this endpoint. The agent responds "I don't have the ability to archive projects."
Detection
Surfaces: agent-config, agent-tool-execution
Static signals: 1. Diff new routes/pages/handlers. 2. For each, check whether a corresponding tool definition exists. 3. Flag routes with no tool counterpart.
Concrete commands:
rg -l 'export (async )?function (POST|PUT|PATCH|DELETE)' --type=ts src/app/api/
rg -l 'tool\(|defineTool|createTool|server\.tool' --type=ts src/tools/False-positive guards:
- Skip health-check endpoints (
/api/health), webhook receivers, test files. - Skip files with
// ax-audit-ignore:parity-no-tool-parity.
Fix
For every UI capability, ensure an equivalent tool exists.
// before: route exists, no tool
// POST /api/projects/[id]/archive exists; no archive_project tool
// after: tool mirrors the UI action
export const archiveProject = tool({
name: "archive_project",
description: "Archive a project by ID.",
parameters: { projectId: { type: "string", required: true } },
execute: async ({ projectId }) => api.post(`/projects/${projectId}/archive`),
});Default tier and overrides
Defaults to: release-blocker: a missing tool is a hard wall the agent cannot work around.
Examples
Anti-pattern (fails):
// Route handler exists, tools array has no archive tool
export const tools = [createProject, listProjects, getProject];Applied (passes):
export const tools = [createProject, listProjects, getProject, archiveProject];Suppression
// ax-audit-ignore:parity-no-tool-parity, internal admin endpoint
export async function POST(req: Request) { ... }New UI capability without corresponding tool
A PR adds a new UI feature (button, page, form action) but no new tool. Each PR without tool parity widens the gap between what users and agents can do.
What goes wrong
PR adds a "Duplicate project" button calling a new endpoint. No tool is added. PR merges. Months later a user asks the agent to duplicate a project. It can't.
Detection
Surfaces: agent-config, agent-tool-execution
Static signals: 1. In the diff, find new onClick handlers, form actions, route handlers. 2. Cross-reference with new tool definitions in the same diff. 3. Flag new UI capabilities with no new tool.
Concrete commands:
git diff main --name-only -- '*.ts' '*.tsx' | xargs rg -l 'export (async )?function (POST|PUT|PATCH|DELETE)' 2>/dev/null
git diff main -U0 -- '*.tsx' | rg '^\+.*onClick'
git diff main -U0 -- '*.ts' | rg '^\+.*(tool\(|defineTool|createTool)'False-positive guards:
- Skip cosmetic UI changes with no new backend call and
// ax-audit-ignore:parity-orphan-ui-action.
Fix
When adding a UI capability, add the corresponding tool in the same PR.
// before: POST /api/projects/[id]/duplicate added, no tool
// after: tool ships in the same PR
export const duplicateProject = tool({
name: "duplicate_project",
execute: async ({ projectId }) => api.post(`/projects/${projectId}/duplicate`),
});Default tier and overrides
Defaults to: fix-this-sprint: orphans are drift, not crisis. Cumulative effect degrades agent usefulness.
Examples
Anti-pattern (fails):
<button onClick={() => fetch(`/api/reports/${id}/export`, { method: "POST" })}>
Export CSV
</button>
// No export_report tool in this PRApplied (passes):
// Same PR adds the button AND the tool
export const exportReport = tool({
name: "export_report",
parameters: { reportId: { type: "string", required: true } },
execute: async ({ reportId }) => api.post(`/reports/${reportId}/export`),
});Suppression
{/* ax-audit-ignore:parity-orphan-ui-action, cosmetic preview, no agent use case */}
<button onClick={handlePreview}>Preview</button>Sections: Agentic Experience (Layer 2)
This file defines the 4 categories of agentic experience audit rules. Each rule file uses one of these category prefixes.
---
1. Trust & Transparency (trust)
Default tier: mostly fix-this-sprint; release-blocker for missing escalation paths Why critical: Users won't trust an agent even when it's right unless they can see why it decided what it decided. Confident wrong answers without uncertainty markers or escalation paths cause permanent trust damage that no amount of future accuracy recovers.
2. Control & Recovery (control)
Default tier: release-blocker for missing escape hatches; fix-this-sprint for over-conversational Why critical: Autonomy without exit is coercion. Every agent action needs a visible path to undo, revise, or override. The approval model must match the stakes and reversibility of the action. Chat-only interfaces for button-worthy actions waste user time and patience.
3. Context & Memory (context)
Default tier: mostly fix-this-sprint to backlog Why critical: Agents that don't show what they remember feel opaque. Agents that don't use available context feel stupid. Interfaces that don't reshape with task progression feel static. All three erode the relationship depth that makes agent products defensible.
4. Agent Communication (comm)
Default tier: release-blocker for silent execution; fix-this-sprint for missing handshake; backlog for missing drafts Why critical: Silent agents feel broken. The communication contract between agent and user (progress signals, intent confirmation, and generative momentum) is the difference between a tool that works and a black box.
---
Rule index
trust-no-confidence-cues trust-no-uncertainty-markers trust-no-escalation-path
control-no-escape-hatch control-no-approval-gate control-over-conversational
context-memory-not-visible context-no-adaptive-canvas context-under-contextual
comm-no-intent-handshake comm-no-progress-signal comm-no-generative-momentumTotal: 12 rules.
---
Cross-rule interactions
These pairings often co-fire on the same surface:
- no-confidence-cues + no-uncertainty-markers, Both address "why should I trust this." Different targets: rationale vs. hedging.
- no-escape-hatch + no-approval-gate: For autonomous actions, both fire. Approval gate may partially satisfy escape hatch.
- no-progress-signal + no-intent-handshake: Long-running tasks that didn't confirm scope AND show no progress are doubly opaque.
- memory-not-visible + under-contextual, Complementary: one says the agent knows things the user can't see; the other says it doesn't know things it should.
- over-conversational + no-generative-momentum, Paradoxical pairing: forcing chat where buttons would do, while failing to offer drafts where blanks would benefit.
<Rule title>
One paragraph explaining the trust or interaction failure mode in plain language. Why it erodes user trust. What AX pattern it violates.
What goes wrong
A concrete, observable scenario. What the user experiences, what the agent does, why trust breaks.
Detection
Surfaces: <which playbooks invoke this: agent-chat, agent-tool-execution, agent-config, agent-dashboard>
Auditability: <code-auditable | hybrid | observational>
Static signals (for code-auditable and hybrid rules): 1. Concrete grep / Read step. Use rg / find / file-extension filters. 2. Each step produces evidence: a file path, a line number, a presence/absence boolean, a count. 3. Last step compares evidence to a threshold.
Concrete commands:
# Inline grep recipes the agent can run. Note: ripgrep has no 'tsx' type: '--type=ts' covers *.ts and *.tsx.
rg 'pattern' --type=ts src/Judgment signals (for hybrid and observational rules):
- What to look for in the component tree or interaction flow.
- What qualifies as present vs. missing vs. misapplied.
False-positive guards:
- Skip files that already have the expected pattern.
- Skip files with
// ax-audit-ignore:<this-slug>near the match. - Skip test and Storybook fixtures.
Fix
Concrete change:
// before: the anti-pattern
// after: the corrected patternDefault tier and overrides
Defaults to: <tier>
Surface overrides:
| Surface | Tier |
|---|---|
| Agent tool execution | <usually one tier higher> |
| Agent chat | <same or one tier lower> |
| Agent config | <same> |
| Agent dashboard | <usually one tier lower> |
Examples
Anti-pattern (fails):
// Real-world example showing the trust failure.Applied (passes):
// Same component with trust pattern applied.Cross-reference
If a finding overlaps with ux-audit rules, link out:
ux-auditrule<slug>for the traditional UX dimension
Suppression
To intentionally ignore this rule on a specific component:
{/* ax-audit-ignore:<slug>, reason */}
<Component />Blank-canvas surface with no agent-generated starting content
User opens a new document, email, or report. Empty canvas. Blinking cursor. Agent is available but silent. A half-written draft is easier to shape than an empty page, but the agent doesn't offer one.
What goes wrong
User clicks "New marketing email" in a tool with their brand voice and audience data. Blank editor. Agent sits idle. A contextual draft would have gotten them editing in ten seconds.
Detection
Surfaces: agent-chat, agent-config
Auditability: observational
Judgment signals:
- Find creation surfaces (new/create routes, empty editors, blank composition areas).
- Check whether agent-generated content or templates are offered on first load.
- Flag blank-canvas surfaces with no generative starting point where the agent has enough context.
Concrete commands:
rg '(/new|/create|/compose|/draft)' --type=ts -l src/
rg '(EmptyState|BlankCanvas|emptyDocument|initialContent:\s*["'"'"']{2})' --type=ts -l src/
rg '(generateDraft|suggestDraft|aiDraft|startWithAI|TemplatePicker)' --type=ts -l src/False-positive guards:
- Skip files with
// ax-audit-ignore:comm-no-generative-momentum. - Skip test/Storybook fixtures and code editors where blank is the expected state.
Fix
Offer an agent-generated draft on blank-canvas surfaces: "Start with AI draft" button, template suggestions, or outline. Always let the user dismiss and start from scratch.
Default tier and overrides
Defaults to: backlog
| Surface | Tier |
|---|---|
| Agent chat | backlog |
| Agent config | backlog |
Examples
Anti-pattern (fails):
export function NewReport() {
// Agent has project data, metrics, goals: offers nothing
return <RichTextEditor initialContent="" />;
}Applied (passes):
export function NewReport() {
const project = useProject();
const { suggestion, dismiss } = useAgentSuggestion({
prompt: `Draft a report outline for ${project.name}`,
});
return (
<div>
{suggestion && (
<Banner onAccept={() => editor.setContent(suggestion)} onDismiss={dismiss}>
Start with AI outline?
</Banner>)}
<RichTextEditor ref={editor} />
</div>
);
}Suppression
{/* ax-audit-ignore:comm-no-generative-momentum, code editor, blank canvas is intentional */}
<CodeEditor />Agent acts on non-trivial request without confirming intent
User says "reorganize my files." Agent immediately starts moving files. User meant "suggest a new folder structure" not "execute a restructure right now." Intent Handshake requires agents to play back their interpretation before executing. The gap between intent and interpretation is invisible until the damage is done.
What goes wrong
User asks "clean up my project." Agent deletes unused files, renames directories, and updates imports, all in one shot. User wanted a report. No playback, no scoping choices, no "here's what I'll do" before action. Destructive and ambiguous requests get instant-execute treatment.
Detection
Surfaces: agent-chat, agent-tool-execution
Auditability: hybrid
Static signals: 1. Find agent action triggers for non-trivial operations (multi-step, destructive, ambiguous). 2. Check for a confirmation/playback step between request and execution. 3. Flag direct execution of complex requests with no preview.
Concrete commands:
rg '(executeTool|runAction|performAction|handleToolCall)' --type=ts -l src/
rg '(delete|remove|move|rename|reorganize|migrate|deploy|publish)' --type=ts src/tools/ src/actions/
rg '(confirm|approval|preview|playback|requireApproval)' --type=ts src/
rg '(autoExecute|skipConfirm|auto_approve)' --type=ts src/Judgment signals:
- Trivial, unambiguous requests ("what time is it?") don't need a handshake.
- Targets multi-step, destructive, ambiguous, or high-stakes requests.
False-positive guards:
- Skip files with
// ax-audit-ignore:comm-no-intent-handshake. - Skip test and Storybook fixtures.
- Skip read-only operations (queries, lookups, status checks).
Fix
Before executing non-trivial actions, play back understanding: "I'll reorganize your files by moving X to Y. Proceed?" Options: text playback, structured plan preview, or scoping choices.
Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | fix-this-sprint |
Examples
Anti-pattern (fails):
async function onToolCall(tool: string, args: Record<string, unknown>) {
const result = await tools[tool].execute(args); // no confirmation, even for destructive ops
return { role: "tool", content: result };
}Applied (passes):
async function onToolCall(tool: string, args: Record<string, unknown>) {
const meta = tools[tool].metadata;
if (meta.destructive || meta.multiStep)
return { type: "pending_approval", message: `I'll ${meta.describe(args)}. Proceed?`,
onApprove: () => tools[tool].execute(args) };
return tools[tool].execute(args);
}Suppression
{/* ax-audit-ignore:comm-no-intent-handshake, read-only lookup, no side effects */}
<QuickSearchAgent />Multi-step agent task shows no progress
Agent runs a task that takes 30+ seconds. The UI shows nothing: no streaming, no step counter, no thinking indicator. User doesn't know if it's working, stuck, or crashed. Silent agents feel broken.
What goes wrong
User asks the agent to analyze a dataset. Three tool calls, API waits, synthesis, 45 seconds. The user sees a spinner or nothing. At 15 seconds they wonder if it's broken. At 30 they refresh.
Detection
Surfaces: agent-chat, agent-tool-execution, agent-dashboard
Auditability: code-auditable
Static signals: 1. Find agent invocation code (chat submit handlers, tool execution triggers). 2. Check for streaming (onChunk, onToken, SSE, useChat) or progress events (onProgress, onStatus). 3. Flag agent calls with only a final result handler and no intermediate feedback.
Concrete commands:
rg '(useChat|useCompletion|agent\.chat|agent\.run|streamText|generateText)' --type=ts -l src/
rg '(onChunk|onToken|onProgress|onStatus|stream:\s*true)' --type=ts src/
rg -A 10 '(executeTool|runTool|toolCall)' --type=ts src/ | rg -v '(onProgress|onStatus|stream)'False-positive guards:
- Skip files with
// ax-audit-ignore:comm-no-progress-signal. - Skip test and Storybook fixtures.
- Skip agent calls that reliably complete in under 2 seconds.
Fix
Stream responses incrementally. Show a thinking indicator. For multi-step tasks, emit step-level progress: "Thinking..." then "Searching for X..." then "Found 3 results, analyzing..." then final response.
Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | release-blocker |
| Agent dashboard | fix-this-sprint |
Examples
Anti-pattern (fails):
async function onAsk(query: string) {
const data = await fetch("/api/agent/research", {
method: "POST", body: JSON.stringify({ query }),
}).then((r) => r.json()); // 30-60s silence, no feedback
setResult(data);
}Applied (passes):
export function ResearchPanel() {
const [steps, setSteps] = useState<string[]>([]);
const { data, isStreaming } = useAgentStream("/api/agent/research", {
onStatus: (s) => setSteps((prev) => [...prev, s]),
});
return <>
{isStreaming && <ProgressList steps={steps} current={steps.at(-1)} />}
{data && <Results data={data} />}
</>;
}Suppression
{/* ax-audit-ignore:comm-no-progress-signal, instant lookup, sub-second response */}
<QuickLookup />Agent uses context the user can't see or edit
Agent injects preferences, past interactions, or learned patterns into its prompt, but the user can't see what the agent "knows" about them. Opaque memory feels invasive. Memory in Motion requires every piece of stored context to have a user-facing view and edit path.
What goes wrong
Agent says "Based on your preference for concise answers..." and the user thinks "What preference? I never said that." The system built a profile from past interactions, injected it into the system prompt, and the user had zero visibility. No settings page, no memory panel, no way to correct it.
Detection
Surfaces: agent-chat, agent-config, agent-dashboard
Auditability: code-auditable
Static signals: 1. Find context injection points (prompt builders, context loaders, preference injectors). 2. Search for UI that exposes this context (settings pages, memory panels). 3. Flag injected context with no user-facing view or edit path.
Concrete commands:
rg '(systemPrompt|buildPrompt|contextLoader|injectContext|userPreferences|userMemory)' --type=ts -l src/
rg '(MemoryPanel|PreferencesView|WhatIKnow|MemorySettings)' --type=ts -l src/
rg '(savePreference|updateMemory|storePattern|learnFrom)' --type=ts -l src/False-positive guards:
- Skip files with
// ax-audit-ignore:context-memory-not-visible. - Skip test and Storybook fixtures.
- Skip internal admin-only agent tools where the operator is the developer.
Fix
For every piece of context injected into the agent prompt, provide a corresponding UI where the user can view and edit it. A "Memory" or "What I know about you" panel with edit/delete per item.
Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent chat | fix-this-sprint |
| Agent config | fix-this-sprint |
| Agent dashboard | backlog |
Examples
Anti-pattern (fails):
async function getAgentContext(userId: string) {
const prefs = await redis.get(`user:${userId}:prefs`);
const history = await redis.get(`user:${userId}:patterns`);
return { preferences: prefs, patterns: history }; // never shown to user
}Applied (passes):
// Context store is shared: same data feeds the agent AND the settings UI
async function getAgentContext(userId: string) {
return await getVisibleMemory(userId); // MemorySettings reads the same store
}
function MemorySettings() {
const memory = useMemory();
return memory.items.map((m) => (
<li key={m.id}>{m.summary} <button onClick={() => memory.delete(m.id)}>Delete</button></li>
));
}Suppression
{/* ax-audit-ignore:context-memory-not-visible, internal dev tool, operator is the developer */}
<AgentPromptBuilder />Interface static during agent task progression
Agent moves through phases (researching, drafting, reviewing, complete) but the UI looks identical in every phase. No phase indicator, no layout change, no context-appropriate tools surfaced. Adaptive Canvas requires the interface to reshape itself around the agent's current activity.
What goes wrong
Agent starts a research task. User sees "Searching..." then nothing changes for 45 seconds. The agent transitions through phases but the layout never shifts. No stepper, no phase-specific controls. User doesn't know where the agent is or how close to done.
Detection
Surfaces: agent-tool-execution, agent-dashboard, agent-config
Auditability: code-auditable
Static signals: 1. Find agent workflow state: phase, status, or stage enums/state machines. 2. Check whether rendering differs across phases (conditional rendering, different components per phase). 3. Flag workflows where UI is identical regardless of agent phase.
Concrete commands:
rg '(phase|stage|status|workflow).*(enum|type|const)' --type=ts src/
rg '(stateMachine|createMachine|useReducer|switch.*phase)' --type=ts src/
rg '(Stepper|ProgressBar|PhaseIndicator|StageIndicator)' --type=ts -l src/False-positive guards:
- Skip files with
// ax-audit-ignore:context-no-adaptive-canvas. - Skip test and Storybook fixtures.
- Skip single-step agent interactions where no multi-phase workflow exists.
Fix
Show a phase indicator (stepper, progress bar). Surface phase-appropriate tools (research tools during research, editing tools during review). Reshape the layout to match the current activity.
Default tier and overrides
Defaults to: backlog
| Surface | Tier |
|---|---|
| Agent tool execution | fix-this-sprint |
| Agent dashboard | backlog |
| Agent config | backlog |
Examples
Anti-pattern (fails):
function ResearchAgent({ status }: { status: string }) {
// status is "searching" | "analyzing" | "complete": UI never changes
return <div className="flex"><ChatPanel /><Sidebar /></div>;
}Applied (passes):
function ResearchAgent({ status, data }: { status: AgentStatus; data: AgentData }) {
return (
<div>
<Stepper steps={["Searching", "Analyzing", "Complete"]} current={status} />
{status === "searching" && <SearchProgress queries={data.queries} />}
{status === "analyzing" && <AnalysisView sources={data.sources} />}
{status === "complete" && <ResultsView results={data.results} />}
</div>
);
}Suppression
{/* ax-audit-ignore:context-no-adaptive-canvas, single-turn chat, no multi-phase workflow */}
<AgentChat />Agent ignores available context it should use
The system has the user's project history, preferences, recent activity, and team context, but the agent's prompt doesn't include any of it. The agent asks questions it should already know the answer to. Being under-contextual wastes time and makes the agent feel stupid.
What goes wrong
User opens a project page and asks "help me write a status update." Agent responds: "What project are you working on?" The project name, recent commits, and open tickets are all in the app state, but the prompt ignores them. Every unnecessary question erodes confidence.
Detection
Surfaces: agent-chat, agent-tool-execution
Auditability: hybrid
Static signals: 1. Catalog available context sources (user profile, project state, recent activity, team info). 2. Find agent prompt/context assembly functions. 3. Check whether available sources are referenced in context injection. 4. Flag significant context sources never passed to the agent.
Concrete commands:
rg '(useUser|useProject|useTeam|useActivity|currentProject|activeWorkspace)' --type=ts -l src/
rg '(buildPrompt|systemPrompt|assembleContext|getAgentContext)' --type=ts -l src/
rg -A 15 '(buildPrompt|assembleContext|getAgentContext)' --type=ts src/Judgment signals:
- Would a human assistant in this position already know the answer?
- Is the missing context high-signal (project name, recent activity) or low-signal?
False-positive guards:
- Skip files with
// ax-audit-ignore:context-under-contextual. - Skip test/Storybook fixtures and generic agent surfaces with no page-specific context.
Fix
Inject relevant context at session start using the context.md pattern: "What I Know About This User," "What Exists," "Recent Activity." Update dynamically during the session.
Default tier and overrides
Defaults to: backlog
| Surface | Tier |
|---|---|
| Agent tool execution | fix-this-sprint |
| Agent chat | backlog |
Examples
Anti-pattern (fails):
// User is on /projects/acme-redesign but agent gets no project context
export function ProjectAgent() {
const { sendMessage } = useAgent({ system: "You are a helpful assistant." });
return <AgentChat onSend={sendMessage} />;
}Applied (passes):
export function ProjectAgent() {
const project = useProject();
const activity = useRecentActivity(project.id);
const { sendMessage } = useAgent({
system: `Assistant for ${project.name}. Recent: ${activity.map((a) => a.summary).join("; ")}`,
});
return <AgentChat onSend={sendMessage} />;
}Suppression
{/* ax-audit-ignore:context-under-contextual, generic help chat, no page context needed */}
<HelpAgent />Autonomous agent action without stakes-appropriate approval
Agent sends an email, posts to Slack, or deletes data without asking. Or: agent asks confirmation for every trivial action. Either extreme breaks trust: too autonomous or too cautious. The approval model must match the stakes and reversibility of the action.
What goes wrong
Scenario A: User says "clean up my calendar." Agent deletes meetings including one with the VP. No confirmation. Scenario B: Agent asks "Move report.pdf? [Yes/No]" for 40 files. User gives up at file 12. Both are approval mismatches.
Detection
Surfaces: agent-tool-execution, agent-chat
Auditability: hybrid
Static signals: 1. Find agent-initiated side effects (send, delete, create, publish). 2. Classify by stakes and reversibility. Check whether approval precedes high-stakes actions. 3. Flag mismatches in both directions.
Concrete commands:
rg -l 'sendEmail|sendMessage|deleteAccount|publishPost|processPayment' --type=ts src/
rg -B 10 'sendEmail|delete|publish' --type=ts src/ | rg 'confirm|approval|modal'Judgment signals:
- "User-requested" vs. "agent-initiated" matters. "Clean up my inbox" per-email = user-requested. Agent proactively acting = agent-initiated.
- A single "Are you sure?" for 50 actions is insufficient.
False-positive guards:
- Skip
// ax-audit-ignore:control-no-approval-gate, test, and Storybook files.
Fix
Implement the stakes x reversibility matrix. Low/easy: auto-apply. Low/hard: quick confirm. High/easy: show diff. High/hard: explicit modal approval.
Examples
Anti-pattern (fails):
async function handleSendEmail(draft: EmailDraft) {
await emailClient.send(draft);
return { status: "sent", message: `Email sent to ${draft.to}` };
}Applied (passes):
async function handleSendEmail(draft: EmailDraft, ctx: AgentContext) {
const approved = await ctx.modalApproval({
title: `Send email to ${draft.to}?`,
preview: <EmailPreview draft={draft} />,
actions: ["Send", "Edit", "Cancel"],
});
if (!approved) return { status: "cancelled" };
await emailClient.send(draft);
return { status: "sent" };
}Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | release-blocker |
| Agent config | fix-this-sprint |
| Agent dashboard | fix-this-sprint |
Suppression
{/* ax-audit-ignore:control-no-approval-gate, user opted into auto-apply mode */}
<AutoApplyToggle enabled={userPreference.autoApply} />No way to interrupt, redirect, or undo agent action
Agent starts a long response or multi-step workflow. User realizes it's wrong. No stop button, no undo, no "go back." User watches the agent do the wrong thing and can't intervene. Autonomy without exit is coercion.
What goes wrong
User asks the agent to refactor a module. Agent begins a 12-step migration. After step 3, user sees it's the wrong approach. No stop button. Agent runs to completion, leaving the codebase in an unwanted state. Manual revert takes longer than doing it themselves.
Detection
Surfaces: agent-chat, agent-tool-execution
Auditability: hybrid
Static signals: 1. Find agent execution UI (chat panels, action panels, tool execution views). 2. Check for cancel/stop during execution (onCancel, AbortController). 3. Check for undo/revert after completion. Flag flows with neither.
Concrete commands:
rg -l 'AbortController|onCancel|stopGenerat' --type=ts src/
rg -A 10 'isGenerating|isStreaming|isPending' --type=ts src/ | rg -v 'cancel|stop|abort'Judgment signals:
- A cancel button not wired to
AbortController.abort()is a false affordance, worse than nothing.
False-positive guards:
- Skip
// ax-audit-ignore:control-no-escape-hatch, test, and Storybook files.
Fix
During execution: stop button wired to AbortController. After completion: undo/revert for reversible actions. For irreversible actions, the approval gate (control-no-approval-gate) is the pre-execution escape hatch.
Examples
Anti-pattern (fails):
<div>
{messages.map((m) => <Message key={m.id} {...m} />)}
{isGenerating && <Spinner />}
{/* no stop button, no undo */}
</div>Applied (passes):
<div>
{messages.map((m) => <Message key={m.id} {...m} />)}
{isGenerating && (
<>
<Spinner />
<Button onClick={onStop} aria-label="Stop generating">Stop</Button>
</>
)}
{!isGenerating && <Button onClick={onUndo} variant="ghost">Undo</Button>}
</div>Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | release-blocker |
| Agent config | fix-this-sprint |
| Agent dashboard | fix-this-sprint |
Suppression
{/* ax-audit-ignore:control-no-escape-hatch, single status check, completes in <1s */}
<StatusCheckResult result={result} />Chat interface for actions that should be buttons
User wants to toggle a setting or trigger a known action. Only interface is chat. User types "turn on dark mode" and waits for a round-trip instead of flipping a switch. Chat is the ONLY path to deterministic actions.
What goes wrong
User wants dark mode. Types "enable dark mode" in chat. Agent responds after 2 seconds. Same action could be a toggle taking 50ms. Multiply across every simple action and chat becomes a bottleneck.
Detection
Surfaces: agent-chat
Auditability: observational
Static signals: 1. Find chat input surfaces. 2. Identify deterministic actions achievable through chat (toggles, selections, CRUD). 3. Flag cases where chat is the only path to a simple action.
Concrete commands:
rg -l 'ChatInput|MessageInput|PromptInput' --type=ts src/
rg -l 'Toggle|Switch|Select|Dropdown' --type=ts src/components/Judgment signals:
- The anti-pattern is chat-only for deterministic actions. Some conversational interface is expected.
False-positive guards:
- Skip
// ax-audit-ignore:control-over-conversational, test, and Storybook files.
Fix
Add direct-manipulation controls alongside chat: quick-action buttons, command palette, context menus. Keep chat for ambiguous or multi-step requests.
Examples
Anti-pattern (fails):
<div>
<DataTable data={data} />
<AgentChat onSend={handleAgentCommand} /> {/* no sort, filter, or action controls */}
</div>Applied (passes):
<div>
<DataTable data={data} onSort={handleSort} sortable />
<QuickActions actions={[{ label: "Export CSV", handler: exportCsv }]} />
<AgentChat onSend={handleAgentCommand} />
</div>Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent tool execution | backlog |
| Agent chat | fix-this-sprint |
| Agent config | fix-this-sprint |
| Agent dashboard | fix-this-sprint |
Suppression
{/* ax-audit-ignore:control-over-conversational, chat-first product by design */}
<AgentChat onSend={onSend} />Agent output with no rationale or sources
Agent says "You should refactor this function" with no explanation of why. User has no way to evaluate the advice: follows it blindly or ignores it entirely. Neither builds trust.
What goes wrong
Agent responds with a confident directive and nothing else. User cannot tell if advice comes from docs, past conversations, or hallucination. When the advice is wrong once, user stops trusting all future responses because there was never a way to distinguish good answers from bad.
Detection
Surfaces: agent-chat, agent-dashboard
Auditability: hybrid
Static signals: 1. Find agent output components (role="assistant", <AssistantMessage>, <AiResponse>). 2. Check for citation, source, reasoning, or thinking child components. 3. Flag output containers with zero rationale children.
Concrete commands:
rg -l 'role.*assistant|AssistantMessage|AiResponse|completion' --type=ts src/
rg -A 15 'role.*assistant|<AssistantMessage|<AiResponse' --type=ts src/ | rg -v 'Citation|Source|Reasoning|Thinking'Judgment signals:
- Even if
<Sources>exists, check whether it's populated vs. always empty. - A rationale section for some response types but not others is a partial pass.
False-positive guards:
- Skip
// ax-audit-ignore:trust-no-confidence-cues, test, and Storybook files. - Skip status-only messages ("Done!" confirmations).
Fix
Add inline rationale: sources, reasoning steps, or a collapsible thinking section.
Examples
Anti-pattern (fails):
<div className="agent-response" role="assistant">
<Markdown>{completion.text}</Markdown>
</div>Applied (passes):
<div className="agent-response" role="assistant">
<Markdown>{completion.text}</Markdown>
{completion.reasoning && <ThinkingBlock steps={completion.reasoning} />}
{completion.sources.length > 0 && <CitationList sources={completion.sources} />}
</div>Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent tool execution | fix-this-sprint |
| Agent chat | fix-this-sprint |
| Agent config | backlog |
| Agent dashboard | fix-this-sprint |
Suppression
{/* ax-audit-ignore:trust-no-confidence-cues, status-only messages need no rationale */}
<AgentMessage content={statusText} />High-stakes agent action with no human escalation
Agent handles a refund request, medical question, or legal inquiry with no way to hand off to a human. It either gives a dangerous answer or refuses entirely. An escalation path is the trust floor.
What goes wrong
User asks about a billing dispute. Agent applies a partial credit that doesn't match. No "talk to a person" button. Agent keeps trying, making things worse. User files a chargeback.
Detection
Surfaces: agent-tool-execution, agent-chat
Auditability: code-auditable
Static signals: 1. Find action handlers for high-stakes operations (financial, medical, legal, account deletion). 2. Check for escalation/handoff logic. Flag high-stakes handlers with no escalation path.
Concrete commands:
rg -l 'refund|payment|delete.*account|send.*email|legal|medical' --type=ts src/
rg 'escalat|handoff|transfer.*human|transfer.*agent' --type=ts src/Judgment signals:
- An escalation tool never referenced in the system prompt is effectively invisible.
False-positive guards:
- Skip
// ax-audit-ignore:trust-no-escalation-path, test, and Storybook files.
Fix
Add escalate_to_human(reason, context) as an agent tool. Surface it in the UI as "Talk to a person."
Examples
Anti-pattern (fails):
const agentTools = {
processRefund: async (amount: number) => {
await api.refund(amount);
return { success: true, message: "Refund processed." };
},
};Applied (passes):
const agentTools = {
processRefund: async (amount: number) => {
if (amount > ESCALATION_THRESHOLD) return { escalate: true, reason: "Exceeds limit" };
await api.refund(amount);
return { success: true };
},
escalateToHuman: async (reason: string, ctx: AgentContext) => {
await support.transfer({ reason, transcript: ctx.messages });
return { message: "Connecting you with a team member." };
},
};Default tier and overrides
Defaults to: release-blocker
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | release-blocker |
| Agent config | backlog |
| Agent dashboard | fix-this-sprint |
Suppression
{/* ax-audit-ignore:trust-no-escalation-path, internal admin tool, operator is the human */}
<AgentToolPanel tools={adminTools} />Agent presents everything with equal certainty
Agent is 95% sure about one recommendation and 40% sure about another, but both render identically. When the 40% answer is wrong, user doesn't just distrust that answer. They distrust everything. Confident wrong answers cause permanent trust damage.
What goes wrong
Two recommendations in the same response: one well-supported, one a guess. Same font, same weight, same formatting. User treats both as equally reliable. The guess is wrong. User now second-guesses every future response. Trust is binary when the interface gives no gradient.
Detection
Surfaces: agent-chat, agent-dashboard
Auditability: observational
Static signals: 1. Find agent output containers. 2. Check for confidence props (confidence, certainty, score) or uncertainty components. 3. Absence of all = flag.
Concrete commands:
rg 'confidence|certainty|ConfidenceBadge|UncertaintyIndicator' --type=ts src/Judgment signals:
- Hedging in prompt instructions is weaker than structured indicators but better than nothing.
- A badge always showing "high" is not meaningful: check for actual variation.
False-positive guards:
- Skip
// ax-audit-ignore:trust-no-uncertainty-markers, test, and Storybook files. - Skip trivial outputs (confirmations, acknowledgments) where confidence is always 100%.
Fix
Add confidence indicators: numeric score, visual badge (high/medium/low), hedging language, or expandable reasoning that shows uncertainty.
Examples
Anti-pattern (fails):
<ul>
{recommendations.map((rec) => (
<li key={rec.id}>{rec.text}</li>
))}
</ul>Applied (passes):
<ul>
{recommendations.map((rec) => (
<li key={rec.id}>
{rec.text}
<ConfidenceBadge level={rec.confidence > 0.8 ? "high" : "low"} />
</li>
))}
</ul>Default tier and overrides
Defaults to: fix-this-sprint
| Surface | Tier |
|---|---|
| Agent tool execution | release-blocker |
| Agent chat | fix-this-sprint |
| Agent config | backlog |
| Agent dashboard | fix-this-sprint |
Suppression
{/* ax-audit-ignore:trust-no-uncertainty-markers, deterministic lookups, no uncertainty */}
<AgentRecommendation text={result.text} />