rayfin / data-app
AGENTS.md646 starsChanged 24 days agoMITView on GitHub
You will help the user build out this React-based web app that visualizes data from Power BI semantic models. The app fetches live data via DAX queries, renders charts and grids using Vega-Lite and a built-in DataGrid component, and supports light/dark theming. Your job is to discover the user's data, write correct DAX queries, build React components that fetch and display that data, and validate the result in the browser. Break tasks into independent pieces and delegate them to sub-agents…
We split this AGENTS.md into the rules it gives an agent
14 rules. About 0 are common, 5 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 93
When a parameter changes the structure of the query (e.g.
- Very specific: line 130
Example workflow: Run npx fabric-app-data query myModel --query '<DAX>', observe the output columns are [SalesPersonID] and [Name], then …
- Very specific: line 142
Name files after the visualization they drive: Use kebab-case base names (e.g., revenue-by-region.dax, revenue-by-region.json, …
- Very specific: line 146
Group by page/domain: Use subfolders when the dashboard has multiple pages or logical sections.
- Distinctive: line 170
Ask the user whether they would like to:
- Very specific: line 185
Before any data work, verify you can execute DAX queries using the Fabric CLI execute command.
- Distinctive: line 195
Follow the dax-authoring skill to write and test DAX queries.
- Very specific: line 207
Create the barrel .ts file with a factory function that returns { connection, query, columnMetadata, vegaLiteSpec }.
- Very specific: line 215
Use the useSemanticModelQuery hook from src/hooks/use-semantic-model-query.ts — components call the factory functions from src/queries/ and …
- Distinctive: line 278
NEVER use mock, fake, or hardcoded data: All data must come from a real source.
- Distinctive: line 280
Do not assume a data source: Always confirm with the user first.
- Very specific: line 281
Never guess query result schema (e.g. column names): Always run the query with npx fabric-app-data query first and use the exact column …
- Very specific: line 283
Do not ask the user to describe the data schema: Use DAX INFO functions via fabric-app-data query to discover metadata progressively.
- Distinctive: line 284
ALWAYS run browser validation after UI changes: Read the app-validation skill.
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Agent Instructions
Purpose
You will help the user build out this React-based web app that visualizes data from Power BI semantic models. The app fetches live data via DAX queries, renders charts and grids using Vega-Lite and a built-in DataGrid component, and supports light/dark theming. Your job is to discover the user's data, write correct DAX queries, build React components that fetch and display that data, and validate the result in the browser.
Sub-Agent Delegation
Break tasks into independent pieces and delegate them to sub-agents running in parallel. Don't do work sequentially when it can be done concurrently.
For example, when building a new dashboard page: a sub-agent finds the semantic model and discovers its schema. Separate sub-agents write the DAX query files, then have separate sub-agents build each component in parallel once the queries are ready.
Project Structure
fabric.yaml # Fabric connection config (managed by `npx fabric-app-data`)
index.html # Vite entry HTML
vite.config.ts # Vite + Tailwind build config
tsconfig.json # TypeScript configuration
src/
├── fabric.generated.ts # Auto-generated from fabric.yaml — connection aliases → workspace/item IDs
├── main.tsx # App entry point
├── App.tsx # Main dashboard layout
├── ErrorFallback.tsx # Error boundary fallback UI
├── global.css # Tailwind v4 @theme design tokens
├── components/ # Dashboard UI components (cards, charts, banners)
├── hooks/ # React hooks (data fetching, theming)
├── lib/ # Utilities, Fabric client
├── queries/ # DAX queries (.dax) + Vega-Lite specs (.json) + factory functions (.ts), grouped by page/domain
└── vite-env.d.ts # Vite type declarationsQuery & Spec Organization
DAX queries and Vega-Lite specs live in src/queries/, grouped by dashboard page or domain. Each visualization gets files sharing the same kebab-case base name: one or more .dax files for queries, a .json file for the Vega-Lite spec, and a .ts factory file that imports them and exports { connection, query, columnMetadata, vegaLiteSpec }. The factory function accepts optional parameters to select between query variants or modify the spec:
src/queries/
├── index.ts # Re-exports all query modules
├── {page-or-domain}/ # Group by dashboard page or domain
│ ├── {visualization-name}.dax # DAX query (plain text)
│ ├── {visualization-name}-{variant}.dax # Additional query variants (optional)
│ ├── {visualization-name}.json # Vega-Lite spec (JSON)
│ ├── {visualization-name}.ts # Factory function: imports .dax + .json, exports { connection, query, vegaLiteSpec, columnMetadata }
│ └── index.ts # Re-exports all visualizations in this groupExample TS File
revenue-by-region.ts — the factory function accepts use-case-specific parameters and uses them to modify the DAX query and/or Vega-Lite spec as appropriate:
import type { VisualizationSpec } from "@microsoft/fabric-visuals";
import type { ColumnMetadataMap } from "@/lib/to-data-table";
import baseQuery from "./revenue-by-region.dax?raw";
import spec from "./revenue-by-region.json";
const connection = "{connection-alias}"; // from fabric.yaml
/** Column metadata keyed by original DAX column name. */
const columnMetadata: ColumnMetadataMap = {
"Products[Region]": { name: "ProductsRegion", displayName: "Region" },
"[Total Revenue]": { name: "Total Revenue", displayName: "Total Revenue", format: "$#,0.00" },
};
interface RevenueByRegionParams {
/** Filter to specific product categories (modifies the DAX query). */
categories?: string[];
/** Only show regions with revenue above this threshold (modifies the Vega-Lite spec). */
minRevenue?: number;
}
export function revenueByRegion(params?: RevenueByRegionParams) {
let query = baseQuery;
let vegaLiteSpec = spec; // Should clone if modifying
if (params?.categories?.length) {
// make changes to the DAX query to filter by the specified categories and update the query variable
}
if (params?.minRevenue != null) {
// Clone spec and append a client-side filter transform to the Vega-Lite spec
}
return { connection, query, columnMetadata, vegaLiteSpec };
}The parameters, their types, and how they modify the query or spec are entirely use-case-specific. Some visualizations may need no parameters at all; others may accept date ranges, top-N limits, grouping dimensions, or string search terms. The factory function is the single place that translates caller intent into DAX and/or Vega-Lite modifications.
Query variants with multiple .dax files
When a parameter changes the structure of the query (e.g. different GROUP BY columns, different aggregations), use separate .dax files for each variant. The factory function selects the right one:
revenue-trend/
revenue-trend-yearly.dax
revenue-trend-quarterly.dax
revenue-trend-monthly.dax
revenue-trend.json
revenue-trend.tsimport yearlyQuery from "./revenue-trend-yearly.dax?raw";
import quarterlyQuery from "./revenue-trend-quarterly.dax?raw";
import monthlyQuery from "./revenue-trend-monthly.dax?raw";
type Granularity = "yearly" | "quarterly" | "monthly";
const queryByGranularity: Record<Granularity, string> = {
yearly: yearlyQuery,
quarterly: quarterlyQuery,
monthly: monthlyQuery,
};
export function revenueTrend(params?: { granularity?: Granularity }) {
const query = queryByGranularity[params?.granularity ?? "monthly"];
return { connection, query, columnMetadata, vegaLiteSpec };
}Column Metadata
Capture column metadata in the barrel .ts file as a columnMetadata: ColumnMetadataMap constant (import ColumnMetadataMap from @/lib/to-data-table). The dictionary must be keyed by the exact column name from the CLI query output (the name field in table.columns). Do not guess or clean these keys — copy them verbatim from the query result. Each value is a ColumnDef (from @microsoft/fabric-visuals-core) containing:
name— a cleaned-up identifier derived from the original column name by removing.,[,],\,", and'characters. E.g.,"Products[Region]"→"ProductsRegion","[Total Revenue]"→"Total Revenue"— to be used when building visual specs.displayName— a human-readable label sourced from the semantic model schema (e.g.,"Region","Total Revenue"). Used for axis titles, grid headers, and tooltips.format— a VBA/ECMA-376 format string for number/date formatting (e.g.,#,##0.00,0.00%, andmm/dd/yyyy). Omit for text type columns.
Example workflow: Run npx fabric-app-data query myModel --query '<DAX>', observe the output columns are [SalesPersonID] and [Name], then use those exact strings as metadata keys:
export const columnMetadata: ColumnMetadataMap = {
"[SalesPersonID]": { name: "SalesPersonID", displayName: "Sales Person ID" },
"[Name]": { name: "Name", displayName: "Name" },
};Key rules
- All DAX lives in
.daxfiles. Never inline full DAX query strings in.tsfactory files. If a parameter changes the query structure, create a separate.daxfile for each variant and select the right one in the factory function. Small modifications — such as replacing filter value placeholders, wrapping the query withCALCULATETABLEto apply filters, or substituting a column reference — are acceptable in.ts, but the base query must always come from a.daximport. - Name files after the visualization they drive. Use kebab-case base names (e.g.,
revenue-by-region.dax,revenue-by-region.json,revenue-by-region.ts). Variant.daxfiles append a suffix (e.g.,revenue-trend-yearly.dax,revenue-trend-quarterly.dax). - Use
.daxfor queries. Plain-text DAX files keep queries readable and diff-friendly. Import them with Vite's?rawsuffix. - Use
.jsonfor specs. JSON files get free schema validation in editors and are importable as modules by default in Vite. - Barrel
.tsexports a factory function. The function name is the camelCase version of the kebab-case file name (e.g.,revenueByRegionforrevenue-by-region.ts). It accepts optional parameters (typed per use case) and returns{ connection, query, columnMetadata, vegaLiteSpec }. - Group by page/domain. Use subfolders when the dashboard has multiple pages or logical sections. For simple single-page dashboards, a flat structure under
src/queries/is fine. - Re-export via
index.ts. Each subfolder and the rootsrc/queries/index.tsshould re-export all modules for clean imports.
Testing & Spec Files
See the app-validation skill for when and how to write spec files.
Key Conventions
See the app-design skill for styling conventions, UI token rules, theming, and component standards.
Recommended Workflow
Recommend following these steps when building or modifying the dashboard unless the user instructs otherwise.
The workflow has three distinct phases:
- Authoring phase (Steps 1–3): You explore data and validate queries using the Fabric CLI
executecommand (backed by the same SDK used at runtime). No app code is written yet. - Design phase (Step 4): You design the web app UX before writing any runtime code. This requires the app-design skill. You create theming tokens in
src/global.cssaccording to the theming direction and plan how to cohesively apply them across components. - App code phase (Steps 5–7): You write React components that fetch data at runtime using the Fabric SDK.
1. Ask the user for a semantic model
Local .pbix files are not supported. This app connects to semantic models published to the Power BI Service (cloud), not to local .pbix files on disk. If the user provides a local file path (e.g., C:\...\Model.pbix), do not attempt to open, upload, or search for it. Instead:
- Inform the user that local
.pbixfiles are not supported — only models published to the Power BI Service can be used. - Ask the user whether they would like to:
- Search the Power BI Service for a semantic model by name (you will search on their behalf), or
- Provide a specific online model directly (workspace ID + dataset ID, or a Power BI / Fabric URL).
Once the user confirms a published semantic model, read the schema-discovery skill to progressively discover schema metadata as needed — do not fetch the full schema upfront.
Once the model is identified, register it as a connection using the Fabric CLI (see the fabric-cli skill for full command reference):
npx fabric-app-data add <alias> --from-url "<Power BI or Fabric URL>"
npx fabric-app-data generate -o src/fabric.generated.ts2. Ensure query execution is available
Before any data work, verify you can execute DAX queries using the Fabric CLI execute command. This uses the same SDK pipeline as the running app — ensuring identical results between authoring and runtime.
Prerequisites:
- Azure CLI installed and signed in (
az login) - A semantic model registered via
fabric-app-data add(see Step 1)
Test with: npx fabric-app-data query <alias> --query "EVALUATE ROW(\"test\", 1)"
3. Write and test DAX queries (authoring phase)
Follow the dax-authoring skill to write and test DAX queries. The skill covers DAX syntax rules, query patterns, time intelligence, and an iterative test workflow. Use the schema-discovery skill to progressively discover model metadata as needed.
Use npx fabric-app-data query <alias> --query '<DAX>' to test queries. This runs the query through the same SDK pipeline used at runtime, so the results (column names, data types, row structure) are identical to what the app will produce. If a --query command fails due to shell escaping issues with special characters, write the query to a .dax file and retry with --file instead.
Iterate on queries until they return the expected columns and data shape.
Tip: Delegate query exploration to a sub-agent. It can discover the schema, draft queries, and test them in parallel while you plan the component structure.
Once queries are validated, promote them to the app following the Query & Spec Organization conventions above:
- Save each query as a
.daxfile insrc/queries/following the naming and grouping conventions. - Capture column metadata — copy the exact column names from the query output as dictionary keys in a
columnMetadata: ColumnMetadataMapconstant. See the conventions above for the full format and rules. - Create the corresponding
.jsonVega-Lite spec — refer to visuals. Use the cleanednamevalues from the metadata for Vega-Lite field encodings (they are already free of characters that require escaping). UsedisplayNamevalues for axis titles, legend labels, and tooltip headers. Useformatvalues for axis/tooltip formatting. - Create the barrel
.tsfile with a factory function that returns{ connection, query, columnMetadata, vegaLiteSpec }.
4. UX Design for the app
Principles for overall aesthetics, theming, layout, and accessibility requirements are outlined in app-design skill - refer to it before creating or modifying any UI component, layout, page, or style.
5. Build components with data (app code phase)
Use the useSemanticModelQuery hook from src/hooks/use-semantic-model-query.ts — components call the factory functions from src/queries/ and destructure { connection, query, columnMetadata, vegaLiteSpec } from the result.
Use toDataTable() from src/lib/to-data-table.ts to convert the SDK's QueryTable (from data.table) into a DataTable by merging it with the columnMetadata from the factory function result. This applies everywhere the data is consumed like rendering in VegaVisual or DataGrid (pass the DataTable via their data prop), displaying values in custom components, or any other usage.
VegaVisual and DataGrid components should call factory functions for query + spec + columnMetadata — never define specs inline in component files. Refer to the visuals skill when building them.
Example
import { revenueByRegion } from "@/queries/sales/revenue-by-region";
import { useSemanticModelQuery } from "@/hooks/use-semantic-model-query";
import { toDataTable } from "@/lib/to-data-table";
import { VegaVisual, useCssTheme } from "@microsoft/fabric-visuals";
import { DataGrid } from "@microsoft/fabric-datagrid";
function RevenueByRegionChart() {
const theme = useCssTheme();
const { connection, query, columnMetadata, vegaLiteSpec } = revenueByRegion({
categories: ["Category A"],
});
const { data, isLoading, error } = useSemanticModelQuery({
connection,
query,
});
if (isLoading) return <LoadingSpinner />;
// The SDK never throws — errors are returned in the result status.
// Check data.status to handle query failures (auth errors, invalid DAX, etc.).
if (data?.status === "error") {
return <ErrorMessage message={data.error.message} />;
}
if (data?.status !== "success") return null;
// Convert the SDK's QueryTable into a DataTable enriched with column metadata.
// toDataTable merges data.table with columnMetadata so that display names,
// format strings, and cleaned field names are available to visuals.
const dataTable = toDataTable(data.table, columnMetadata);
// Pass the DataTable to VegaVisual (chart) or DataGrid (table) via the data prop.
return (
<div>
<VegaVisual spec={vegaLiteSpec} data={dataTable} theme={theme} />
<DataGrid data={dataTable} theme={theme} />
</div>
);
}Caching: Query results are cached automatically by the SDK's built-in LRU cache. Subsequent calls with the same connection + query return cached data instantly. To force a fresh fetch, pass bypassCache: true to useSemanticModelQuery. To programmatically clear the cache (e.g., after a known data refresh), call clearQueryCache("salesModel") for a specific model or clearQueryCache() for all models.
Error handling: The SDK never throws on query failures — errors are returned as data.status === "error" with details in data.error.message (e.g., "401 Unauthorized", invalid DAX syntax). Check data.status before accessing data.table. Network-level errors that prevent the SDK call itself are surfaced via the error field returned by the hook.
For deeper details on the SDK client, caching internals, and advanced query options, refer to the fabric-sdk skill.
6. Final validation
Follow the app-validation skill (what to check, performance rules, Fabric portal embed flow) together with the playwright-cli skill (the tool itself) to validate the app in the browser. Fix any issues before considering the task complete. The app can only be ran and validated with the Fabric portal embed flow, app-validation skill covers how to do this with the right browser flags and auth setup.
Critical Rules
- NEVER use mock, fake, or hardcoded data. All data must come from a real source. If unsure where data should come from, stop and ask the user before writing any code.
- Never store data in memory or local storage. Fetch on demand from the real source.
- Do not assume a data source. Always confirm with the user first.
- Never guess query result schema (e.g. column names). Always run the query with
npx fabric-app-data queryfirst and use the exact column names from the output as metadata keys. The CLI output is identical to what the app receives at runtime. - Do not use any data source without explicit user consent. If any required data has not already been provided or consented to by the user, stop and ask the user before using any additional data sources. This includes (but is not limited to) additional Power BI semantic models and non-Power BI external sources (web search, web APIs, public datasets, scraped web content, or any non-Power BI data). Never silently supplement user-provided data with additional sources.
- Do not ask the user to describe the data schema. Use DAX INFO functions via
fabric-app-data queryto discover metadata progressively. Refer to the schema-discovery skill for discovery query patterns. - ALWAYS run browser validation after UI changes. Read the app-validation skill. Do NOT skip any validation steps in favor of brevity.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository76 other files it gives its agents.
- common/config/typedoc/AGENTS.mdAGENTS.md
- docs/site/AGENTS.mdAGENTS.md
- .github/skills/react-best-practices/AGENTS.mdAGENTS.md
- packages/docgen/AGENTS.mdAGENTS.md
- packages/guide/AGENTS.mdAGENTS.md
- packages/host-docs/AGENTS.mdAGENTS.md
- packages/tools/AGENTS.mdAGENTS.md
- packages/tools/cli/AGENTS.mdAGENTS.md
- packages/tools/cli/assets/agent-files/AGENTS.mdAGENTS.md
- packages/tools/common/AGENTS.mdAGENTS.md
- packages/tools/docs-lib/AGENTS.mdAGENTS.md
- packages/tools/mcp/AGENTS.mdAGENTS.md
- packages/tools/vscode/AGENTS.mdAGENTS.md
- packages/typescript-sdk/AGENTS.mdAGENTS.md
- packages/typescript-sdk/auth/AGENTS.mdAGENTS.md
- packages/typescript-sdk/core/AGENTS.mdAGENTS.md
- packages/typescript-sdk/data/AGENTS.mdAGENTS.md
- packages/typescript-sdk/storage/AGENTS.mdAGENTS.md
- packages/udf/udf-worker-extension/AGENTS.mdAGENTS.md
- samples/getting-started-auth/AGENTS.mdAGENTS.md
- samples/notes-app/AGENTS.mdAGENTS.md
- samples/todo-app/AGENTS.mdAGENTS.md
- samples/universal-app/template/AGENTS.mdAGENTS.md
- samples/welcome-app-react-auth/AGENTS.mdAGENTS.md
- samples/welcome-app-react-no-auth/AGENTS.mdAGENTS.md
- samples/welcome-app-typescript/AGENTS.mdAGENTS.md
- hotfix-releaseSkill
- launch-vscode-extensionSkill
- openspec-apply-changeSkill
- openspec-archive-changeSkill
- openspec-bulk-archive-changeSkill
- openspec-continue-changeSkill
- openspec-exploreSkill
- openspec-ff-changeSkill
- openspec-new-changeSkill
- openspec-onboardSkill
- openspec-sync-specsSkill
- openspec-verify-changeSkill
- privacy-dfdSkill
- rayfin-cli-uxSkill
- vercel-react-best-practicesSkill
- react-clean-architectureSkill
- run-cli-e2e-locallySkill
- vscode-react-webview-architectureSkill
- vscode-webview-trpc-messagingSkill
- rayfin-connectorsSkill
- rayfin-functionsSkill
- rayfinSkill
- rayfin-storageSkill
- rayfin-getting-startedSkill
- app-designSkill
- app-validationSkill
- dax-authoringSkill
- fabric-cliSkill
- fabric-sdkSkill
- playwright-cliSkill
- query-designSkill
- schema-discoverySkill
- visualsSkill
- analyticsSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

