@yottagraph-app/aether-instructions 1.1.48 → 1.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -8,11 +8,11 @@
8
8
 
9
9
  **Data:** This app runs on the Lovelace platform -- entities, news, filings, sentiment, relationships, events. See [`data.md`](.agents/skills/aether/data.md) in the `aether` skill for access patterns and gotchas. Additional skill docs: `.agents/skills/data-model/` (entity types, properties, relationships; `SKILL.md` first), `.agents/skills/elemental-mcp-patterns/` (MCP response shapes, property type handling, Python patterns for agent tools). Do NOT call external APIs for data the platform provides.
10
10
 
11
- **Storage:** KV (Upstash Redis) for preferences and lightweight state via `Pref<T>` from `usePrefsStore()`. Neon Postgres for relational data if connected (see [`storage.md`](.agents/skills/aether/storage.md) in the `aether` skill).
11
+ **Storage:** KV (Upstash Redis) for preferences and lightweight state via `Pref<T>` from `usePrefsStore()`. Neon Postgres for relational data if connected (see [`storage.md`](.agents/skills/aether/storage.md) in the `aether` skill). BigQuery for analytical/warehouse data when configured — go through `server/utils/bigquery.ts` (which proxies the Broadchurch Portal gateway), never `@google-cloud/bigquery` or a pasted service-account key. See [`bigquery.md`](.agents/skills/aether/bigquery.md) in the `aether` skill.
12
12
 
13
13
  **Source of truth:** `DESIGN.md` -- read before starting work, update when changing features. The starter UI is placeholder -- replace freely. Feature docs in `design/` for implementation planning.
14
14
 
15
- **Git:** Commit meaningful units of work. Run `npm run format` before commit. Message format: `[Agent commit] {summary}`. Push directly to main — do NOT create PRs or feature branches.
15
+ **Git:** Commit meaningful units of work. Run `npm run format` before commit. Message format: `[Agent commit] {summary}`. Push directly to `main` with `git push origin main` — do NOT create PRs, do NOT run `gh pr create`, and do NOT use feature branches. Tenant `main` is not protected; Vercel auto-deploys on push to `main`, and that's how the app gets live. (This is the opposite of the `aether-dev` repo, which IS PR-only — do not import that habit.)
16
16
 
17
17
  **First action for a new project:** Run `/build_my_app`.
18
18
 
@@ -112,6 +112,7 @@ Key capabilities:
112
112
  - **Query Server / Elemental API** -- the primary data source. Use `useElementalClient()` from `@yottagraph-app/elemental-api/client`. See [`data.md`](../skills/aether/data.md) in the `aether` skill.
113
113
  - **KV storage** -- always available for preferences and lightweight data (see [`pref.md`](../skills/aether/pref.md) in the `aether` skill)
114
114
  - **Neon Postgres** -- may be available for relational data; see [`storage.md`](../skills/aether/storage.md) in the `aether` skill for how to check and how to use it
115
+ - **BigQuery** -- may be available for analytical / warehouse data via the portal gateway. **If the brief mentions BigQuery, analytics, a warehouse, dashboards over event data, or anything in that family, read [`bigquery.md`](../skills/aether/bigquery.md) in the `aether` skill BEFORE writing any code.** A pre-built `server/utils/bigquery.ts` helper is already scaffolded — do NOT add `@google-cloud/bigquery` to `package.json` and do NOT ask the user to paste a service-account key. The build will fail if you try (a prebuild guard catches it).
115
116
  - **AI agent chat** -- use the `useAgentChat` composable to build a chat UI for deployed agents
116
117
  - **MCP servers** -- Lovelace MCP servers may be available (check `.agents/mcp.json`)
117
118
  - **Components** -- Vuetify 3 component library is available
@@ -228,16 +229,24 @@ git add -A
228
229
  git commit -m "[Agent commit] Initial app build from project brief"
229
230
  ```
230
231
 
231
- **Push directly to main.** Vercel auto-deploys on push to main, so this
232
- gets the app live immediately. Do NOT try to create a pull request via
233
- `gh pr create` in Cursor Cloud environments, the GitHub integration
234
- token lacks PR permissions (known Cursor issue). Pushing to main is the
235
- correct workflow.
232
+ **Push directly to main. Do NOT create a pull request or a feature branch.**
233
+
234
+ Vercel auto-deploys on push to `main`, which is how this project gets
235
+ live. Creating a PR blocks that auto-deploy and breaks the smooth
236
+ first-run experience the user is expecting from the wizard. The `main`
237
+ branch on tenant repos is **not** protected — `git push origin main`
238
+ will succeed. Tenant repos follow a different policy than the upstream
239
+ `aether-dev` template (which IS PR-only) — do not import that habit.
236
240
 
237
241
  ```bash
238
242
  git push origin main
239
243
  ```
240
244
 
245
+ Specifically: do NOT run `gh pr create`, do NOT create a feature
246
+ branch, and do NOT push to anything other than `main`. If a push to
247
+ `main` fails because of a real permission issue (not branch
248
+ protection), report the exact error rather than falling back to a PR.
249
+
241
250
  If running locally (not in Cursor Cloud), the user may prefer a PR-based
242
251
  workflow — ask before pushing directly to main in that case.
243
252
 
@@ -0,0 +1,227 @@
1
+ # Deploy Compute Job
2
+
3
+ Deploy a Cloud Run Job from the `jobs/` directory via the Broadchurch Portal.
4
+
5
+ ## Overview
6
+
7
+ A "compute job" is a containerized Python (or any-language) entrypoint
8
+ that runs on Google Cloud Run Jobs. Use it for:
9
+
10
+ - **Cron jobs** (set `schedule:` in `job.yaml` — Cloud Scheduler is wired up automatically)
11
+ - **Event-triggered batch work** (HTTP-triggered from your Vercel app or an Agent Engine tool)
12
+ - **Heavy compute** (entity enrichment, scoring, ETL, exports, aggregations)
13
+ - **Workflow steps** (called from a Cloud Workflow definition under `workflows/`)
14
+
15
+ This command triggers a Cloud Build → Cloud Run Job deploy through
16
+ the Portal. No local GCP credentials needed.
17
+
18
+ The job must live in `jobs/<name>/` with at minimum:
19
+
20
+ ```
21
+ jobs/<name>/
22
+ ├── main.py # Entrypoint (or any executable; see Dockerfile)
23
+ ├── requirements.txt # Python deps (only required if no custom Dockerfile)
24
+ ├── job.yaml # Manifest: resources, schedule, env
25
+ └── Dockerfile # Optional — auto-generated if missing
26
+ ```
27
+
28
+ **Prerequisite:** The project must have a valid `broadchurch.yaml`
29
+ (created during provisioning).
30
+
31
+ ---
32
+
33
+ ## Step 1: Read Configuration
34
+
35
+ Read `broadchurch.yaml` from the project root.
36
+
37
+ ```bash
38
+ cat broadchurch.yaml
39
+ ```
40
+
41
+ **If the file does not exist:**
42
+
43
+ > This project hasn't been provisioned yet. Create it in the Broadchurch Portal first.
44
+
45
+ Stop here.
46
+
47
+ Extract these values:
48
+
49
+ - `tenant.org_id` (tenant org ID)
50
+ - `gateway.url` (Portal Gateway URL)
51
+
52
+ ---
53
+
54
+ ## Step 2: Discover Jobs
55
+
56
+ List the directories under `jobs/`:
57
+
58
+ ```bash
59
+ ls -d jobs/*/
60
+ ```
61
+
62
+ **If no directories exist:**
63
+
64
+ > No jobs found. Create one by making a directory under `jobs/` with the structure above.
65
+ > See `docs/COMPUTE_JOBS.md` for guidance, or copy `jobs/example_job/` as a starting point.
66
+
67
+ Stop here.
68
+
69
+ **Skip `example_job`** — this is a template placeholder and should
70
+ never be deployed. Filter it out before proceeding.
71
+
72
+ **If multiple jobs remain:** Deploy all of them. If called interactively
73
+ (not from `/build_my_app`), ask the user which one to deploy.
74
+
75
+ **If only one job remains:** Proceed with it — no confirmation needed.
76
+
77
+ **Important:** Job directory names should use underscores; the deploy
78
+ workflow translates them to Cloud Run-friendly hyphens automatically.
79
+
80
+ ---
81
+
82
+ ## Step 3: Validate Job Structure
83
+
84
+ For the selected job directory, verify the required files exist:
85
+
86
+ ```bash
87
+ ls jobs/<name>/main.py jobs/<name>/job.yaml
88
+ ```
89
+
90
+ If `Dockerfile` exists, the deploy uses it as-is. If not, the deploy
91
+ auto-generates a Python 3.12 Dockerfile that runs `python main.py`.
92
+
93
+ If using the auto-Dockerfile, `requirements.txt` must also exist:
94
+
95
+ ```bash
96
+ ls jobs/<name>/requirements.txt 2>/dev/null
97
+ ```
98
+
99
+ Validate the manifest is well-formed:
100
+
101
+ ```bash
102
+ yq -e '.name // ""' jobs/<name>/job.yaml >/dev/null
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Step 4: Ensure Code is Pushed
108
+
109
+ The deployment workflow runs on the code in the GitHub repo, not the
110
+ local working directory:
111
+
112
+ ```bash
113
+ git status
114
+ ```
115
+
116
+ **If there are uncommitted changes in `jobs/<name>/`:**
117
+
118
+ > Your job code has local changes that aren't pushed yet. The
119
+ > deployment will use the version on GitHub. Would you like me to
120
+ > commit and push first?
121
+
122
+ If yes, commit and push. If no, warn them and continue.
123
+
124
+ ---
125
+
126
+ ## Step 5: Trigger Deployment
127
+
128
+ Call the Portal API to trigger the deploy workflow:
129
+
130
+ ```bash
131
+ curl -sf -X POST "<GATEWAY_URL>/api/projects/<ORG_ID>/deploy" \
132
+ -H "Content-Type: application/json" \
133
+ -d '{"type": "job", "name": "<JOB_NAME>"}'
134
+ ```
135
+
136
+ **If this fails with 404:** The job directory may not exist on GitHub
137
+ yet. Push your code first.
138
+
139
+ **If this succeeds:** The Portal has triggered the `deploy-job.yml`
140
+ GitHub Actions workflow.
141
+
142
+ ---
143
+
144
+ ## Step 6: Monitor Progress
145
+
146
+ > Deployment triggered! The compute job is being deployed via GitHub Actions.
147
+ >
148
+ > - **Job:** <name>
149
+ > - **Workflow:** deploy-job.yml
150
+ >
151
+ > This typically takes 2-5 minutes (container build + Cloud Run Job create/update).
152
+ > You can monitor progress:
153
+ >
154
+ > - In the Broadchurch Portal under your project's "Jobs" tab
155
+ > - On GitHub: `https://github.com/<REPO>/actions`
156
+ >
157
+ > Once complete:
158
+ >
159
+ > - The job is callable via the Portal "Run now" button
160
+ > - If `schedule:` is set in `job.yaml`, Cloud Scheduler will trigger it automatically
161
+ > - Run history is visible in the Portal's "Jobs" tab
162
+
163
+ ---
164
+
165
+ ## Step 7: (Optional) Trigger a Test Run
166
+
167
+ After deployment, trigger an ad-hoc run to verify the job works:
168
+
169
+ ```bash
170
+ curl -sf -X POST "<GATEWAY_URL>/api/projects/<ORG_ID>/jobs/<JOB_NAME>/run" \
171
+ -H "Content-Type: application/json" \
172
+ -d '{}'
173
+ ```
174
+
175
+ Then poll for results:
176
+
177
+ ```bash
178
+ curl -sf "<GATEWAY_URL>/api/projects/<ORG_ID>/jobs/<JOB_NAME>/runs" | jq '.runs[0]'
179
+ ```
180
+
181
+ Each run has a `status` field. Terminal statuses are: `Succeeded`,
182
+ `Failed`, `Cancelled`.
183
+
184
+ ---
185
+
186
+ ## Troubleshooting
187
+
188
+ ### Build fails
189
+
190
+ Check the GitHub Actions logs for the `Deploy Compute Job` workflow.
191
+ Common issues:
192
+
193
+ - **"requirements.txt" errors**: list every Python dep your `main.py` imports.
194
+ - **Custom Dockerfile**: ensure the `CMD` actually runs your entrypoint.
195
+ - **Memory/CPU mismatch**: Cloud Run Jobs require 1 vCPU per 4 GiB memory minimum (see Google Cloud docs).
196
+
197
+ ### Job times out
198
+
199
+ Increase `task_timeout` in `job.yaml`. Cloud Run Jobs supports up to 24
200
+ hours per task. For longer-running work, split into shards
201
+ (`task_count: N` + `parallelism: N`) or escalate to GCP Batch.
202
+
203
+ ### Schedule doesn't fire
204
+
205
+ Cloud Scheduler entries are named `job-<name>`. Check in the Cloud
206
+ Console (Cloud Scheduler → us-central1) and verify:
207
+
208
+ - The cron expression is valid
209
+ - The OAuth service account email matches the tenant SA
210
+ - The target URL points at the Cloud Run Job's `:run` endpoint
211
+
212
+ ### Need to update an existing job
213
+
214
+ Just run `/deploy_job` again. It will rebuild the container, update
215
+ the Cloud Run Job in place, and reconcile the Cloud Scheduler entry
216
+ to match the current `job.yaml`.
217
+
218
+ ### Want to delete a job
219
+
220
+ Delete via the Portal "Jobs" tab, or:
221
+
222
+ ```bash
223
+ curl -sf -X DELETE "<GATEWAY_URL>/api/projects/<ORG_ID>/jobs/<JOB_NAME>"
224
+ ```
225
+
226
+ This removes the Cloud Run Job, its Cloud Scheduler entry, and the
227
+ Portal's job registration.
@@ -0,0 +1,189 @@
1
+ # Deploy Cloud Workflow
2
+
3
+ Deploy a Cloud Workflow definition from the `workflows/` directory via
4
+ the Broadchurch Portal.
5
+
6
+ ## Overview
7
+
8
+ A Cloud Workflow orchestrates one or more compute jobs. It's the
9
+ declarative DAG layer on top of the job runtime — useful for:
10
+
11
+ - **Multi-step pipelines** ("for each entity: enrich, score, write")
12
+ - **Fan-out / fan-in** patterns (parallel jobs that converge)
13
+ - **Retry / error-branch logic** managed by the workflow runner, not the jobs
14
+ - **Scheduled multi-step orchestration** (set `schedule:` in `manifest.yaml`)
15
+
16
+ Cloud Workflows handles state, retries, fan-out, and observability.
17
+ Your jobs stay stateless and idempotent — that's the design point.
18
+
19
+ The workflow must live in `workflows/<name>/` with:
20
+
21
+ ```
22
+ workflows/<name>/
23
+ ├── workflow.yaml # Cloud Workflows DSL — the DAG itself
24
+ └── manifest.yaml # Name, schedule, log level, schedule input
25
+ ```
26
+
27
+ **Prerequisite:** The project must have a valid `broadchurch.yaml`,
28
+ and any jobs the workflow references must already be deployed via
29
+ `/deploy_job`.
30
+
31
+ ---
32
+
33
+ ## Step 1: Read Configuration
34
+
35
+ ```bash
36
+ cat broadchurch.yaml
37
+ ```
38
+
39
+ **If the file does not exist:** Stop and tell the user the project
40
+ isn't provisioned yet.
41
+
42
+ Extract:
43
+
44
+ - `tenant.org_id`
45
+ - `gateway.url`
46
+
47
+ ---
48
+
49
+ ## Step 2: Discover Workflows
50
+
51
+ ```bash
52
+ ls -d workflows/*/
53
+ ```
54
+
55
+ **If no directories exist:** Suggest copying `workflows/example_workflow/`
56
+ or creating a new one matching the structure above. Stop here.
57
+
58
+ **Skip `example_workflow`** — it's a template placeholder. Filter it out.
59
+
60
+ **If multiple remain:** Ask which to deploy.
61
+ **If one remains:** Proceed without confirmation.
62
+
63
+ ---
64
+
65
+ ## Step 3: Validate Workflow Structure
66
+
67
+ ```bash
68
+ ls workflows/<name>/workflow.yaml workflows/<name>/manifest.yaml
69
+ ```
70
+
71
+ Validate `workflow.yaml` is syntactically valid:
72
+
73
+ ```bash
74
+ yq -e '.main.steps' workflows/<name>/workflow.yaml >/dev/null
75
+ ```
76
+
77
+ Validate `manifest.yaml` has a name:
78
+
79
+ ```bash
80
+ yq -e '.name' workflows/<name>/manifest.yaml >/dev/null
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Step 4: Ensure Referenced Jobs Are Deployed
86
+
87
+ Cloud Workflows can't deploy referenced jobs for you. Check the
88
+ workflow definition for any `googleapis.run.v1.namespaces.jobs.run`
89
+ calls and verify those jobs exist:
90
+
91
+ ```bash
92
+ grep -oP 'jobs/\K[a-z0-9-]+' workflows/<name>/workflow.yaml | sort -u
93
+ ```
94
+
95
+ For each job name found, confirm it appears in the Portal:
96
+
97
+ ```bash
98
+ curl -sf "<GATEWAY_URL>/api/projects/<ORG_ID>/jobs" | jq '.jobs[].job_name'
99
+ ```
100
+
101
+ If a referenced job isn't deployed yet:
102
+
103
+ > The workflow `<name>` references job `<job>` which isn't deployed.
104
+ > Deploy it first with `/deploy_job <job>`.
105
+
106
+ ---
107
+
108
+ ## Step 5: Ensure Code is Pushed
109
+
110
+ ```bash
111
+ git status
112
+ ```
113
+
114
+ If there are uncommitted changes in `workflows/<name>/`, prompt to
115
+ commit and push.
116
+
117
+ ---
118
+
119
+ ## Step 6: Trigger Deployment
120
+
121
+ ```bash
122
+ curl -sf -X POST "<GATEWAY_URL>/api/projects/<ORG_ID>/deploy" \
123
+ -H "Content-Type: application/json" \
124
+ -d '{"type": "workflow", "name": "<WORKFLOW_NAME>"}'
125
+ ```
126
+
127
+ **If 404:** The directory may not exist on GitHub yet. Push first.
128
+
129
+ **If success:** The Portal has triggered the `deploy-workflow.yml`
130
+ GitHub Actions workflow.
131
+
132
+ ---
133
+
134
+ ## Step 7: Monitor Progress
135
+
136
+ > Workflow deployment triggered.
137
+ >
138
+ > - **Workflow:** <name>
139
+ > - **GitHub Action:** deploy-workflow.yml
140
+ >
141
+ > Typically completes in under a minute (workflow definitions are tiny).
142
+ >
143
+ > Once deployed:
144
+ >
145
+ > - The workflow is executable via the Portal "Run now" button on the Workflows tab
146
+ > - If `schedule:` is set, Cloud Scheduler invokes it automatically
147
+ > - Execution history is in the Portal Workflows tab
148
+
149
+ ---
150
+
151
+ ## Step 8: (Optional) Trigger a Test Execution
152
+
153
+ ```bash
154
+ curl -sf -X POST "<GATEWAY_URL>/api/projects/<ORG_ID>/workflows/<WORKFLOW_NAME>/run" \
155
+ -H "Content-Type: application/json" \
156
+ -d '{"input": {"limit": 10}}'
157
+ ```
158
+
159
+ Then poll for the result:
160
+
161
+ ```bash
162
+ curl -sf "<GATEWAY_URL>/api/projects/<ORG_ID>/workflows/<WORKFLOW_NAME>/executions" | jq '.executions[0]'
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Troubleshooting
168
+
169
+ ### Workflow rejected with parsing errors
170
+
171
+ Cloud Workflows YAML has strict syntax. Common gotchas:
172
+
173
+ - Indentation must use spaces (no tabs)
174
+ - `${...}` expressions must be quoted strings in YAML
175
+ - `parallel for` requires `value` and `range` (or `in`) under `for:`
176
+ - `args:` for `googleapis.run.v1.*` must include `name: namespaces/<project>/jobs/<job>`
177
+
178
+ Reference: https://cloud.google.com/workflows/docs/reference/syntax
179
+
180
+ ### Workflow times out
181
+
182
+ Cloud Workflows has a 1-year max duration but each step has its own
183
+ limits. Long-running step calls (`googleapis.run.v1.namespaces.jobs.run`)
184
+ should use `connector_params: {timeout: <seconds>}` to bound them.
185
+
186
+ ### Schedule doesn't fire
187
+
188
+ Cloud Scheduler entries are named `workflow-<name>`. Verify in the
189
+ Cloud Console.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottagraph-app/aether-instructions",
3
- "version": "1.1.48",
3
+ "version": "1.1.50",
4
4
  "description": "Cursor commands and skills for Aether development",
5
5
  "files": [
6
6
  "commands",
@@ -32,27 +32,28 @@ topics that apply.
32
32
 
33
33
  ## Files
34
34
 
35
- | File | When to read |
36
- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37
- | [architecture.md](architecture.md) | Project structure, navigation, server routes, agents, MCP. Read when adding pages, navigation, or server-side functionality. |
38
- | [data.md](data.md) | How this app reads platform data (clients, schema discovery, entity/news/filings access, common gotchas). Read when building any feature that fetches or displays platform data. |
39
- | [cookbook.md](cookbook.md) | Copy-paste UI patterns for common pages: data table, form, chart, dialog, master-detail. For data-fetching recipes see `cookbook-data.md`. |
40
- | [cookbook-data.md](cookbook-data.md) | Data-fetching UI recipes: entity search, news feed, filings, and related helpers. Read with `data.md` when building pages that display platform data. |
41
- | [design.md](design.md) | `DESIGN.md` workflow, feature docs, starter-app-is-placeholder guidance. Read when starting work, planning features, or updating project design. |
42
- | [ui.md](ui.md) | Vue/Vuetify page templates, layouts, scrollable content, data tables, loading states. Applies when creating or editing page templates (`pages/**`, `components/**`). |
43
- | [pref.md](pref.md) | User preferences and KV storage: `usePrefsStore`, `Pref<T>`, app namespacing (`NUXT_PUBLIC_APP_ID`). Read when working on settings persistence. |
44
- | [branding.md](branding.md) | Visual styling, colors, typography, theming, branding, UI appearance. Read when updating brand assets or theme code. |
45
- | [server.md](server.md) | Nitro server-side API routes (`server/**`): file-based routing, event handlers, image proxy. Read when adding server routes. For storage backends see `storage.md`; for platform-data proxying see `server-data.md`. |
46
- | [server-data.md](server-data.md) | Reading platform data from Nitro server routes (`server/**`). Read when a server route needs to fetch platform data on behalf of the app. |
47
- | [storage.md](storage.md) | Storage backends: KV (always on) vs Neon Postgres (if provisioned). Read when choosing persistence, wiring `getRedis()`/`getDb()`, creating tables, or handling missing credentials gracefully. |
48
- | [agents.md](agents.md) | Conventions for developing ADK agents in `agents/**`. Read when writing or editing agent code. |
49
- | [agents-data.md](agents-data.md) | How ADK agents access platform data (authentication, local testing env vars, mode-specific wiring). Read when an agent needs platform data (`agents/**`). |
50
- | [mcp-servers.md](mcp-servers.md) | Conventions for developing MCP servers in `mcp-servers/**`. Read when writing or editing FastMCP servers. |
51
- | [deployment.md](deployment.md) | App, agent, and MCP server deployment targets (Vercel, Vertex AI Agent Engine, Cloud Run). Read when pushing to main, running `/deploy_agent` or `/deploy_mcp`, or explaining how code reaches production. |
52
- | [env.md](env.md) | `.env` variable reference (`APP_ID`, `USER_NAME`, `QUERY_SERVER_ADDRESS`, etc.). Read when adding env vars, configuring Auth0 bypass, or inspecting runtime config. |
53
- | [local-setup.md](local-setup.md) | Manual local dev setup (`npm run init -- --local`, `npm run dev`). Read when running the app locally outside Cursor Cloud. |
54
- | [cursor-cloud.md](cursor-cloud.md) | Cursor Cloud environment quirks (Node managed by env, dev server auto-started, skip browser testing during initial setup). Read when `$HOME` is under `/root` or `/home/ubuntu`, or when a dev-server terminal was auto-started. |
55
- | [claude-code-cloud.md](claude-code-cloud.md) | Claude Code (claude.ai/code) cloud session quirks (ephemeral sessions, Claude GitHub App, env auto-runs init + dev server, skip browser testing during initial setup). Read when running in claude.ai/code, especially `$HOME` under `/root` or `/home/ubuntu`. |
56
- | [git-support.md](git-support.md) | Git commit workflow and conventions. Read when finishing implementation work, making commits, or troubleshooting git/pre-commit failures. |
57
- | [something-broke.md](something-broke.md) | Error recovery and build failure troubleshooting. Read when something broke, build failed, `npm run build` errors, or the user wants to restore previous behavior. |
58
- | [instructions_warning.md](instructions_warning.md) | Warning about editing `.agents/` files managed by `@yottagraph-app/aether-instructions`. Read before modifying installed instruction files. |
35
+ | File | When to read |
36
+ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37
+ | [architecture.md](architecture.md) | Project structure, navigation, server routes, agents, MCP. Read when adding pages, navigation, or server-side functionality. |
38
+ | [data.md](data.md) | How this app reads platform data (clients, schema discovery, entity/news/filings access, common gotchas). Read when building any feature that fetches or displays platform data. |
39
+ | [cookbook.md](cookbook.md) | Copy-paste UI patterns for common pages: data table, form, chart, dialog, master-detail. For data-fetching recipes see `cookbook-data.md`. |
40
+ | [cookbook-data.md](cookbook-data.md) | Data-fetching UI recipes: entity search, news feed, filings, and related helpers. Read with `data.md` when building pages that display platform data. |
41
+ | [design.md](design.md) | `DESIGN.md` workflow, feature docs, starter-app-is-placeholder guidance. Read when starting work, planning features, or updating project design. |
42
+ | [ui.md](ui.md) | Vue/Vuetify page templates, layouts, scrollable content, data tables, loading states. Applies when creating or editing page templates (`pages/**`, `components/**`). |
43
+ | [pref.md](pref.md) | User preferences and KV storage: `usePrefsStore`, `Pref<T>`, app namespacing (`NUXT_PUBLIC_APP_ID`). Read when working on settings persistence. |
44
+ | [branding.md](branding.md) | Visual styling, colors, typography, theming, branding, UI appearance. Read when updating brand assets or theme code. |
45
+ | [server.md](server.md) | Nitro server-side API routes (`server/**`): file-based routing, event handlers, image proxy. Read when adding server routes. For storage backends see `storage.md`; for platform-data proxying see `server-data.md`. |
46
+ | [server-data.md](server-data.md) | Reading platform data from Nitro server routes (`server/**`). Read when a server route needs to fetch platform data on behalf of the app. |
47
+ | [storage.md](storage.md) | Storage backends: KV (always on) vs Neon Postgres (if provisioned). Read when choosing persistence, wiring `getRedis()`/`getDb()`, creating tables, or handling missing credentials gracefully. |
48
+ | [bigquery.md](bigquery.md) | Analytical reads via the portal gateway: `isBigQueryConfigured()`, `listDatasets()`, `listTables()`, `runQuery()`. Read whenever the task touches BigQuery — and DO NOT add `@google-cloud/bigquery` or paste service-account keys (BC 1.0 pattern, not supported). |
49
+ | [agents.md](agents.md) | Conventions for developing ADK agents in `agents/**`. Read when writing or editing agent code. |
50
+ | [agents-data.md](agents-data.md) | How ADK agents access platform data (authentication, local testing env vars, mode-specific wiring). Read when an agent needs platform data (`agents/**`). |
51
+ | [mcp-servers.md](mcp-servers.md) | Conventions for developing MCP servers in `mcp-servers/**`. Read when writing or editing FastMCP servers. |
52
+ | [deployment.md](deployment.md) | App, agent, and MCP server deployment targets (Vercel, Vertex AI Agent Engine, Cloud Run). Read when pushing to main, running `/deploy_agent` or `/deploy_mcp`, or explaining how code reaches production. |
53
+ | [env.md](env.md) | `.env` variable reference (`APP_ID`, `USER_NAME`, `QUERY_SERVER_ADDRESS`, etc.). Read when adding env vars, configuring Auth0 bypass, or inspecting runtime config. |
54
+ | [local-setup.md](local-setup.md) | Manual local dev setup (`npm run init -- --local`, `npm run dev`). Read when running the app locally outside Cursor Cloud. |
55
+ | [cursor-cloud.md](cursor-cloud.md) | Cursor Cloud environment quirks (Node managed by env, dev server auto-started, skip browser testing during initial setup). Read when `$HOME` is under `/root` or `/home/ubuntu`, or when a dev-server terminal was auto-started. |
56
+ | [claude-code-cloud.md](claude-code-cloud.md) | Claude Code (claude.ai/code) cloud session quirks (ephemeral sessions, Claude GitHub App, env auto-runs init + dev server, skip browser testing during initial setup). Read when running in claude.ai/code, especially `$HOME` under `/root` or `/home/ubuntu`. |
57
+ | [git-support.md](git-support.md) | Git commit workflow and conventions. Read when finishing implementation work, making commits, or troubleshooting git/pre-commit failures. |
58
+ | [something-broke.md](something-broke.md) | Error recovery and build failure troubleshooting. Read when something broke, build failed, `npm run build` errors, or the user wants to restore previous behavior. |
59
+ | [instructions_warning.md](instructions_warning.md) | Warning about editing `.agents/` files managed by `@yottagraph-app/aether-instructions`. Read before modifying installed instruction files. |
@@ -0,0 +1,200 @@
1
+ # BigQuery
2
+
3
+ The tenant app reads BigQuery through the **Broadchurch Portal gateway**.
4
+ The Vercel-hosted Aether app NEVER holds GCP credentials directly — the
5
+ portal runs queries in the tenant's per-tenant GCP project on the app's
6
+ behalf, using its own service account.
7
+
8
+ | Capability | How to check | Env var | Utility file |
9
+ | ------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------- |
10
+ | **BigQuery (analytical)** | `process.env.NUXT_PUBLIC_BIGQUERY_ENABLED === 'true'` | `NUXT_PUBLIC_BIGQUERY_ENABLED` | `server/utils/bigquery.ts` (pre-scaffolded) |
11
+ | | OR `curl <gateway.url>/api/tenants/<org_id>` → `gcp.bigquery` set | `NUXT_PUBLIC_BIGQUERY_DATASET_ID`, `NUXT_PUBLIC_BIGQUERY_PROJECT_ID`, `NUXT_PUBLIC_BIGQUERY_LOCATION` | |
12
+
13
+ Only available if the tenant was provisioned with BigQuery enabled (or
14
+ had it enabled later via the portal's "Enable BigQuery" action). Check
15
+ `isBigQueryConfigured()` before calling any of the helpers.
16
+
17
+ For transactional / relational data see [storage.md](storage.md)
18
+ (Postgres + KV); BigQuery is the analytical store, optimized for
19
+ append-only / large columnar scans.
20
+
21
+ ## Critical: never do these
22
+
23
+ The agent reflexively reaches for `@google-cloud/bigquery` and a
24
+ service-account-key env var when asked to talk to BigQuery — that's the
25
+ **BC 1.0 pattern** and it is not supported here. If you find yourself
26
+ about to write any of these, **stop**, re-read this file, and use
27
+ `server/utils/bigquery.ts` instead:
28
+
29
+ - DO NOT add `@google-cloud/bigquery` to `package.json`. It only ships
30
+ with the Broadchurch Portal where the SA actually exists; in the
31
+ tenant app it would fail at runtime with "could not load default
32
+ credentials".
33
+ - DO NOT paste a JSON service-account key into Vercel env (as
34
+ `GCP_SERVICE_ACCOUNT_KEY` or any other name). There is no key for the
35
+ app to use — the portal is what holds credentials. The Vercel env is
36
+ also not a secure place to put long-lived GCP keys.
37
+ - DO NOT add `GOOGLE_APPLICATION_CREDENTIALS`. That env var points at a
38
+ key file on disk; Vercel has no such file and no way to obtain one.
39
+ - DO NOT call the BigQuery REST API directly from `<script setup>` or
40
+ any client-side code. Always go through a Nitro server route
41
+ (`server/api/**`).
42
+
43
+ ## Where credentials come from
44
+
45
+ There is no credential on the tenant side. The portal gateway resolves
46
+ the tenant's GCP project from `org_id` (passed in the URL) and runs
47
+ jobs with the portal SA's ADC. Inside `bc-{slug}` the portal SA has:
48
+
49
+ - `roles/bigquery.jobUser` + `roles/bigquery.metadataViewer` at the
50
+ project level (list datasets/tables, run SELECT jobs).
51
+ - `roles/bigquery.dataEditor` on `bc_{slug}_analytics` (read row data).
52
+
53
+ That last binding scopes read access: the picker can list every dataset
54
+ the agent or tenant has created in the project, but `runQuery()` will
55
+ fail on datasets the portal hasn't been ACL'd into. The intended
56
+ analytical write target is `bc_{slug}_analytics`.
57
+
58
+ ## `isBigQueryConfigured()` — feature gating
59
+
60
+ The provisioner injects `NUXT_PUBLIC_BIGQUERY_ENABLED=true` into the
61
+ Vercel env when BQ is on. Use it to gate UI:
62
+
63
+ ```vue
64
+ <script setup lang="ts">
65
+ const bqEnabled = useRuntimeConfig().public.bigqueryEnabled === 'true';
66
+ </script>
67
+
68
+ <template>
69
+ <v-card v-if="bqEnabled">…analytics UI…</v-card>
70
+ <v-card v-else>
71
+ <v-card-title>BigQuery is not configured</v-card-title>
72
+ <v-card-text> Ask the platform operator to enable BigQuery for this app. </v-card-text>
73
+ </v-card>
74
+ </template>
75
+ ```
76
+
77
+ For server routes, use `isBigQueryConfigured()` from
78
+ `server/utils/bigquery.ts` and return a 503-style message when it's
79
+ false (don't throw — the page should still render).
80
+
81
+ ## Server-route pattern
82
+
83
+ All BQ access lives in `server/api/**`. Helpers exposed by
84
+ `server/utils/bigquery.ts`:
85
+
86
+ | Helper | Returns |
87
+ | --------------------------------- | ---------------------------------------- |
88
+ | `isBigQueryConfigured()` | `boolean` |
89
+ | `getDefaultDataset()` | `string \| null` — `bc_{slug}_analytics` |
90
+ | `getBigQueryProjectId()` | `string \| null` — `bc-{slug}` |
91
+ | `getBigQueryLocation()` | `string \| null` — e.g. `us-central1` |
92
+ | `listDatasets()` | `BqDataset[]` |
93
+ | `listTables(datasetId, options?)` | `BqTable[]` |
94
+ | `runQuery(sql, options?)` | `BqQueryResult` |
95
+ | `toRowObjects(result)` | `Record<string, unknown>[]` |
96
+
97
+ ### Example: list datasets
98
+
99
+ ```typescript
100
+ // server/api/bq/datasets.get.ts
101
+ import { isBigQueryConfigured, listDatasets } from '~/server/utils/bigquery';
102
+
103
+ export default defineEventHandler(async () => {
104
+ if (!isBigQueryConfigured()) {
105
+ throw createError({ statusCode: 503, statusMessage: 'BigQuery not configured' });
106
+ }
107
+ return await listDatasets();
108
+ });
109
+ ```
110
+
111
+ ### Example: list tables in a dataset
112
+
113
+ ```typescript
114
+ // server/api/bq/tables/[dataset].get.ts
115
+ import { isBigQueryConfigured, listTables } from '~/server/utils/bigquery';
116
+
117
+ export default defineEventHandler(async (event) => {
118
+ if (!isBigQueryConfigured()) {
119
+ throw createError({ statusCode: 503, statusMessage: 'BigQuery not configured' });
120
+ }
121
+ const dataset = getRouterParam(event, 'dataset');
122
+ if (!dataset) {
123
+ throw createError({ statusCode: 400, statusMessage: 'dataset is required' });
124
+ }
125
+ return await listTables(dataset);
126
+ });
127
+ ```
128
+
129
+ ### Example: run a parameterized SELECT
130
+
131
+ ```typescript
132
+ // server/api/bq/events.get.ts
133
+ import { isBigQueryConfigured, runQuery, toRowObjects } from '~/server/utils/bigquery';
134
+
135
+ export default defineEventHandler(async (event) => {
136
+ if (!isBigQueryConfigured()) {
137
+ throw createError({ statusCode: 503, statusMessage: 'BigQuery not configured' });
138
+ }
139
+ const { date, limit } = getQuery(event);
140
+ const result = await runQuery(
141
+ `SELECT * FROM events WHERE event_date = @date ORDER BY ts DESC LIMIT @limit`,
142
+ {
143
+ params: [
144
+ { name: 'date', type: 'DATE', value: String(date ?? '2026-01-01') },
145
+ { name: 'limit', type: 'INT64', value: Number(limit ?? 100) },
146
+ ],
147
+ }
148
+ );
149
+ return {
150
+ schema: result.schema,
151
+ rows: toRowObjects(result),
152
+ truncated: result.truncated,
153
+ };
154
+ });
155
+ ```
156
+
157
+ `defaultDataset` is set automatically to `bc_{slug}_analytics` for
158
+ unqualified table refs. Pass `options.defaultDataset` to override.
159
+
160
+ ## Guardrails the gateway enforces
161
+
162
+ The portal will reject:
163
+
164
+ - DML / DDL (`INSERT`, `DELETE`, `UPDATE`, `CREATE`, `DROP`, `MERGE`,
165
+ `TRUNCATE`, etc.). Only `SELECT` / `WITH` / `CALL` (for TVFs) are
166
+ allowed. If you need to write to BigQuery, do it from a Cloud Run
167
+ Job in the tenant project — that's the documented append path.
168
+ - Queries that would scan more than 10 GB (default cap 1 GB). The
169
+ gateway sets BigQuery's `maximumBytesBilled` server-side; over-scope
170
+ queries fail with a clear error from BQ before any cost is incurred.
171
+ - More than 10,000 rows returned in one call (default 1,000). Paginate
172
+ with `LIMIT … OFFSET` in SQL if you need more.
173
+ - 30s wall-clock. The response sets `truncated: true` if BQ ran out of
174
+ time producing the first page.
175
+
176
+ ## Local dev
177
+
178
+ `NUXT_PUBLIC_BIGQUERY_*` are intentionally unset in local `.env`.
179
+ `isBigQueryConfigured()` returns false, helpers throw with a clear
180
+ message ("BigQuery is not configured for this tenant…"). Test BQ
181
+ features on the deployed preview/production URL where the env vars
182
+ are injected.
183
+
184
+ ## Where the data goes
185
+
186
+ Cloud Run Jobs and Workflows in the tenant project write to
187
+ `bc_{slug}_analytics` (see [`deployment.md`](deployment.md) and the
188
+ portal's compute-jobs docs). The tenant app reads from that dataset
189
+ through this gateway. Round-trip:
190
+
191
+ ```
192
+ Cloud Run Job → BigQuery (bc_{slug}_analytics) → Portal gateway → Aether app
193
+
194
+ portal SA's ADC
195
+ ```
196
+
197
+ If you see "Permission denied on resource project" in the gateway
198
+ response, the dataset's IAM was likely created in a previous tenant
199
+ version and is missing the portal SA. Re-run "Enable BigQuery" in the
200
+ portal cockpit to reconcile.
@@ -5,6 +5,12 @@ primary data source — use it first for any data needs (entities, news,
5
5
  filings, sentiment, relationships, events). Do NOT call external APIs
6
6
  (e.g. sec.gov, Wikipedia) for data that the platform already provides.
7
7
 
8
+ Tenant-owned analytical data (event streams, derived tables, time-series
9
+ features that Cloud Run Jobs write into the tenant project) lives in
10
+ BigQuery, not the Elemental API. For that see [`bigquery.md`](bigquery.md)
11
+ — and importantly, do NOT add `@google-cloud/bigquery` or any GCP
12
+ credentials to this app. Queries go through the portal gateway.
13
+
8
14
  The Elemental API provides access to the Lovelace Knowledge Graph through
9
15
  the Query Server. Use it to search for entities, retrieve properties,
10
16
  explore relationships, and analyze sentiment. New data sources are added
@@ -236,6 +242,7 @@ types or property names. Instead, discover them at runtime:
236
242
  and properties (PIDs) available in the system. See `schema.md`.
237
243
 
238
244
  The schema response contains:
245
+
239
246
  - **Flavors** (entity types): Company, Person, GovernmentOrg, etc.
240
247
  Each flavor has a numeric ID and a human-readable name.
241
248
  - **PIDs** (properties): name, country, industry, lei_code, etc.
@@ -40,17 +40,27 @@ If a user asks about this error, explain the `npm run format` requirement.
40
40
 
41
41
  ## Pushing
42
42
 
43
- **Push directly to main.** Vercel auto-deploys on push to main, so this
44
- gets the app live immediately.
43
+ **Push directly to main.** Vercel auto-deploys on push to `main`, which
44
+ is how this project gets live. Creating a PR breaks that auto-deploy
45
+ flow and disrupts the smooth first-run experience the wizard sets up.
45
46
 
46
- **Do NOT create pull requests** — do not run `gh pr create` or create
47
- feature branches. In Cursor Cloud environments, the GitHub integration
48
- token lacks PR permissions (known Cursor issue). Pushing to main is the
49
- correct workflow.
47
+ **Do NOT create pull requests** — do not run `gh pr create`, do not
48
+ create a feature branch, and do not push to anything other than `main`.
49
+ The `main` branch on tenant repos is **not** protected; `git push
50
+ origin main` will succeed.
51
+
52
+ > Note: tenant repos follow a different policy than the upstream
53
+ > `aether-dev` template. `aether-dev` is PR-only because its `main` is
54
+ > protected; tenant projects deliberately are not. Do not import the
55
+ > `aether-dev` habit when working in a tenant repo.
50
56
 
51
57
  ```bash
52
58
  git push origin main
53
59
  ```
54
60
 
61
+ If a push to `main` fails because of an actual permission issue (not
62
+ branch protection), report the exact error to the user — do not fall
63
+ back to a PR as a workaround.
64
+
55
65
  If running locally (not in Cursor Cloud), the user may prefer a PR-based
56
66
  workflow — ask before pushing directly to main in that case.
@@ -1,5 +1,12 @@
1
1
  # Storage
2
2
 
3
+ This skill covers **transactional / state** storage — KV (always on) and
4
+ Neon Postgres (if provisioned). For **analytical / append-only** reads
5
+ from large datasets (anything you'd reach for a data warehouse for), see
6
+ [`bigquery.md`](bigquery.md) instead — BigQuery is provisioned per-tenant
7
+ in the GCP tenant project and read through the portal gateway, NOT via
8
+ direct GCP credentials.
9
+
3
10
  Two storage services are available. KV is always connected; Neon Postgres
4
11
  is only present if the tenant was provisioned with it. Each store has its
5
12
  own way of checking availability — see the **How to check** column below:
@@ -170,16 +170,24 @@ git add -A
170
170
  git commit -m "[Agent commit] Initial app build from project brief"
171
171
  ```
172
172
 
173
- **Push directly to main.** Vercel auto-deploys on push to main, so this
174
- gets the app live immediately. Do NOT try to create a pull request via
175
- `gh pr create` in Cursor Cloud environments, the GitHub integration
176
- token lacks PR permissions (known Cursor issue). Pushing to main is the
177
- correct workflow.
173
+ **Push directly to main. Do NOT create a pull request or a feature branch.**
174
+
175
+ Vercel auto-deploys on push to `main`, which is how this project gets
176
+ live. Creating a PR blocks that auto-deploy and breaks the smooth
177
+ first-run experience the user is expecting from the wizard. The `main`
178
+ branch on tenant repos is **not** protected — `git push origin main`
179
+ will succeed. Tenant repos follow a different policy than the upstream
180
+ `aether-dev` template (which IS PR-only) — do not import that habit.
178
181
 
179
182
  ```bash
180
183
  git push origin main
181
184
  ```
182
185
 
186
+ Specifically: do NOT run `gh pr create`, do NOT create a feature
187
+ branch, and do NOT push to anything other than `main`. If a push to
188
+ `main` fails because of a real permission issue (not branch
189
+ protection), report the exact error rather than falling back to a PR.
190
+
183
191
  If running locally (not in Cursor Cloud), the user may prefer a PR-based
184
192
  workflow — ask before pushing directly to main in that case.
185
193