feat(cli): figma import telemetry — subcommand labels, typed error codes, figma_import event (#1979)

Closes the observability gaps on the figma integration:
- withFigmaErrors takes a command label (figma:asset|tokens|component) and
  reports the failure inline before its process.exit — the top-level
  trackCommandFailures wrapper never sees self-exiting commands, so typed
  codes (NO_TOKEN, BAD_TOKEN, FORBIDDEN, RATE_LIMITED) were invisible.
  FigmaClientError codes surface as the error name for dashboarding the
  first-run funnel (NO_TOKEN -> later success = onboarding conversion).
- new figma_import event per import: phase, duration, reused (dedup
  effectiveness), tokens variables-vs-styles mode + entry count
  (Enterprise gating rate), unresolved-binding + rasterized-node counts
  (fidelity degradation). No fileKeys, node ids, names, or descriptions.
- /figma skill fires the events beacon (figma-motion / figma-shaders /
  figma-storyboard) for the MCP phases that never touch the CLI.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-07-05 23:27:58 -07:00
committed by GitHub
co-authored by Claude Fable 5
parent 232d591479
commit e9076324e7
10 changed files with 153 additions and 5 deletions
+39
View File
@@ -11,6 +11,7 @@ const {
trackRenderObservation,
trackCommandFailure,
trackCliError,
trackFigmaImport,
trackRenderFeedback,
trackRenderPreflightRejected,
} = await import("./events.js");
@@ -187,3 +188,41 @@ describe("trackCommandFailure", () => {
);
});
});
describe("trackFigmaImport", () => {
beforeEach(() => {
trackEvent.mockClear();
});
it("emits figma_import with phase + quality counters, no identifiers", () => {
trackFigmaImport({
phase: "component",
durationMs: 1234,
unresolvedBindings: 2,
rasterizedNodes: 3,
});
expect(trackEvent).toHaveBeenCalledWith("figma_import", {
phase: "component",
duration_ms: 1234,
unresolved_bindings: 2,
rasterized_nodes: 3,
});
});
it("carries reused for the asset phase and omits absent props entirely", () => {
trackFigmaImport({ phase: "asset", durationMs: 42, reused: true });
expect(trackEvent).toHaveBeenCalledWith("figma_import", {
phase: "asset",
duration_ms: 42,
reused: true,
});
});
it("carries tokens mode + entry count for the tokens phase", () => {
trackFigmaImport({ phase: "tokens", durationMs: 10, tokensMode: "styles", entryCount: 0 });
expect(trackEvent).toHaveBeenCalledWith(
"figma_import",
expect.objectContaining({ phase: "tokens", tokens_mode: "styles", entry_count: 0 }),
);
});
});