docs: add AI agent bootstrap / quickstart document
Concise onboarding doc covering: project overview, critical rules (IDAA privacy, no-rm, svelte-check), env/deploy cheat sheet, Svelte 5 runes patterns, V3 API patterns, naming conventions, real past mistakes, source layout, and reading order for deeper dives. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
300
documentation/BOOTSTRAP__AI_Agent_Quickstart.md
Normal file
300
documentation/BOOTSTRAP__AI_Agent_Quickstart.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# Aether SvelteKit — AI Agent Bootstrap / Quickstart
|
||||
> **Read this first.** This doc is the fast path to being productive on this project.
|
||||
> It covers the rules, patterns, and gotchas that matter most.
|
||||
> Deep dives are in the linked docs at the bottom.
|
||||
|
||||
---
|
||||
|
||||
## 1. What This Project Is
|
||||
|
||||
**Aether** is an event management platform built by One Sky IT (Scott Idem).
|
||||
This repo is the frontend: **Svelte 5 (runes mode) + SvelteKit v2**.
|
||||
|
||||
The backend is a separate repo (`aether_api_fastapi`) — a FastAPI + MariaDB app
|
||||
running in Docker. The frontend talks to it exclusively via the V3 REST API.
|
||||
|
||||
**Key clients:**
|
||||
- **Conference organizers** — Presentation Management (pres_mgmt), Launcher, Badges
|
||||
- **Exhibitors** — Leads capture
|
||||
- **IDAA** — International Doctors in Alcoholics Anonymous (private medical/recovery community)
|
||||
|
||||
**Stack at a glance:**
|
||||
|
||||
| Layer | Technology |
|
||||
|---|---|
|
||||
| Framework | Svelte 5 (runes mode) + SvelteKit v2 |
|
||||
| Styling | Tailwind CSS v4 + Flowbite (Skeleton UI being phased out) |
|
||||
| State | `$state`/`$derived` runes + Dexie.js IndexedDB (`liveQuery`) |
|
||||
| Icons | Lucide (`@lucide/svelte`) |
|
||||
| Editors | CodeMirror 6 (primary), Edra/TipTap (secondary) |
|
||||
| Native | Electron app for onsite launcher (`src/lib/electron/electron_relay.ts`) |
|
||||
| Backend | FastAPI + MariaDB, V3 API (`/v3/crud/`, `/v3/lookup/`) |
|
||||
| Auth | Custom headers: `x-aether-api-key` + `x-account-id` (no Bearer tokens) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Critical Rules — Read Before Touching Any Code
|
||||
|
||||
### Privacy (Sev-1 class failures if violated)
|
||||
- **IDAA content is ALWAYS private.** All routes under `/idaa/` require authentication.
|
||||
A previous AI agent accidentally made IDAA bulletin board data publicly accessible.
|
||||
This is the single most serious class of mistake on this project. When in doubt — it's private.
|
||||
- **Journals** are private personal data. Always authenticated.
|
||||
|
||||
### File Safety
|
||||
- **Never use `rm`** to delete files. Move to `~/tmp/gemini_trash` instead.
|
||||
- Never commit `.env` files, API keys, or passwords.
|
||||
|
||||
### Before Every Commit
|
||||
- Run `npx svelte-check` — zero errors, zero warnings. No exceptions.
|
||||
- Atomic commits: one component or one fix per commit.
|
||||
|
||||
### Before Starting Any Task
|
||||
- Read `documentation/TODO__Agents.md` — it has active tasks, known bugs, and context
|
||||
about what was recently changed and why.
|
||||
|
||||
### V3 API — Never Include the Object ID in PATCH Body Fields
|
||||
The ID is in the URL. Including it in `data_kv` causes a `400: Unknown column in SET`.
|
||||
```ts
|
||||
// WRONG — causes 400 error:
|
||||
update_ae_obj__event_file({ event_file_id, data_kv: { event_file_id, file_purpose: 'final' } })
|
||||
|
||||
// CORRECT:
|
||||
update_ae_obj__event_file({ event_file_id, data_kv: { file_purpose: 'final' } })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Environment & Deploy Cheat Sheet
|
||||
|
||||
There are **two separate `.env` systems** — do not confuse them:
|
||||
|
||||
| System | File | Controls |
|
||||
|---|---|---|
|
||||
| `aether_container_env/.env` | Docker orchestration | Ports, `AE_CFG_ID`, replicas, paths |
|
||||
| `aether_app_sveltekit/.env.*` | Vite/SvelteKit build | `PUBLIC_*` API vars baked into the JS bundle |
|
||||
|
||||
**The 4 commands you run and which env file each uses:**
|
||||
|
||||
| Command | Env file read |
|
||||
|---|---|
|
||||
| `npm run dev` | `aether_app_sveltekit/.env.local` (Vite dev server, localhost:5173) |
|
||||
| `npm run build:docker:dev` | `aether_app_sveltekit/.env.dev` (baked into local Docker image) |
|
||||
| `npm run deploy:remote:test` | `/srv/apps/test_aether_app_sveltekit/.env.test` on Linode |
|
||||
| `npm run deploy:remote:prod` | `/srv/apps/prod_aether_app_sveltekit/.env.prod` on Linode |
|
||||
|
||||
**The `.env.*` files are gitignored** (only `.default` templates are tracked). They must be
|
||||
placed manually on each server during initial setup. On the workstation you only need
|
||||
`.env.local` and `.env.dev`. The Linode servers each have exactly one env file for their environment.
|
||||
|
||||
**What goes in every SvelteKit env file** (same 8 vars, different values per env):
|
||||
```env
|
||||
PUBLIC_AE_API_PROTOCOL=https
|
||||
PUBLIC_AE_API_SERVER=<api server hostname>
|
||||
PUBLIC_AE_API_BAK_SERVER=<bak api hostname>
|
||||
PUBLIC_AE_API_PORT=443
|
||||
PUBLIC_AE_API_PATH=
|
||||
PUBLIC_AE_API_SECRET_KEY=<key>
|
||||
PUBLIC_AE_CRUD_SUPER_KEY=<key>
|
||||
PUBLIC_AE_BOOTSTRAP_KEY=<key>
|
||||
PUBLIC_AE_NO_ACCOUNT_ID=No_Account_ID_Here
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Svelte 5 Runes Mode — Key Patterns & Gotchas
|
||||
|
||||
This codebase is **fully Svelte 5 runes mode**. No Svelte 4 syntax.
|
||||
|
||||
### The basics
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
// Props — with optional two-way binding
|
||||
interface Props { count?: number; label: string; }
|
||||
let { count = $bindable(0), label }: Props = $props();
|
||||
|
||||
// Reactive state
|
||||
let value = $state('');
|
||||
let upper = $derived(value.toUpperCase());
|
||||
|
||||
// Side effects (replaces onMount + $: reactive)
|
||||
$effect(() => {
|
||||
console.log('value changed:', value);
|
||||
return () => { /* cleanup */ };
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### What NOT to use (Svelte 4 patterns — do not introduce)
|
||||
```ts
|
||||
// ❌ No writable() stores for component state
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
// ❌ No reactive declarations
|
||||
$: doubled = count * 2;
|
||||
|
||||
// ❌ No onDestroy for cleanup — use $effect return instead
|
||||
onDestroy(() => cleanup());
|
||||
```
|
||||
|
||||
### `$bindable()` vs `$state()`
|
||||
- Use `$bindable()` when the parent needs two-way binding on a prop.
|
||||
- Use `$state()` for local component state with no external binding.
|
||||
|
||||
### Store reactivity trap (important for `$effect`)
|
||||
The app uses `svelte-persisted-store` (Svelte 4 contract) for `$ae_loc`, `$ae_api`,
|
||||
`$ae_sess`, etc. In Svelte 5 `$effect`, reading **any field** of a Svelte 4 store
|
||||
subscribes to the **entire store**. This means unrelated writes to `$ae_loc`
|
||||
(e.g. iframe height, SWR reload) will re-trigger your effect. Be conservative about
|
||||
what you read from these stores inside `$effect` blocks. See `PROJECT__Stores_Svelte5_Migration.md`
|
||||
for the long-term fix plan.
|
||||
|
||||
### `{#await}` blocks
|
||||
```svelte
|
||||
{#await somePromise}
|
||||
<LoadingSpinner />
|
||||
{:then result}
|
||||
<div>{result}</div>
|
||||
{:catch error}
|
||||
<ErrorMessage {error} />
|
||||
{/await}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. V3 API Patterns
|
||||
|
||||
### SWR (Stale-While-Revalidate) — the standard load pattern
|
||||
Return cached Dexie data immediately, refresh from API in background.
|
||||
```ts
|
||||
async function load_ae_obj_id__my_obj({ api_cfg, obj_id }) {
|
||||
// 1. Return stale cache immediately (fast)
|
||||
const cached = await db.my_obj.get(obj_id);
|
||||
if (cached) my_obj_state = cached;
|
||||
|
||||
// 2. Fetch fresh from API in background
|
||||
_refresh_my_obj_background({ api_cfg, obj_id });
|
||||
}
|
||||
```
|
||||
|
||||
### ID convention — never use `_id_random` fields
|
||||
The V3 API uses random string IDs (e.g. `event_file_id = "aBc123"`). The `*_id_random`
|
||||
fields are legacy aliases. Always use the short form:
|
||||
```ts
|
||||
// ✅ Correct
|
||||
event_file_obj.event_file_id
|
||||
|
||||
// ❌ Wrong — legacy alias, don't use
|
||||
event_file_obj.event_file_id_random
|
||||
```
|
||||
|
||||
### PATCH — only field values in the body
|
||||
```ts
|
||||
// The obj_id goes in the URL (handled by update_ae_obj__* function).
|
||||
// Only the fields you want to update go in data_kv.
|
||||
await events_func.update_ae_obj__event_file({
|
||||
api_cfg: $ae_api,
|
||||
event_file_id: 'aBc123', // → becomes the URL path param
|
||||
data_kv: { file_purpose: 'final' } // → only changed fields
|
||||
});
|
||||
```
|
||||
|
||||
### Auth headers (set automatically by `api.ts`)
|
||||
```
|
||||
x-aether-api-key: <PUBLIC_AE_API_SECRET_KEY>
|
||||
x-account-id: <account_id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Naming Conventions
|
||||
|
||||
| Pattern | Example | Used for |
|
||||
|---|---|---|
|
||||
| `ae_comp__*` | `ae_comp__event_badge.svelte` | Route-level components |
|
||||
| `ae_<module>_comp__*` | `ae_events_comp__session_list.svelte` | Module-scoped components |
|
||||
| `element_*` | `element_input_files_tbl.svelte` | Reusable library primitives |
|
||||
| `lq__*` | `lq__journal_obj` | Read-only liveQuery |
|
||||
| `lqw__*` | `lqw__journal_obj` | Writable form snapshot liveQuery |
|
||||
| `ae_<module>__<obj>.ts` | `ae_journals__journal.ts` | Object type + functions |
|
||||
| `db_<module>.ts` | `db_journals.ts` | Dexie instance per module |
|
||||
|
||||
The **canonical pattern reference** is the Journals module (`src/lib/ae_journals/`).
|
||||
When building anything new, model it after Journals.
|
||||
|
||||
---
|
||||
|
||||
## 7. Mistakes Agents Have Made on This Project
|
||||
|
||||
These are real incidents — know them before you start.
|
||||
|
||||
1. **IDAA BB exposed publicly** — an agent removed an auth guard from the bulletin board
|
||||
route. All IDAA content must be behind authentication. Always check route guards when
|
||||
touching `/idaa/` routes.
|
||||
|
||||
2. **`event_file_id` in PATCH body (400 error)** — including the object ID in `data_kv`
|
||||
when calling `update_ae_obj__*`. The V3 API tries to `SET event_file_id = ...` which
|
||||
fails because it's a view alias, not a DB column. See Section 2 above.
|
||||
|
||||
3. **Bad `.d.ts` declaration silently hid 1368 errors** — a `declare module` in `app.d.ts`
|
||||
(a script-context file) replaced the entire `@lucide/svelte` type exports instead of
|
||||
merging. `svelte-check` showed 0 errors, masking real problems. If `svelte-check`
|
||||
suddenly drops to 0 errors, verify it's not because a bad declaration wiped a module.
|
||||
|
||||
4. **Coarse store reactivity loop** — an `$effect` that read `$ae_loc.some_field` was
|
||||
re-triggering repeatedly because unrelated writes to `$ae_loc` (e.g. SWR config reload)
|
||||
fired the effect. In Svelte 5, any read of a Svelte 4 store inside `$effect` subscribes
|
||||
to the whole store. Scope what you read carefully.
|
||||
|
||||
5. **`file_purpose == 'admin'` not hidden in Launcher** — the `hide_draft` prop hid
|
||||
`outline` and `draft` files but not `admin` files. Gaps like this happen when a new
|
||||
enum value is added to a field without auditing all the places that filter on it.
|
||||
|
||||
6. **Deleting files with `rm`** — always move to `~/tmp/gemini_trash`. A deleted file may
|
||||
contain context that's not recoverable from git if it was gitignored.
|
||||
|
||||
---
|
||||
|
||||
## 8. Source Layout (Quick Reference)
|
||||
|
||||
```
|
||||
src/lib/
|
||||
ae_api/ — API helpers (V3 preferred)
|
||||
ae_core/ — Account, User, Person, Site, hosted files
|
||||
ae_events/ — Events, sessions, presenters, badges, locations, files
|
||||
ae_journals/ — Journals (canonical/frontier model — copy patterns from here)
|
||||
ae_idaa/ — IDAA custom module (PRIVATE — always authenticated)
|
||||
elements/ — Reusable UI: V3 field editor, data store, CodeMirror, QR scanner
|
||||
electron/ — Native Electron bridge (electron_relay.ts)
|
||||
stores/ — ae_stores.ts, ae_events_stores.ts, ae_idaa_stores.ts
|
||||
|
||||
src/routes/
|
||||
/core/ — Admin (accounts, people, sites, users)
|
||||
/events/[id]/
|
||||
/(pres_mgmt)/ — Presentation management
|
||||
/(launcher)/ — Event launcher (kiosk display)
|
||||
/(badges)/ — Badge printing
|
||||
/(leads)/ — Exhibitor leads
|
||||
/journals/ — Journals
|
||||
/idaa/ — IDAA module (PRIVATE)
|
||||
/hosted_files/ — File management
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Reading Order for Deeper Dives
|
||||
|
||||
Start here, then go deeper as needed:
|
||||
|
||||
| What you need | Read |
|
||||
|---|---|
|
||||
| Active tasks + known bugs | `documentation/TODO__Agents.md` ← always first |
|
||||
| Dev workflow + commit rules | `documentation/GUIDE__Development.md` |
|
||||
| V3 API reference | `documentation/GUIDE__AE_API_V3_for_Frontend.md` |
|
||||
| Dexie / liveQuery patterns | `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` |
|
||||
| Svelte 5 patterns + pitfalls | `documentation/GEMINI__Svelte_and_Me.md` |
|
||||
| Permissions + auth levels | `documentation/AE__Permissions_and_Security.md` |
|
||||
| Electron / native launcher | `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` |
|
||||
| Store migration plan | `documentation/PROJECT__Stores_Svelte5_Migration.md` |
|
||||
| Exhibitor Leads module | `documentation/MODULE__AE_Events_Exhibitor_Leads.md` |
|
||||
| Naming conventions | `documentation/AE__Naming_Conventions.md` |
|
||||
Reference in New Issue
Block a user