Bootstrap
cawplan skill check
Entry Routing
| Input | Flow |
|---|---|
| A business metric trend to query or chart (subscriptions, CawPlan tickets, QA, workflow, tokens, API usage, ...) | A — Metrics query |
| Recording/logging a business metric event | B — Metrics ingest |
There is no device/app metrics flow here — the public endpoint that used to serve installations/ crash rate/etc. via API key/CLI has been retired. If a user asks for that kind of data, tell them it's no longer available through this skill/CLI rather than guessing at a substitute.
Workflow A — Metrics query
Every domain is prefixed cawplan_. Business domains and their metric names are open-ended (new
ones get added over time), but the common ones today — and, critically, which metrics inside
each domain actually have a producer writing to them right now vs. which are defined but still
empty (a query against an empty one returns zero rows, which looks identical to "no data in this
range," not "this isn't wired yet" — don't guess past this list without checking):
cawplan_ticket:ticket_created,ticket_updated,ticket_status_changed— all wired.cawplan_qa_insight:test_point_created,test_point_updated,report_created,execution— all wired.cawplan_subscription:new,renew(from Stripeinvoice.paid, disambiguated bybilling_reason) andcancel(user-initiated cancel-to-Free) are wired;winback,upgrade,downgradeare defined but nothing calls them yet.cawplan_api:throttled(one point per 429 response) is wired;requestis defined but nothing calls it yet.cawplan_ai_session_usage:usage_reported— wired, one point per daily AI coding-tool usage report upload (tokens/sessions/cost, workspace-scoped). This is internal engineering cost telemetry for AI coding tools (the cawplan-coding-insights pipeline), not a customer-facing token/credit ledger — don't conflate it withcawplan_tokenbelow.cawplan_workflow(execution),cawplan_token(consumed,recharged,refunded,cost),cawplan_user(signup,active),cawplan_csm,cawplan_purchase: all defined, none wired to any producer yet — querying any of these will always return zero rows today.
If the user names a metric that doesn't obviously map to one of these, ask rather than guessing a
domain/metric pair.
-
Determine the time range. Require both
--startand--end(RFC3339) — if the user gave a relative range ("last 30 days", "this week"), compute the bounds client-side; there is notime_rangeshorthand on this endpoint. -
If the user wants to filter or break the result down by product or workspace, resolve the product name to an ID first (
cawplan products list --search "<product name>", same match rules as any other skill: exact or unique short-form match only, ask if ambiguous) and pass it as--tag product:<product_id>/--tag workspace_id:<workspace_id>, or includeproduct/workspace_idin--group_by—productholds the product'sunique_id(same valuecawplan products listreturns, not a display name);workspace_idis the tenant-attribution dimension and is worth adding whenever a query should be scoped to one workspace rather than the whole database. Forcawplan_ticketevents specifically,--tag ticket_id:<ticket_id>(a dynamic tag, not a standard dimension) disambiguates a burst of points for one product — an ancestor-status-propagation cascade touching several different tickets looks identical to the same ticket being written repeatedly unless you filter or group byticket_id. -
Run the query:
cawplan metrics query \ --domain <domain> [--metric <metric>] \ --start <iso> --end <iso> \ --granularity day \ [--tag <key:value> ...] \ [--group_by <k1,k2>] \ [--fields value,count]Default
--granularity rawreturns every stored point unaggregated; pickday/hour/minuteto get one summed row per bucket per tag combination instead — use a bucketed granularity whenever the user wants a trend rather than a raw event list. -
Check
data.truncatedin the response. Iftrue, the result was capped — narrow the time range or filters (or raise--limit, up to the server's own maximum) before reporting a total, rather than presenting a truncated count as complete.
Workflow B — Metrics ingest
Only use this when the user explicitly asks to record/log/write a metric event — not as a way to backfill or correct history in bulk (the ingest endpoint validates and rejects on the first bad item in a batch; it isn't a bulk-load tool).
cawplan metrics ingest --domain <domain> --metric <metric> --value 1 \
[--dimensions '{"product":"<product_id>"}'] \
[--tags '{"from_plan":"basic","to_plan":"pro"}']
Confirm the domain/metric pair and any dimensions/tags with the user before writing — there is
no undo for a written point (see references/CAWPLAN_OPEN_API.md §7).
Output
Workflow A:
queryreturns full structureddata.rows— that's usually all you need. For a chart the user just wants to see in this conversation (not a file to keep), don't reach for a CLI rendering flag at all: readdata.rowsyourself and render/describe the chart directly (or hand it to thedatavizskill for an inline/interactive visualization). Maptimeto the x-axis, group by the tag combination that distinguishes series, and the requested field to the y-axis. Nothing needs to be written to disk for this.- If the user asked for a number/summary instead of a chart: report totals per bucket/tag combination directly.
- If the user asked for a standalone chart file to keep/share (not just to look at now): add
--chart <path>.svg(and--chart-field <value|count|cost|credit>if notvalue) and hand back the file path. - If that file needs to render as an actual image inline in this chat (e.g. via the
Readtool, which decodes raster images but treats SVG as plain text): add--png <path>.pngto the samequerycall, thenReadthat path to show it. It rasterizes from an SVG built the same way--chartdoes, using whatever's available in the environment (qlmanageon macOS,rsvg-convertelsewhere) — no separate rendering step needed. If no rasterizer is available the command exits with an error rather than writing a broken file. Note:Read-ing the PNG doesn't always actually display it to the user even when it decodes fine for you (observed in an iTerm2-direct session — manually runningimgcat <path>.pngin the same window rendered correctly, but the same file viaReaddidn't show up on the user's screen). If the user reports not seeing the image after youReadit, tell them to tryimgcat <path>.png(or whatever their terminal supports) directly instead of relying onRead. --preview(inline image via iTerm2/Kitty/chafa) is for a human runningcawplandirectly in their own terminal — it does not work when you run it: the escape codes it prints only render as an image inside a real terminal emulator reading raw stdout, and this chat's tool output is captured as text, so it would show up as garbled escape-sequence text instead. Don't use it here; readdata.rowsyourself or use--pnginstead.- Always mention the time range and granularity used, and flag if
data.truncatedwas true.
Workflow B: confirm what was written (domain, metric, fields, dimensions/tags) and note that this metrics store is a reporting store, not a system of record — for anything with financial or compliance weight (e.g. token/credit), the authoritative ledger is elsewhere; this write is for dashboards only.
Decision Guide
- For Support Ops Google Sheet exports, CSM ticket-count backfills, corrections, or reconciliation:
use
cawplan-support-metrics. - For a combined product health overview (feedback + critical issues, without device metrics,
which are no longer available through this CLI): use
cawplan-product-insights. - For AI-categorized feedback trends only:
cawplan analytics get <product_id>. - For a date-range activity/progress report: use
cawplan-product-report.
References
references/CAWPLAN_OPEN_API.md