Ask
Queries Span for organizational observability across six domains: productivity (cycle time, throughput, review metrics), DORA (deployment frequency, lead time, MTTR, change failure rate), investment (effort allocation, workstreams, cost capitalization), AI transformation (AI code ratio, adoption, spend), calendar (focus time, meeting load), and AI traces (agent sessions — what happened inside them). Use when asked about engineering metrics, team velocity, pull requests, commits, deployments, epics, issues, sprints, investments, teams, or people; or about agent sessions, coding-agent or Claude Code sessions, agent traces, skill usage, prompts, tool calls, or AI-agent behavior.From its SKILL.md
npx -y skills add Attuned-Corp/skills --skill askAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
20.0 KB, as published. Nobody here has run it
Span
If the user provided arguments with this invocation, treat $ARGUMENTS as their query and proceed directly (still ask for clarification if the time range is missing).
Script & Path Setup
All bash commands in this skill should start by setting these two variables:
SPAN_DIR="${SPAN_CONFIG_DIR:-$HOME/.spanrc}"
SKILL_SCRIPTS="${CLAUDE_SKILL_DIR}/scripts"
Use $SKILL_SCRIPTS/query.sh, $SKILL_SCRIPTS/fetch-metadata.sh, etc. for all API operations. Always prefer the scripts over inline curl commands.
Invocation Flow
- Check configuration state — the dynamic context injection below tells you if the skill is configured
- If not configured — run the First-Time Setup flow before anything else
- Check metadata cache — load
$SPAN_DIR/metadata-cache.json, or fetch if missing - Ask clarifying questions — especially for missing time ranges
- Plan query — search metadata for metrics, choose asset level, use pre-aggregated metrics
- Execute query — write query JSON to a temp file, run
$SKILL_SCRIPTS/query.sh - Format and return results — convert units, handle pagination
Configuration State
!${CLAUDE_SKILL_DIR}/scripts/check-config.sh
First-Time Setup
If the configuration state above shows "api version mismatch", warn the user that their installed skill version may be incompatible with the current Span API, and suggest they update the skill. You may still attempt queries, but results may be unreliable.
If the configuration state above shows "not configured", you MUST run the onboarding flow before doing anything else:
-
Create the configuration folder:
SPAN_DIR="${SPAN_CONFIG_DIR:-$HOME/.spanrc}" mkdir -p "$SPAN_DIR" -
Prompt user to add their token to
$SPAN_DIR/auth.json:{ "token": "your-personal-access-token" } -
Verify the token file exists by running
$SKILL_SCRIPTS/check-config.sh(never readauth.jsondirectly). -
Fetch initial metadata:
$SKILL_SCRIPTS/fetch-metadata.sh -
Confirm setup is complete and proceed with the user's original request.
Configuration Directory
The skill stores configuration in ~/.spanrc/ by default. Override with SPAN_CONFIG_DIR:
~/.spanrc/ # or $SPAN_CONFIG_DIR
├── auth.json # Span Personal Access Token (required)
└── metadata-cache.json # Cached API metadata (auto-generated)
Token Security (CRITICAL)
NEVER read, print, or expose the Personal Access Token. The scripts in $SKILL_SCRIPTS/ handle authentication internally. You must NOT:
- Read
auth.json(never use the Read tool on this file) - Echo, print, or log the token value
- Construct inline
curlcommands with the token — use the scripts instead - Include the token in error messages or output
Clarifying Questions (IMPORTANT)
Always ask clarifying questions rather than making assumptions.
Time Intervals
If the user's query does not specify a time range, always ask before executing:
"What time period would you like me to query? For example: last 30 days, last quarter, or a specific date range. (If you'd like, I can default to the last 30 days.)"
Do NOT assume a time range. Even if the query sounds like it implies "recent" data, ask explicitly.
Examples requiring clarification:
- "How many PRs did we merge?" → Ask for time range
- "What's our cycle time?" → Ask for time range
Examples where time range is provided (no need to ask):
- "PRs merged last week" → Use last 7 days
- "Cycle time for Q4" → Use Q4 date range
Other Ambiguities
Also ask for clarification when:
- Team/person is unclear: "Which team would you like me to query?"
- Metric is ambiguous: "Did you mean X or Y metric?"
- Scope is unclear: "Should this be organization-wide or for a specific team?"
For trace questions, don't ask whose sessions you may read — permissions decide that, and it isn't knowable in advance. Query, then report the scope you actually measured (see references/domains.md → AI Traces). Still ask for the time range.
Metadata Caching
API metadata (assets, fields, metrics) is cached at $SPAN_DIR/metadata-cache.json. If the cache exists, use it. If not, run $SKILL_SCRIPTS/fetch-metadata.sh. Only refresh when the user explicitly asks to reload.
Query Planning (MANDATORY)
Before executing any query, you MUST follow this process:
Step 0: Verify Feasibility
Before building any query, confirm:
- The requested metric exists in cached metadata
- The requested asset type exists (Team, Person, PullRequest, etc.)
- The metric is available on that asset type
Trace facades are gated. Trace, TraceEvent, TraceTurn and TraceRecommendation are absent from metadata unless the org has AI traces enabled and the token carries trace access. If they're missing after a refresh, report that — not "no agent activity".
If verification fails, prioritize partial fulfillment:
- Execute what IS possible, return available data
- Note what couldn't be included and why
- Suggest alternatives from available metrics/assets
Only refuse entirely when the core metric/asset is completely unavailable.
Step 1: Check Available Metrics
ALWAYS inspect the cached metadata to find relevant metrics:
SPAN_DIR="${SPAN_CONFIG_DIR:-$HOME/.spanrc}"
cat "$SPAN_DIR/metadata-cache.json" | jq '.data[].metrics[]? | select(.label | test("<keyword>"; "i"))'
NEVER calculate or aggregate metrics yourself if a pre-built metric exists. The API provides pre-computed metrics that are more accurate and efficient than manual calculations.
NEVER guess or fabricate metric IDs. Always use the exact metricId UUIDs returned by the cached metadata. Hallucinated metric IDs will cause query failures.
Step 2: Determine the Right Asset Level
Assets act as aggregation points (like SQL GROUP BY). Choose the appropriate asset:
| User asks about... | Query this asset | Filter by |
|---|---|---|
| Organization/company-wide (most metrics) | Team | Team.name = "<root team>" — the org root; see note below |
| Organization-wide deployments | Team (mode: "groups") | Team.groupPath DESCENDANT_OF the org root path — Team.name is leaf-only for deploys and does NOT work here; see "Deployment metrics across a team tree" |
| A specific team's metrics | Team | Team.name = "<team-name>" (use =, NOT DESCENDANT_OF) |
| People in a team and sub-teams | Person | Person.Teams.name with DESCENDANT_OF operator |
| Metrics across a team tree (most metrics) | Person | Person.Teams.name with DESCENDANT_OF + metrics on Person — does NOT work for deployment metrics, see below |
| Deployment metrics for a team + its sub-teams | Team (mode: "groups") | Team.groupPath DESCENDANT_OF the team's path — see "Deployment metrics across a team tree" below |
| A specific person | Person or PullRequest.Author | Email or name |
| A specific repository | Repository or PullRequest.Repository | Repository name |
| Individual PRs/commits | PullRequest or Commit | As needed |
| Volume/cost/adoption of agent sessions | Team or Person + trace metrics | Normal metric loop |
| Agent sessions broken down by tool/model/task category | Trace (mode: "groups") | Group by a dimension (aiTraceTool, …), never by the Author/Team relation |
| What happened inside agent sessions (skills, prompts, tool calls, files) | Trace → TraceTurn → TraceEvent | Corpus queries, metrics: [] — see domains.md |
| Breakdown by dimension (tenure, job level, etc.) | Use mode: "groups" | See api-reference.md |
IMPORTANT: For org-wide metrics, query Team filtered by the root team — the one whose Team.path has no . separator (just <hash>__<slug>). Find it by listing teams: {"select": ["Team.name", "Team.path"]}. Do NOT manually aggregate across repositories or people. Exception — deployments (and other team-attributed metrics): Team.name returns only that team's own deploys, so even the org-wide total must use the Team.groupPath DESCENDANT_OF roll-up anchored at the org root path — see "Deployment metrics across a team tree".
Common mistake — querying the wrong facade for "metric by X":
"PR count by repository" queried on the PullRequest facade returns one row per PR (each with count=1) — useless for aggregation. Query the Repository facade instead to get one row per repo with aggregated counts. Same applies to "by team" (use Team), "by person" (use Person). If "by X" refers to a dimension without its own facade (tenure, job level), use mode: "groups" instead.
Team Hierarchy: = vs DESCENDANT_OF vs IN
=— Use for metrics on a single team.Team.name = "Backend"returns that team's aggregated metrics. For most metrics this already rolls up the team's sub-teams (theTeamfacade aggregates hierarchically). Exception: deployment metrics are leaf-only — see "Deployment metrics across a team tree" below.DESCENDANT_OF— Use for roster discovery (listing people), and for the deployment roll-up below. For mostTeammetric queries do NOT use it —Team.name = "X"already includes sub-team data, so it would double-count.IN— Use to query a specific set of teams by name.
Most metrics across a team tree: Query Person (not Team) with DESCENDANT_OF on Person.Teams.name, then attach metrics to Person. Individual-level metrics don't double-count.
Deployment metrics across a team tree
Deployment metrics are attributed to teams, not people (a deploy has no author), which makes them behave differently from the rest:
- The
Person+DESCENDANT_OFpattern above does not apply — there's no person to attach them to. Team.name = "X"returns only that team's own deploys; unlike other metrics it does not roll up sub-teams.
To total a team and all of its sub-teams, roll up over the team's subtree with Team.groupPath (metadata describes it: "Filter with DESCENDANT_OF to scope a metric to a team and all of its subteams"):
- Get the target team's path —
{ "select": ["Team.name", "Team.path"] }— and read itsTeam.path(an ltree, e.g."<org-id>__organization.platform"). For the org-wide total, use the root team's path (the one with no.). - Roll up in groups mode:
{
"mode": "groups",
"select": ["Team.groupPath"],
"metrics": [{ "metricId": "<deploy metric id from metadata>" }],
"filters": [{ "field": "Team.groupPath", "operator": "DESCENDANT_OF", "value": "<Team.path>" }],
"timeDimension": { "timeRange": { "startTime": "...", "endTime": "..." } }
}
You get back one row — the subtree total, de-duplicated across teams (add granularity for a per-period series). That de-duplication is the reason to use the roll-up rather than fetching per-team rows and adding them up.
Complete working example — org-wide deployments for a date range (the value is the org root Team.path; for a single team + its sub-teams, use that team's Team.path instead):
{
"mode": "groups",
"select": ["Team.groupPath"],
"metrics": [
{ "metricId": "c3377814-3805-4803-89ba-6f77bb3a8907", "responseKey": "deploys" }
],
"filters": [
{
"field": "Team.groupPath",
"operator": "DESCENDANT_OF",
"value": "991ab8636a4c495e9febbd48509e480f__organization"
}
],
"timeDimension": {
"timeRange": { "startTime": "2026-04-01", "endTime": "2026-04-11" }
}
}
Returns { "data": [{ "deploys": <subtree total> }] }. Notes: the value is org-specific — it comes from Team.path (step 1), never hard-code it. The metricId shown is the "Deployments" total-count metric; confirm the current id from metadata (pick by id, not label — labels are shared across metric versions).
Three things to get right:
- Pick the metric by id, not label — V1 and V2 share labels.
Team.groupPathis the subtree anchor only — it's filter-only and dropped from the output, so it can't double as a breakdown (doing so returns a 400).- One grain per query — a groups-mode query can't mix deploy (team) metrics with person metrics; run them as separate queries.
For additional patterns (top-level team discovery, manager lookups), see references/api-reference.md.
Step 3: Use Pre-Aggregated Metrics
When using granularity in the time dimension, the API returns already-aggregated values. Do not perform additional aggregation on these results.
Bad pattern (DO NOT DO THIS):
- Query individual PRs
- Extract cycle times
- Calculate average manually
Good pattern (DO THIS):
- Query Team with
Team.name = "Organization" - Include the cycle time metric
- Use granularity for time series
- Return the pre-aggregated results directly
When comparing time periods, always use the same asset, metric, and query path for both periods. Inconsistent query paths (e.g., different facades or aggregation levels) produce incomparable numbers.
Exception — trace corpus queries. TraceEvent and TraceTurn have no metrics and no server-side grouping, so "which skills are most used" has no pre-built metric to find. There, send "metrics": [], page the rows, and aggregate them yourself with jq. This is the only domain where client-side aggregation is correct.
Step 4: Plan Multi-Step Queries
For complex queries requiring multiple API calls, use TaskCreate to track steps:
Example: "Compare PR cycle time for top 5 teams over last 30 days"
- Verify pr_cycle_time metric exists on Team asset
- Fetch teams sorted by PR volume, limit 5
- Get pr_cycle_time for each team with date filter
- Format and present results
Execute independent API calls in parallel, but limit to 2 concurrent calls maximum.
Superlative queries ("most", "least", "highest", "top N"): ensure you paginate through the full dataset. A query with limit=25 may miss the actual top result if there are more than 25 rows.
Querying the API
Always use the query script. Write the query JSON to a temp file, then execute:
cat > /tmp/span-query.json << 'EOF'
{
"select": ["Team.name"],
"filters": [{"field": "Team.name", "operator": "=", "value": "Organization"}],
"metrics": [{"metricId": "<metric-id-from-cache>", "responseKey": "cycleTime"}],
"timeDimension": {
"timeRange": {"startTime": "2024-05-01", "endTime": "2024-06-01"},
"granularity": "week"
}
}
EOF
$SKILL_SCRIPTS/query.sh /tmp/span-query.json
For paginated results, pass limit and cursor: $SKILL_SCRIPTS/query.sh /tmp/span-query.json 25 <cursor>
See references/api-reference.md for full request body format, filter operators, time dimensions, and groups mode.
Formatting Results
Always check annotations in the API response for each metric's unit. The annotations tell you exactly how to interpret the raw values.
Possible units: seconds, hours, days, usd, count, percentage_as_ratio, score, boolean, estimate.
Convert accordingly:
seconds→ hours or days for display (e.g.,42483seconds → "11.8 hours")percentage_as_ratio→ multiply by 100 for display (e.g.,0.85→ "85%")score→ display as-is (dimensionless score)boolean→ display as Yes/Noestimate→ display as-is (story points or similar)
Error Handling
API Errors:
- Metric unavailable on asset → "Metric X isn't available on Y. Available: [list]. Try [alternative]?"
- Empty results → "No data for [filters]. Try expanding time range or removing filters."
- Rate limits → Return partial results with note about limit reached
Ambiguous Queries (see "Clarifying Questions" section above):
- Missing time range → Always ask. Suggest 30 days as default option.
- Unclear entity → "Which team/service? Available: [list top options]"
- Unknown metric → "Metric not found. Did you mean: [similar metrics]?"
Handle pagination if hasNextPage is true in the response.
Example Workflows by Domain
Productivity
- "How long does it take to merge PRs?" → Query
TeamorPersonwith cycle time / time-to-merge metrics, weekly granularity - "Are we completing what we planned this sprint?" → Query
Sprintwith planning accuracy, velocity, story point metrics - "How is the new hire ramping up?" → Query
Personfiltered by name/email with PR throughput and review metrics over monthly granularity
DORA
- "How often are we deploying?" → Query
TeamorServicewith deployment frequency metric, weekly granularity - "What's our change failure rate?" → Query
ServiceorTeamwith change failure rate metric (incidents / deployments) - "What's our mean time to recovery?" → Query
Teamwith MTTR metric (check for avg, p50, p75, p90 variants)
Investment
- "Where is engineering effort going?" → Query
Teamwith FTE Days metric, filter by work type (features, maintenance, developer experience) - "How much effort is going into this epic?" → Query
Epicfiltered by name with FTE Days metric - "What are our active workstreams?" → Query
Teamwith workstream-related metrics for effort breakdown
AI Transformation
- "Who is using AI tools?" → Query
TeamorPersonwith AI adoption rate, active AI users metrics - "What percentage of code is AI-generated?" → Query
TeamorPersonwith AI code ratio metric - "How much are we spending on AI?" → Query
Teamwith AI spend metrics (base + overage)
Calendar
- "How much time are engineers in meetings?" → Query
TeamorPersonwith meeting hours, focus time metrics - "What's our maker time like?" → Query
Teamwith focus time and fragmented time metrics
AI Traces
- "Which skills do we actually use?" →
TraceEventcorpus query (toolChannel = 'skill', notraceId), then group bytoolName— and state whose sessions you could actually read - "Which agent tool calls are failing?" →
TraceEventcorpus query withsuccess = false - "Walk me through this session" →
Trace.id→TraceTurn(traceId)→TraceEvent(traceId, turnIndex = N) - "How much are we running agents?" → trace metrics on
Team/Person, orTraceinmode: "groups"byaiTraceTool
Administration
- "What version is installed?" / "Check for updates" → Run
$SKILL_SCRIPTS/check-version.sh - "Reload Span metadata" → Run
$SKILL_SCRIPTS/fetch-metadata.sh - "Reconfigure Span skill" → Delete
$SPAN_DIR/auth.jsonand re-run onboarding
For more detailed step-by-step walkthroughs, see references/workflows.md.
For domain-specific query guidance (investment views, DORA facades, PR time dimensions, AI tool patterns, agent-session traces, calendar caveats), see references/domains.md.
Reference Material
- For API endpoint details, query parameters, time dimensions, and filters, see references/api-reference.md
- For detailed step-by-step example workflows, see references/workflows.md
- For domain-specific knowledge and caveats, see references/domains.md