{# Default analyst-onboarding workspace prompt for "agnes init".
   Rendered server-side by src/claude_md.py. Edit this file to change
   the OSS default; admins override per-instance via /admin/workspace-prompt.

   Available context (see docs/agent-workspace-prompt.md for the full reference):
     instance.name, instance.subtitle
     server.url, server.hostname
     sync_interval                — string from instance.yaml
     data_source.type             — keboola | bigquery | local
     tables                       — list of {name, description, query_mode}
     metrics.count, metrics.categories
     marketplaces                 — list of {slug, name, plugins:[{name}]}
     user.id, user.email, user.name, user.is_admin, user.groups
     now, today                   — datetime / date string
#}
# {{ instance.name }} — AI Data Analyst

This workspace is connected to {{ server.url }}.
{% if instance.subtitle %}Operated by **{{ instance.subtitle }}**.{% endif %}

> Looking for human-readable workspace docs? Open `AGNES_WORKSPACE.md` in this directory — that file documents what `agnes init` installed, where files live, and how to uninstall.

## Rules
- Before computing any business metric: run `agnes catalog --metrics --show <category>/<name>`
- **For canonical table list with query modes: `agnes catalog`.** Treat `agnes catalog` as source of truth (covers all `query_mode` values: `local`, `remote`, `materialized`).
- Do not use DESCRIBE/SHOW COLUMNS — use `agnes schema <table>` instead
- Sync data regularly with `agnes pull`
- **Personal customizations go in `.claude/CLAUDE.local.md`, NOT here.** This file is regenerated by `agnes init --force`; edits here will be lost. CLAUDE.local.md is preserved across regeneration and uploaded on `agnes push`.

## Metrics Workflow
1. `agnes catalog --metrics` — find the relevant metric ({{ metrics.count }} available, categories: {{ metrics.categories | join(", ") or "none yet" }})
2. `agnes catalog --metrics --show <category>/<name>` — read SQL and business rules
3. Use the canonical SQL from the metric definition, adapt to the question
4. Never invent metric calculations — always check existing definitions first

## Data Sync
- `agnes pull` — download current data from server
- `agnes push` — upload sessions and local notes to server
- Data on the server refreshes every {{ sync_interval }}

## Available Datasets
{% for t in tables -%}
- `{{ t.name }}`{% if t.description %} — {{ t.description }}{% endif %}{% if t.query_mode == "remote" %} *(remote, queried on demand)*{% endif %}
{% else -%}
- _No tables registered yet — ask an admin to register tables in the dashboard._
{% endfor %}

{% if marketplaces -%}
## Plugins available to you
{% for mp in marketplaces -%}
- **{{ mp.name }}** ({{ mp.slug }}): {{ mp.plugins | map(attribute="name") | join(", ") }}
{% endfor %}
{% endif -%}

## Remote Queries (BigQuery) — when data isn't on the laptop

Not every table is synced. Tables registered with `query_mode: "remote"` live in
BigQuery, accessed server-side via DuckDB's BQ extension — no parquet on disk.
Tables you don't see in `server/parquet/` may still be queryable.

### Discovery first

```
agnes catalog --json | jq '.[] | {name, source_type, query_mode}'   # see all tables + their modes
agnes schema <table>                                                # columns + types
agnes describe <table> -n 5                                         # sample rows
```

For local-mode tables, query directly with `agnes query "SELECT … FROM <table>"`.

### Three patterns for `query_mode: "remote"` tables

| Pattern | Tool | Use when |
|---------|------|----------|
| **`agnes snapshot create`** (preferred) | materializes a filtered subset locally → query the snapshot | repeated questions on same slice |
| **`agnes query --remote`** | one-shot, server-side execution against BigQuery (works for BASE TABLE rows directly + VIEW/MATERIALIZED_VIEW rows via the BQ jobs API; cost-guarded by a 5 GiB scan cap configurable in /admin/server-config) | single aggregate / cheap probe |
| **`agnes query --register-bq`** | hybrid joins between local snapshots and ad-hoc BQ subqueries | crossing local + remote |

### Permission model + cost — important

- BQ access goes through the **agnes server's GCE service account**, not your personal Google credentials. If a query fails with a permission error, the table is in a project the server SA cannot read — escalate to admin, do NOT try to authenticate yourself.
- Every BQ query bills the SA's GCP project for **bytes scanned**. A naive `SELECT * FROM <large_table>` can cost real money. ALWAYS:
  - filter via `--where` on the partition column (typically a date)
  - list specific columns in `--select` — column-store BQ skips the rest, cheaper
  - run `--estimate` first when unsure of the table size or partitioning

### `agnes snapshot create` discipline

```
# 1. ESTIMATE first — refuses to fetch without knowing the cost
agnes snapshot create <table> --select col1,col2 --where "date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)" --estimate

# 2. If reasonable, fetch as a named snapshot
agnes snapshot create <table> --select col1,col2 --where "..." --as my_recent

# 3. Query the local snapshot
agnes query "SELECT col1, COUNT(*) FROM my_recent GROUP BY 1"

# 4. List + drop snapshots when done
agnes snapshot list
agnes snapshot drop my_recent
```

Rules of thumb:
- ALWAYS list specific columns in `--select`. Avoid implicit SELECT *.
- ALWAYS include a `--where` for remote tables; otherwise add `--limit`.
- ALWAYS run `--estimate` first when the table is `partition_by` / `clustered_by`
  per `agnes schema`, or could plausibly exceed 1 GB local bytes.
- Reuse snapshots across questions in the same conversation — `agnes snapshot list`
  before fetching.

### Snapshot freshness — when to refresh

Snapshots are point-in-time copies. They go stale as the source data updates (most BQ tables refresh daily; check `sync_schedule` per `agnes catalog`). For each new conversation:

```
agnes snapshot list                            # see existing snapshots + their ages
agnes snapshot drop my_recent                  # drop stale ones
agnes snapshot create <table> --select ... --where ... --as my_recent   # re-fetch
```

If the question is time-sensitive (e.g. "today's orders"), assume any snapshot older than the table's `sync_schedule` is stale and refresh.

### Hybrid query example — local + remote in one query

`agnes query --register-bq` lets a single SQL statement join a local table with an ad-hoc BQ subquery. The BQ subquery runs first (server-side), result registered as a DuckDB view, then the joined query runs locally.

```
agnes query \
  --register-bq "traffic=SELECT date, country, SUM(views) AS views \
                 FROM \`prj.web_analytics.sessions\` \
                 WHERE date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY) \
                 GROUP BY 1, 2" \
  --sql "SELECT o.date, o.country, o.revenue, t.views, o.revenue / NULLIF(t.views,0) AS rev_per_view \
         FROM orders o \
         JOIN traffic t ON o.date = t.date AND o.country = t.country \
         ORDER BY 1 DESC"
```

The BQ subquery MUST contain `WHERE` and/or `GROUP BY` to keep the registered result manageable (target: under 500K rows, well under 100 MB). Multiple `--register-bq` flags can compose multiple BQ sources. For complex SQL, use `--stdin` mode (`echo '{"register_bq":{...},"sql":"..."}' | agnes query --stdin`).

### BigQuery SQL flavor for `--where`

Source-typed `bigquery` tables use BigQuery dialect, not DuckDB:

- Date literal: `DATE '2026-01-01'`
- Timestamp literal: `TIMESTAMP '2026-01-01 00:00:00 UTC'`
- Now: `CURRENT_DATE()`, `CURRENT_TIMESTAMP()`
- Date arithmetic: `DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)`
- Regex: `REGEXP_CONTAINS(col, r'pattern')` (raw string!)
- Cast: `CAST(x AS INT64)` (NOT `INT`)

### When the table you want isn't in `agnes catalog`

The table may exist in BigQuery but not be registered with Agnes yet. Two options:

1. **Ad-hoc one-shot** — register a BQ subquery as a view inline, no admin needed
   if the agnes server SA has BQ access:
   ```
   agnes query --register-bq "live=SELECT * FROM \`project.dataset.table\` WHERE date >= '...' LIMIT 1000" \
            --sql "SELECT * FROM live"
   ```
2. **Ask admin to register** the table with `query_mode: "remote"` so it shows up
   in `agnes catalog` and supports `agnes snapshot create` / `agnes query --remote`. This is the
   right path for any table you'll query repeatedly.

### Deeper guidance

For the full protocol, including hybrid-query examples, snapshot hygiene, and
when NOT to use `agnes snapshot create`, run:

```
agnes skills show agnes-data-querying
```

## Corporate Memory

Rules injected by `agnes pull` from the server's corporate knowledge base live in `.claude/rules/km_*.md`. They are automatically loaded by Claude Code on every session start.

- `km_<id>.md` — mandatory rules (always enforced)
- `km_approved.md` — approved guidance (confidence × recency ranked)

Run `agnes pull` to refresh. Rules are pruned automatically when items are revoked.

## Directory Structure
- `server/parquet/*.parquet` — synced table data (RBAC-filtered subset for you)
- `user/duckdb/analytics.duckdb` — local analytics DuckDB views — what `agnes query` reads
- `user/snapshots/*.parquet` — ad-hoc materialized snapshots from `agnes snapshot create`
- `user/sessions/*.jsonl` — Claude Code session logs (uploaded on `agnes push`)
- `.claude/CLAUDE.local.md` — your personal notes + workspace customizations. **Never overwritten by `agnes init --force`.** Uploaded to the server on `agnes push`. Put any local-only Claude instructions, project-specific reminders, or temporary notes here — NOT in CLAUDE.md (this file is regenerated from a template).

_Hello {{ user.name or user.email }} — generated {{ today }}._
