feat: add system configuration platform

This commit is contained in:
2026-07-02 00:03:27 -07:00
parent aed5c6db56
commit e3e7b0d8e0
41 changed files with 5746 additions and 405 deletions

View File

@@ -0,0 +1,58 @@
# Deployment Targets
## Scenario: Wulanchabu Server Deployment
### 1. Scope / Trigger
- Trigger: the user asks to deploy this project.
- Default target: Wulanchabu server, reached through SSH host alias `wlcb1`.
- This preference is project-specific and should be used for future deployment requests unless the user explicitly names another target.
### 2. Signatures
- SSH entrypoint: `ssh wlcb1`
- Repository remote: push code to `origin` before deploying.
- Database migration command: `pnpm db:migrate`
- Production build command: `pnpm build`
### 3. Contracts
- Required environment on the server:
- `DATABASE_URL` must point to the production PostgreSQL database.
- Node.js and pnpm must be available.
- Deployment should run from the server-side checkout for this repository.
- Code should be pushed to `origin` before server deployment so `wlcb1` deploys committed source.
### 4. Validation & Error Matrix
- Missing `DATABASE_URL` -> deployment is not healthy; setup/login APIs return database configuration failures.
- Migration failure -> stop deployment and report the failing migration output.
- Build failure -> stop deployment and report the failing command output.
- SSH failure -> report that `wlcb1` could not be reached.
### 5. Good/Base/Bad Cases
- Good: commit locally, push to `origin`, SSH to `wlcb1`, update checkout, install deps, migrate DB, build, restart app.
- Base: if the server has a project-specific deploy script, run that script after pushing.
- Bad: deploy uncommitted local files, deploy to a different host by default, or skip migrations after schema changes.
### 6. Tests Required
- `pnpm lint`
- `pnpm type-check`
- `pnpm build`
- After deploy, verify the app responds and `/api/auth/bootstrap` returns a structured JSON response.
### 7. Wrong vs Correct
#### Wrong
```bash
ssh some-other-host
```
#### Correct
```bash
ssh wlcb1
```

View File

@@ -21,6 +21,7 @@
| [authentication.md](./authentication.md) | better-auth, sessions, OAuth, protected procedures | Auth-related features |
| [logging.md](./logging.md) | Structured logging, Sentry tracing, telemetry | Debugging, observability |
| [local-json-mvp.md](./local-json-mvp.md) | Local JSON persistence/session/API contracts before database adoption | Single-repo MVP features without DB/oRPC deps |
| [deployment.md](./deployment.md) | Wulanchabu deployment target and validation contract | Deploying this project |
| [performance.md](./performance.md) | Concurrency, caching, batch processing, streaming | Performance optimization |
| [ai-sdk-integration.md](./ai-sdk-integration.md) | Vercel AI SDK, tool calling, prompt patterns | AI-powered features |
| [quality.md](./quality.md) | Pre-commit checklist for backend code | Before committing |

View File

@@ -22,6 +22,8 @@
- Default data file: `.data/teatea.json`.
- Override key: `TEATEA_DATA_DIR` points to the directory containing `teatea.json`.
- `.data/` must stay ignored; it may contain account hashes and session IDs.
- Routes or layouts that read local JSON state or session cookies must opt out of static prerendering with `export const dynamic = "force-dynamic"`.
- API JSON responses backed by local JSON state or session cookies must include `Cache-Control: no-store`.
- API response shape:
```ts
@@ -51,6 +53,7 @@ type ApiResult<T extends Record<string, unknown>> =
- Good: UI submits to Route Handler, Route Handler validates input, calls `requirePermission`, mutates data through `updateData`, writes audit log, returns `ApiResult`.
- Base: Server Component reads data directly via `readData` after session/permission checks.
- Bad: UI or route handler reads `.data/teatea.json` directly, bypasses `requirePermission`, or stores auth state in localStorage.
- Bad: `/app/*`, `/login`, or `/setup` is prerendered as static content and caches a redirect based on build-time empty data.
### 6. Tests Required
@@ -60,6 +63,8 @@ type ApiResult<T extends Record<string, unknown>> =
- Manual or automated integration assertions:
- first setup creates admin and cookie session
- protected app route redirects without cookie
- protected app route renders with a valid cookie after setup and does not return a cached `/setup` redirect
- auth/bootstrap/session API responses include `Cache-Control: no-store`
- CRUD mutation persists across reload/API list
- denied permission returns 403 and writes an audit log
@@ -84,3 +89,28 @@ cookieStore.set({
});
```
#### Wrong
```ts
export default async function AppLayout({ children }: AppLayoutProps) {
const bootstrap = await getBootstrapState();
if (bootstrap.setupRequired) {
redirect("/setup");
}
return children;
}
```
#### Correct
```ts
export const dynamic = "force-dynamic";
export default async function AppLayout({ children }: AppLayoutProps) {
const bootstrap = await getBootstrapState();
if (bootstrap.setupRequired) {
redirect("/setup");
}
return children;
}
```