@percepta/create 3.1.0 → 3.1.3

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.
Files changed (62) hide show
  1. package/README.md +15 -10
  2. package/dist/{chunk-7NPWSTCY.js → chunk-CO3YWUD6.js} +31 -2
  3. package/dist/{chunk-WMJT7CB5.js → chunk-V5EJIUBJ.js} +5 -2
  4. package/dist/index.js +93 -73
  5. package/dist/{init-NP6GRXLL.js → init-EQZ2TCSJ.js} +2 -2
  6. package/dist/{status-BTHGN6QH.js → status-QW5TQDYY.js} +1 -1
  7. package/dist/{sync-3Q27L7XZ.js → sync-RLBZDOFB.js} +1 -1
  8. package/dist/{upstream-C5KFAHVR.js → upstream-TQFVPMEG.js} +1 -1
  9. package/package.json +3 -2
  10. package/templates/monorepo/.dockerignore +18 -0
  11. package/templates/monorepo/gitignore.template +1 -0
  12. package/templates/webapp/.github/workflows/__APP_NAME__-ryvn-release.yaml +6 -2
  13. package/templates/webapp/.github/workflows/__APP_NAME__-terraform-ryvn-release.yaml +98 -0
  14. package/templates/webapp/AGENTS.md +17 -5
  15. package/templates/webapp/Dockerfile +16 -7
  16. package/templates/webapp/README.md +64 -2
  17. package/templates/webapp/agent-skills/deploy.md +50 -51
  18. package/templates/webapp/agent-skills/inngest.md +4 -4
  19. package/templates/webapp/agent-skills/langfuse.md +15 -14
  20. package/templates/webapp/agent-skills/llm.md +59 -0
  21. package/templates/webapp/agent-skills/oneshot.md +14 -1
  22. package/templates/webapp/agent-skills/ryvn.md +1 -1
  23. package/templates/webapp/deploy/README.md +41 -16
  24. package/templates/webapp/deploy/ryvn/__APP_NAME__-terraform.service.yaml +10 -0
  25. package/templates/webapp/deploy/ryvn/__APP_NAME__.service.yaml +2 -2
  26. package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__-terraform.env.percepta-test.serviceinstallation.yaml +11 -0
  27. package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml +60 -11
  28. package/templates/webapp/env.example.template +20 -2
  29. package/templates/webapp/eslint.config.mjs +7 -0
  30. package/templates/webapp/gitignore.template +1 -0
  31. package/templates/webapp/next.config.ts +9 -0
  32. package/templates/webapp/package.json.template +6 -2
  33. package/templates/webapp/scripts/deploy-percepta-test.ts +837 -0
  34. package/templates/webapp/scripts/migrate.ts +3 -0
  35. package/templates/webapp/scripts/open-ryvn-deploy-pr.ts +152 -32
  36. package/templates/webapp/scripts/seed.ts +1 -1
  37. package/templates/webapp/scripts/setup-database.ts +2 -1
  38. package/templates/webapp/scripts/start.sh +3 -2
  39. package/templates/webapp/scripts/with-local-env.ts +75 -0
  40. package/templates/webapp/src/app/(app)/layout.tsx +1 -5
  41. package/templates/webapp/src/app/(auth)/auth/signin/CredentialsSignInForm.tsx +11 -1
  42. package/templates/webapp/src/app/(auth)/auth/signup/CredentialsSignUpForm.tsx +113 -0
  43. package/templates/webapp/src/app/(auth)/auth/signup/page.tsx +30 -0
  44. package/templates/webapp/src/app/global-error.tsx +1 -1
  45. package/templates/webapp/src/components/FaroProvider.tsx +2 -4
  46. package/templates/webapp/src/components/form/FormItem.tsx +2 -2
  47. package/templates/webapp/src/config/getEnvConfig.ts +14 -0
  48. package/templates/webapp/src/drizzle/db.ts +2 -1
  49. package/templates/webapp/src/drizzle/migrations/0000_eager_grandmaster.sql +3 -3
  50. package/templates/webapp/src/drizzle/migrations/meta/0000_snapshot.json +7 -19
  51. package/templates/webapp/src/drizzle/ssl.ts +5 -0
  52. package/templates/webapp/src/instrumentation.ts +102 -10
  53. package/templates/webapp/src/lib/auth/index.ts +1 -1
  54. package/templates/webapp/src/lib/auth-client.ts +1 -1
  55. package/templates/webapp/src/services/llm/LLMService.ts +88 -0
  56. package/templates/webapp/src/services/llm/LlmProviderService.ts +85 -0
  57. package/templates/webapp/src/services/observability/initFaro.ts +1 -1
  58. package/templates/webapp/terraform/schema/main.tf +4 -0
  59. package/templates/webapp/terraform/schema/outputs.tf +9 -0
  60. package/templates/webapp/terraform/schema/variables.tf +19 -0
  61. package/templates/webapp/terraform/schema/versions.tf +38 -0
  62. package/templates/webapp/.github/workflows/__APP_NAME__-terraform.yml +0 -28
@@ -0,0 +1,98 @@
1
+ name: Build & Release __APP_NAME__-terraform
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "main"
7
+ paths:
8
+ - "packages/__APP_NAME__/terraform/schema/**"
9
+ - ".github/workflows/__APP_NAME__-terraform-ryvn-release.yaml"
10
+ pull_request:
11
+ branches:
12
+ - "main"
13
+ paths:
14
+ - "packages/__APP_NAME__/terraform/schema/**"
15
+ - ".github/workflows/__APP_NAME__-terraform-ryvn-release.yaml"
16
+ workflow_dispatch:
17
+
18
+ env:
19
+ SERVICE_NAME: __APP_NAME__-terraform
20
+
21
+ jobs:
22
+ build-and-release:
23
+ name: Build and Release
24
+ runs-on: ubuntu-latest
25
+ permissions:
26
+ contents: write
27
+ id-token: write
28
+
29
+ steps:
30
+ - name: Checkout code
31
+ uses: actions/checkout@v4
32
+ with:
33
+ fetch-depth: 0
34
+
35
+ - name: Install Ryvn CLI
36
+ uses: ryvn-technologies/install-ryvn-cli@v1.0.0
37
+
38
+ - name: Generate Release Tag
39
+ id: generate-tag
40
+ env:
41
+ RYVN_CLIENT_ID: ${{ secrets.RYVN_CLIENT_ID }}
42
+ RYVN_CLIENT_SECRET: ${{ secrets.RYVN_CLIENT_SECRET }}
43
+ run: |
44
+ tag_info=$(ryvn generate-release-tag "$SERVICE_NAME" --prefix="${SERVICE_NAME}@" -o json --default-bump-minor)
45
+
46
+ version=$(echo "$tag_info" | jq -r '.version')
47
+ new_tag=$(echo "$tag_info" | jq -r '.tag')
48
+ channel=$(echo "$tag_info" | jq -r '.channel')
49
+ isPreview=$(echo "$tag_info" | jq -r '.isPreview')
50
+
51
+ echo "version=$version" >> $GITHUB_OUTPUT
52
+ echo "new_tag=$new_tag" >> $GITHUB_OUTPUT
53
+ echo "channel=$channel" >> $GITHUB_OUTPUT
54
+ echo "isPreview=$isPreview" >> $GITHUB_OUTPUT
55
+
56
+ - name: Create Ryvn Release
57
+ if: |
58
+ !contains(github.event.head_commit.message, '[skip-release]') &&
59
+ !contains(github.event.pull_request.title, '[skip-release]') &&
60
+ (steps.generate-tag.outputs.isPreview == 'true' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
61
+ env:
62
+ RYVN_CLIENT_ID: ${{ secrets.RYVN_CLIENT_ID }}
63
+ RYVN_CLIENT_SECRET: ${{ secrets.RYVN_CLIENT_SECRET }}
64
+ run: |
65
+ version="${{ steps.generate-tag.outputs.new_tag }}"
66
+ version="${version#"${SERVICE_NAME}@"}"
67
+ version="${version#@}"
68
+ channel="${{ steps.generate-tag.outputs.channel }}"
69
+
70
+ if [ -n "$channel" ] && [ "$channel" != "null" ]; then
71
+ ryvn create release "$SERVICE_NAME" "$version" --channel "$channel"
72
+ else
73
+ ryvn create release "$SERVICE_NAME" "$version"
74
+ fi
75
+
76
+ - name: Create GitHub Tag
77
+ if: |
78
+ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
79
+ !contains(github.event.head_commit.message, '[skip-release]') &&
80
+ !contains(github.event.pull_request.title, '[skip-release]')
81
+ run: |
82
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
83
+ git config --global user.name "github-actions[bot]"
84
+ git tag "${{ steps.generate-tag.outputs.new_tag }}"
85
+ git push origin "${{ steps.generate-tag.outputs.new_tag }}"
86
+
87
+ - name: Create GitHub Release
88
+ if: |
89
+ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
90
+ !contains(github.event.head_commit.message, '[skip-release]') &&
91
+ !contains(github.event.pull_request.title, '[skip-release]')
92
+ uses: softprops/action-gh-release@v1
93
+ with:
94
+ tag_name: ${{ steps.generate-tag.outputs.new_tag }}
95
+ name: ${{ steps.generate-tag.outputs.new_tag }}
96
+ generate_release_notes: true
97
+ draft: false
98
+ prerelease: false
@@ -9,6 +9,7 @@ Next.js 15 full-stack application scaffolded from the Mosaic webapp template via
9
9
  - `pnpm lint` — run ESLint
10
10
  - `pnpm test` — run Vitest tests
11
11
  - `pnpm docker:up` / `pnpm docker:down` — start/stop PostgreSQL
12
+ - `pnpm inngest:dev` — start local Inngest dev server when working on background jobs
12
13
  - `pnpm db:generate` — generate Drizzle migrations
13
14
  - `pnpm db:migrate` — apply migrations
14
15
  - `pnpm db:setup-and-migrate` — create DB + migrate
@@ -17,6 +18,8 @@ Next.js 15 full-stack application scaffolded from the Mosaic webapp template via
17
18
 
18
19
  **Package manager**: Always use `pnpm`, never `npm` or `yarn`.
19
20
 
21
+ Local development only requires Postgres by default. Run Inngest locally when a workflow needs it. Do not run a local LGTM/Langfuse stack unless you are specifically debugging telemetry; those are wired by the Ryvn environment for deploys. If local LLM calls are needed, `pnpm dev` loads shared provider keys from `~/.config/percepta/create.env` when it exists.
22
+
20
23
  ## Code Style
21
24
 
22
25
  - Double quotes for strings
@@ -44,14 +47,18 @@ src/ # Application source
44
47
  ├── services/
45
48
  │ ├── inngest/ # Background job definitions
46
49
  │ ├── langfuse/ # LLM observability
50
+ │ ├── llm/ # LLM provider selection and call helpers
47
51
  │ ├── logger/ # App logger setup (wraps @percepta/logger)
48
52
  │ └── observability/ # OpenTelemetry setup
49
53
  └── utils/ # Helpers (cn, pathEncryption, etc.)
50
54
 
51
55
  deploy/ # Infrastructure-as-code for Ryvn deployments
52
56
  └── ryvn/
53
- ├── __APP_NAME__.service.yaml # Ryvn Service definition
54
- └── environments/<env>/installations/__APP_NAME__.env.<env>.serviceinstallation.yaml
57
+ ├── __APP_NAME__.service.yaml # Ryvn web service
58
+ ├── __APP_NAME__-terraform.service.yaml # Ryvn schema service
59
+ └── environments/<env>/installations/
60
+ ├── __APP_NAME__.env.<env>.serviceinstallation.yaml
61
+ └── __APP_NAME__-terraform.env.<env>.serviceinstallation.yaml
55
62
  ```
56
63
 
57
64
  ## @percepta Packages
@@ -184,6 +191,7 @@ Detailed how-to guides for each major stack component. Read the relevant guide w
184
191
  | Guide | File | When to read |
185
192
  |-------|------|-------------|
186
193
  | Background Jobs (Inngest) | [agent-skills/inngest.md](agent-skills/inngest.md) | Adding async tasks, scheduled jobs, or agent workflows |
194
+ | LLM Calls | [agent-skills/llm.md](agent-skills/llm.md) | Adding backend model calls, streaming, or structured generation |
187
195
  | LLM Observability (Langfuse) | [agent-skills/langfuse.md](agent-skills/langfuse.md) | App uses LLMs and needs trace/eval monitoring |
188
196
  | Database (Drizzle) | [agent-skills/database.md](agent-skills/database.md) | Adding tables, writing migrations, querying data |
189
197
  | Deployment (Ryvn) | [agent-skills/ryvn.md](agent-skills/ryvn.md) | Ryvn overview and Percepta environment context |
@@ -220,19 +228,23 @@ Better Auth configured in `src/lib/auth/`. Email/password credentials enabled by
220
228
 
221
229
  Inngest for async task processing. Define functions in `src/services/inngest/`. Configure via `INNGEST_*` env vars.
222
230
 
231
+ ### LLM Calls
232
+
233
+ Use `LLMService` from `src/services/llm/LLMService.ts` for backend model calls. It chooses the configured provider, enables AI SDK telemetry, and attaches provider/model metadata for Langfuse.
234
+
223
235
  ### Database
224
236
 
225
237
  PostgreSQL with Drizzle ORM. Schema in `src/drizzle/schema/`, migrations in `src/drizzle/migrations/`. Connection managed by `src/services/DatabaseService.ts`.
226
238
 
227
239
  ### Observability
228
240
 
229
- OpenTelemetry initialized in `src/instrumentation.ts`. Langfuse for LLM tracking in `src/services/langfuse/`. Faro for frontend monitoring via `@percepta/next-utils/faro`.
241
+ OpenTelemetry initialized in `src/instrumentation.ts`. Server traces and metrics export to the configured OTEL collector; platform logs are collected from stdout. Langfuse for LLM tracking in `src/services/langfuse/` activates when `LANGFUSE_*` env vars are configured, but only AI SDK spans are forwarded to Langfuse by default. Faro for frontend monitoring via `@percepta/next-utils/faro`.
230
242
 
231
243
  ## Deployment
232
244
 
233
- To deploy this app to percepta-test, follow [agent-skills/deploy.md](agent-skills/deploy.md). The Ryvn service definition and percepta-test installation YAML are already scaffolded at `deploy/ryvn/` with all values filled in deploy is mostly "copy these two files into the infra repo, open a PR, set three secrets in the Ryvn UI."
245
+ To deploy this app to percepta-test, follow [agent-skills/deploy.md](agent-skills/deploy.md). The direct deploy helper treats percepta-test as an existing platform environment: it preflights the shared Postgres, Inngest, OTEL collector, and LGTM installations, then creates/updates this app's Ryvn services and installations.
234
246
 
235
- The release CI/CD workflow is already included at `.github/workflows/ryvn-release.yaml`.
247
+ The release CI/CD workflows are already included under `.github/workflows/`.
236
248
 
237
249
  For Ryvn CLI operations, use the `/use-ryvn` skill.
238
250
 
@@ -6,6 +6,8 @@ RUN npm install -g pnpm
6
6
 
7
7
  # Build stage:
8
8
  FROM base AS builder
9
+ WORKDIR /repo
10
+ ENV CI=true
9
11
 
10
12
  COPY . .
11
13
 
@@ -20,7 +22,7 @@ ENV BASE_PATH=${BASE_PATH}
20
22
 
21
23
  RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
22
24
 
23
- RUN NODE_ENV=production NODE_OPTIONS="--max-old-space-size=4096" pnpm build
25
+ RUN NODE_ENV=production NODE_OPTIONS="--max-old-space-size=4096" pnpm --filter ./packages/__APP_NAME__ build
24
26
 
25
27
  # Remove .npmrc for security (contains auth token):
26
28
  RUN rm -f .npmrc
@@ -43,21 +45,28 @@ ENV NEXT_TELEMETRY_DISABLED=1
43
45
  ENV BASE_PATH=${BASE_PATH}
44
46
 
45
47
  # Copy built app from builder stage
46
- COPY --from=builder /app/.next/standalone ./
47
- COPY --from=builder /app/.next/static ./.next/static
48
+ COPY --from=builder /repo/packages/__APP_NAME__/.next/standalone ./
49
+ COPY --from=builder /repo/packages/__APP_NAME__/.next/static ./packages/__APP_NAME__/.next/static
48
50
 
49
51
  # Copy scripts and source files needed for start.sh and runtime
50
- COPY --from=builder /app/scripts ./scripts
51
- COPY --from=builder /app/src ./src
52
- COPY --from=builder /app/node_modules ./node_modules
52
+ COPY --from=builder /repo/package.json ./package.json
53
+ COPY --from=builder /repo/pnpm-lock.yaml ./pnpm-lock.yaml
54
+ COPY --from=builder /repo/pnpm-workspace.yaml ./pnpm-workspace.yaml
55
+ COPY --from=builder /repo/node_modules ./node_modules
56
+ COPY --from=builder /repo/packages/__APP_NAME__/package.json ./packages/__APP_NAME__/package.json
57
+ COPY --from=builder /repo/packages/__APP_NAME__/tsconfig.json ./packages/__APP_NAME__/tsconfig.json
58
+ COPY --from=builder /repo/packages/__APP_NAME__/node_modules ./packages/__APP_NAME__/node_modules
59
+ COPY --from=builder /repo/packages/__APP_NAME__/scripts ./packages/__APP_NAME__/scripts
60
+ COPY --from=builder /repo/packages/__APP_NAME__/src ./packages/__APP_NAME__/src
53
61
 
54
62
 
55
63
  # Expose the port:
56
64
  EXPOSE 3000
57
65
 
58
66
  # Set correct permissions and user:
59
- RUN chown -R 1000:1000 /app && chmod +x /app/scripts/start.sh
67
+ RUN chown -R 1000:1000 /app && chmod +x /app/packages/__APP_NAME__/scripts/start.sh
60
68
  USER 1000
69
+ WORKDIR /app/packages/__APP_NAME__
61
70
 
62
71
  # Start the application:
63
72
  CMD ["./scripts/start.sh"]
@@ -9,7 +9,8 @@ A production-ready Next.js application with authentication, database, logging, b
9
9
  - **Database** with PostgreSQL, Drizzle ORM, and migrations
10
10
  - **Logging** with Pino and structured safe/unsafe data separation
11
11
  - **Background Jobs** with Inngest
12
- - **Observability** with OpenTelemetry and Langfuse integration
12
+ - **LLM Calls** with a provider-backed `LLMService`
13
+ - **Observability** with OpenTelemetry, LGTM-compatible traces/metrics/logs, and Langfuse integration
13
14
  - **Infrastructure** with Terraform modules for AWS (RDS, S3, IAM)
14
15
  - **Type Safety** with TypeScript and Zod schemas
15
16
 
@@ -31,7 +32,15 @@ pnpm db:setup-and-migrate
31
32
 
32
33
  Copy `.env.example` to `.env.local` and configure your environment variables.
33
34
 
34
- ### 4. Start Development Server
35
+ ### 4. Start Inngest When Using Background Jobs
36
+
37
+ ```bash
38
+ pnpm inngest:dev
39
+ ```
40
+
41
+ Run this in a separate terminal when the app has Inngest functions or you want the local Inngest dashboard.
42
+
43
+ ### 5. Start Development Server
35
44
 
36
45
  ```bash
37
46
  pnpm dev
@@ -39,6 +48,16 @@ pnpm dev
39
48
 
40
49
  Open [http://localhost:3000](http://localhost:3000) to see your app.
41
50
 
51
+ OpenTelemetry, Faro, and Langfuse are optional in local development. Leave their env vars empty unless you are actively debugging telemetry; production deploys wire server traces, metrics, logs, and shared Langfuse demo credentials into the target Ryvn environment. General HTTP and database spans go to the OTEL/LGTM pipeline; Langfuse receives only AI SDK spans by default.
52
+
53
+ If you need local LLM calls, set provider keys once in your shell profile or in `~/.config/percepta/create.env`:
54
+
55
+ ```bash
56
+ ANTHROPIC_API_KEY=sk-ant-...
57
+ ```
58
+
59
+ `pnpm dev` loads that shared file before starting Next.js, so you do not need to copy the same provider key into every generated app.
60
+
42
61
  ## Project Structure
43
62
 
44
63
  ```
@@ -53,6 +72,7 @@ src/
53
72
  ├── services/ # Business logic services
54
73
  │ ├── inngest/ # Background job definitions
55
74
  │ ├── langfuse/ # LLM observability
75
+ │ ├── llm/ # LLM provider selection and call helpers
56
76
  │ └── logger/ # Structured logging
57
77
  └── utils/ # Utility functions
58
78
  ```
@@ -67,6 +87,7 @@ src/
67
87
  | `pnpm lint` | Run ESLint |
68
88
  | `pnpm docker:up` | Start PostgreSQL container |
69
89
  | `pnpm docker:down` | Stop PostgreSQL container |
90
+ | `pnpm inngest:dev` | Start the local Inngest dev server for this app |
70
91
  | `pnpm db:generate` | Generate Drizzle migrations |
71
92
  | `pnpm db:migrate` | Run database migrations |
72
93
  | `pnpm db:setup` | Create database and user |
@@ -164,6 +185,47 @@ node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
164
185
  | `LANGFUSE_PUBLIC_KEY` | Langfuse public key |
165
186
  | `LANGFUSE_SECRET_KEY` | Langfuse secret key |
166
187
 
188
+ `deploy:percepta-test` sets the shared Langfuse URL and inherits the demo project keys from the `demos-commons` Ryvn variable group.
189
+
190
+ ### LLM Providers
191
+
192
+ | Variable | Description |
193
+ |----------|-------------|
194
+ | `ANTHROPIC_API_KEY` | Anthropic API key. Inherited from `demos-commons` for `percepta-test` deploys |
195
+ | `OPENAI_API_KEY` | OpenAI API key for local or non-demo deployments |
196
+ | `LLM_PROVIDER` | Optional provider override: `anthropic` or `openai` |
197
+ | `LLM_MODEL` | Optional model override |
198
+
199
+ Use `LLMService` for backend model calls:
200
+
201
+ ```typescript
202
+ import { LLMService } from "@/services/llm/LLMService";
203
+
204
+ const result = await LLMService.create().generateText({
205
+ telemetryFunctionId: "summarize-note",
206
+ system: "You summarize notes for internal users.",
207
+ prompt: "Summarize this note...",
208
+ });
209
+ ```
210
+
211
+ For local development, `pnpm dev` also loads `~/.config/percepta/create.env` when that file exists. Production deploys do not use that local file; they inherit provider keys from the target Ryvn environment.
212
+
213
+ ### OpenTelemetry / LGTM
214
+
215
+ | Variable | Description |
216
+ |----------|-------------|
217
+ | `OTEL_SERVICE_NAME` | Service name attached to traces and metrics |
218
+ | `OTEL_RESOURCE_ATTRIBUTES` | Extra resource labels such as deployment environment |
219
+ | `OTEL_EXPORTER_OTLP_PROTOCOL` | OTLP protocol, usually `http/protobuf` |
220
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | Base OTLP HTTP collector endpoint |
221
+ | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Optional trace-specific OTLP endpoint |
222
+ | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | Optional metric-specific OTLP endpoint |
223
+ | `OTEL_TRACES_EXPORTER` | Set to `otlp` to export server traces |
224
+ | `OTEL_METRICS_EXPORTER` | Set to `otlp` to export server metrics |
225
+ | `OTEL_METRIC_EXPORT_INTERVAL` | Metrics export interval in milliseconds |
226
+
227
+ `deploy:percepta-test` configures these for the existing percepta-test OTEL collector and LGTM stack. Application logs are written to stdout and collected by the platform collector.
228
+
167
229
  ## Local AWS Development
168
230
 
169
231
  This application uses the default AWS SDK credential provider chain:
@@ -1,91 +1,90 @@
1
1
  # Deploying to Percepta Test
2
2
 
3
- This guide deploys __APP_TITLE__ to `https://__APP_NAME__.percepta-test.aitco.dev` using Ryvn. Tell Claude "deploy this app to percepta-test" and it will follow the steps below.
3
+ This guide deploys __APP_TITLE__ to `https://__APP_NAME__.percepta-test.aitco.dev` using Ryvn. Tell Claude "deploy this app to percepta-test" and it should run the direct deploy helper below.
4
4
 
5
- ## What's already scaffolded
5
+ This is the existing-environment deploy motion: `percepta-test` already owns the shared platform services, and this app is wired into them. Fresh-environment platform bootstrap is separate and should use a Ryvn blueprint or environment-specific platform rollout before app deploys run.
6
6
 
7
- When this app was created with `@percepta/create`, the IaC files were generated at `deploy/ryvn/` with all values pre-filled:
7
+ ## What's Already Scaffolded
8
8
 
9
- - `deploy/ryvn/__APP_NAME__.service.yaml` — Ryvn Service definition
10
- - `deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml` — percepta-test installation
11
- - `.github/workflows/__APP_NAME__-ryvn-release.yaml` (at the repo root) release workflow that builds the Docker image and creates a Ryvn release on push to `main`. Path filter scoped to `packages/__APP_NAME__/` so unrelated changes don't trigger builds.
12
-
13
- Per-app values (URLs, k8s service names, database schema) are substituted at create-time. The Better Auth and encryption secrets are declared as Ryvn-managed secrets so their values are set after the infra PR merges, not committed to git. Shared platform values (Inngest and OTel endpoints) are baked in as literals because they're stable across percepta-test apps.
9
+ - `deploy/ryvn/__APP_NAME__.service.yaml` — Ryvn server service for the web app.
10
+ - `deploy/ryvn/__APP_NAME__-terraform.service.yaml` — Ryvn Terraform service that creates the app's Postgres schema.
11
+ - `deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml` — web installation.
12
+ - `deploy/ryvn/environments/percepta-test/installations/__APP_NAME__-terraform.env.percepta-test.serviceinstallation.yaml` — schema installation.
13
+ - `.github/workflows/__APP_NAME__-ryvn-release.yaml` builds the Docker image and creates the web Ryvn release.
14
+ - `.github/workflows/__APP_NAME__-terraform-ryvn-release.yaml` — creates the schema Terraform Ryvn release.
15
+ - `deploy/ryvn/percepta-test.secrets.env` — generated locally and ignored by git; patched into Ryvn by the deploy helper.
14
16
 
15
17
  See [`deploy/README.md`](../deploy/README.md) for the file-by-file breakdown.
16
18
 
17
19
  ## Prerequisites
18
20
 
19
- - The app repo is under the `Percepta-Core` GitHub org. If not yet, run `gh repo create Percepta-Core/__APP_NAME__ --private --source=. --push` from the monorepo root.
20
- - The Percepta-Core org has `RYVN_CLIENT_ID`, `RYVN_CLIENT_SECRET`, and `NPM_TOKEN` as org-level GitHub secrets (already in place).
21
- - The `percepta-internal-terraform` installation provides the shared PostgreSQL already deployed on percepta-test.
22
- - `git` and `gh` are installed and authenticated.
23
- - The infra repo (`Percepta-Core/infra`) is checked out locally or can be cloned by the deploy script.
21
+ - `git`, `gh`, and `ryvn` are installed and authenticated.
22
+ - The worktree is clean and committed. The helper pushes the current branch to `main` because GitHub Actions builds from GitHub.
23
+ - The Percepta-Core org has `RYVN_CLIENT_ID`, `RYVN_CLIENT_SECRET`, and `NPM_TOKEN` available as org-level GitHub secrets.
24
+ - These shared platform installations are already deployed and healthy in `percepta-test`: `percepta-internal-terraform`, `inngest-test`, `otel-collector`, `lgtm-stack-helm`, and `langfuse`.
25
+ - The `demos-commons` Ryvn variable group exists in `percepta-test` and provides `LANGFUSE_PUBLIC_KEY` plus sensitive `ANTHROPIC_API_KEY` and `LANGFUSE_SECRET_KEY` for shared demo LLM calls and Langfuse tracing.
24
26
 
25
27
  ## Deploy
26
28
 
27
- ### Step 1: Open the infra PR
28
-
29
- Run the generated deploy helper from this package directory:
29
+ From this package directory:
30
30
 
31
31
  ```bash
32
32
  pnpm deploy:percepta-test -- --yes
33
33
  ```
34
34
 
35
- This script:
35
+ The helper:
36
36
 
37
- - uses `INFRA_REPO` or `--infra <path>` when provided
38
- - otherwise uses a sibling `../infra` checkout, cloning `Percepta-Core/infra` there if needed
39
- - copies the Ryvn service and installation YAML into infra
40
- - appends a `postgresql_schema "__APP_NAME_SNAKE__"` resource to `terraform/percepta-internal/databases.tf`
41
- - opens one PR against `Percepta-Core/infra`
37
+ 1. Checks the existing platform installations and shared demo variable group in `percepta-test`.
38
+ 2. Creates `Percepta-Core/__REPO_NAME__` if needed.
39
+ 3. Pushes the current branch to `main`.
40
+ 4. Creates or replaces the Ryvn web and schema services.
41
+ 5. Runs the schema Terraform release workflow.
42
+ 6. Creates or replaces the schema installation and approves the Terraform plan.
43
+ 7. Runs the web release workflow.
44
+ 8. Creates or replaces the web installation.
45
+ 9. Patches `BETTER_AUTH_SECRET` and `ENCRYPTION_SECRET_KEY` from `deploy/ryvn/percepta-test.secrets.env`.
46
+ 10. Waits for Ryvn health and checks `/api/healthz`, `/api/readyz`, and the protected app route.
42
47
 
43
- Get the PR reviewed and merged.
44
-
45
- ### Step 2: Set Ryvn secrets
48
+ The app will be available at **https://__APP_NAME__.percepta-test.aitco.dev**.
46
49
 
47
- After the infra PR merges, set the base Ryvn secrets for this installation:
50
+ ## Useful Variants
48
51
 
49
52
  ```bash
50
- BETTER_AUTH_SECRET=$(openssl rand -base64 32)
51
- ENCRYPTION_SECRET_KEY=$(openssl rand -hex 16)
53
+ pnpm deploy:percepta-test -- --skip-workflows --yes
54
+ pnpm deploy:percepta-test -- --skip-push --yes
55
+ pnpm deploy:percepta-test -- --timeout-minutes 30 --yes
52
56
  ```
53
57
 
54
- Also set any app-specific secrets the implementation added, such as `OPENAI_API_KEY`.
58
+ Use `--skip-workflows` when the required Ryvn releases already exist. Use `--skip-push` only when the target ref is already pushed.
55
59
 
56
- ### Step 3: Trigger the first deploy
60
+ The legacy infra-PR path is still available:
57
61
 
58
- Push to `main` in the app repo (or `gh workflow run "Build & Release __APP_NAME__"`). The release workflow builds the Docker image and creates a Ryvn release; Ryvn deploys it to percepta-test.
59
-
60
- The workflow lives at `.github/workflows/__APP_NAME__-ryvn-release.yaml` (at the repo root, where GitHub Actions picks it up). It only fires on changes under `packages/__APP_NAME__/`, so unrelated edits to other packages in the monorepo won't trigger it.
62
+ ```bash
63
+ pnpm deploy:percepta-test:pr -- --phase service --yes
64
+ pnpm deploy:percepta-test:pr -- --phase installation --yes
65
+ ```
61
66
 
62
- ### Step 4: Verify
67
+ ## Verify
63
68
 
64
69
  ```bash
65
70
  ryvn get installation __APP_NAME__ -e percepta-test
66
71
  ryvn logs __APP_NAME__ -e percepta-test
67
72
  curl -s https://__APP_NAME__.percepta-test.aitco.dev/api/healthz
68
73
  curl -s https://__APP_NAME__.percepta-test.aitco.dev/api/readyz
74
+ curl -I https://__APP_NAME__.percepta-test.aitco.dev/
69
75
  ```
70
76
 
71
- The app will be available at **https://__APP_NAME__.percepta-test.aitco.dev**.
72
-
73
77
  ## Troubleshooting
74
78
 
75
- - **Pod crash-looping** → check `ryvn logs`; missing Ryvn secrets from Step 2 are the most common cause after a fresh deploy.
76
- - **Database connection refused** → verify `DATABASE_USE_SSL=true` and that `percepta-internal-terraform` is deployed.
77
- - **Database schema missing** → verify the infra PR added `postgresql_schema "__APP_NAME_SNAKE__"` and Ryvn applied `percepta-internal-terraform`.
78
- - **Inngest can't reach the app** → `INNGEST_APP_URL` must use the k8s service name `__APP_NAME__-web-server`. The scaffolded YAML gets this right; if you renamed anything, double-check.
79
- - **Image build fails fetching @percepta packages** → check the `NPM_TOKEN` GitHub org secret. The workflow passes it as a build arg.
79
+ - **Image build fails fetching @percepta packages** → check the Percepta-Core org-level `NPM_TOKEN` secret. Do not add a repo-level token unless the org secret is unavailable.
80
+ - **Ryvn release already exists** → commit a new change or re-run with `--skip-workflows` if the current releases are already present.
81
+ - **Terraform plan needs approval** → the helper approves it when run with `--yes`; without `--yes`, approve the prompt.
82
+ - **Auth/sign-in routes fail after install** → verify the deploy helper patched `BETTER_AUTH_SECRET` and `ENCRYPTION_SECRET_KEY`.
83
+ - **Pod crash-looping** → check `ryvn logs`; migration or database connectivity failures are the most common fresh-deploy causes.
84
+ - **Database schema missing** → check `ryvn get installation __APP_NAME__-terraform -e percepta-test`.
85
+ - **Inngest can't reach the app** → `INNGEST_APP_URL` must use the k8s service name `__APP_NAME__-web-server`.
86
+ - **Platform preflight fails** → deploy or repair the missing shared installation first. This helper only wires apps into an existing environment.
87
+ - **No Langfuse traces** → verify the target environment has Langfuse deployed and that the `demos-commons` variable group has `LANGFUSE_PUBLIC_KEY` and sensitive `LANGFUSE_SECRET_KEY`.
88
+ - **LLM calls fail after deploy** → verify `demos-commons` has sensitive `ANTHROPIC_API_KEY` and the installation has `LLM_PROVIDER=anthropic`.
80
89
 
81
90
  For Ryvn CLI operations, use the `/use-ryvn` skill.
82
-
83
- ## Updating shared platform values
84
-
85
- The Inngest and OTel URLs are baked as literals in every webapp's installation YAML. If percepta-test infra ever changes those endpoints, update them with a single grep across the infra repo:
86
-
87
- ```bash
88
- grep -rl "inngest.percepta-test.svc.cluster.local:8288" ryvn/environments/percepta-test/installations/
89
- ```
90
-
91
- A future improvement would be a shared ConfigMap or Ryvn output reference; for now the scaffolded literals are the convention.
@@ -102,13 +102,13 @@ await inngest.client.send({
102
102
 
103
103
  ## Running Inngest Locally
104
104
 
105
- ### 1. Install the Inngest Dev Server
105
+ ### 1. Start the Inngest Dev Server
106
106
 
107
107
  ```bash
108
- pnpm dlx inngest-cli@latest dev
108
+ pnpm inngest:dev
109
109
  ```
110
110
 
111
- This starts the Inngest Dev Server at `http://localhost:8288` with a dashboard UI.
111
+ This starts the Inngest Dev Server for `http://localhost:3000/api/inngest` with a dashboard UI.
112
112
 
113
113
  ### 2. Set environment variables
114
114
 
@@ -126,7 +126,7 @@ INNGEST_EVENT_KEY=local # any value works locally
126
126
  pnpm dev
127
127
  ```
128
128
 
129
- The Inngest Dev Server auto-discovers functions by calling the serve endpoint at `/api/inngest`. Open `http://localhost:8288` to see registered functions, trigger events manually, and inspect runs.
129
+ The Inngest Dev Server auto-discovers functions by calling the serve endpoint at `/api/inngest`. Open the dashboard URL printed by `pnpm inngest:dev` to see registered functions, trigger events manually, and inspect runs.
130
130
 
131
131
  ## Environment Variables
132
132
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Langfuse is an open-source LLM observability platform. It captures traces, spans, and generations from LLM calls so you can debug prompt behavior, monitor latency/cost, and run evaluations. Think of it as "Datadog for LLM apps."
4
4
 
5
- **Our opinionated setup:** We integrate Langfuse via OpenTelemetry (OTEL), not the Langfuse SDK directly. OTEL auto-instruments the app (HTTP, DB, etc.), and Langfuse acts as a span processor that receives LLM-specific traces. This gives us unified tracing both traditional backend spans and LLM generations in the same trace.
5
+ **Our opinionated setup:** We integrate Langfuse via OpenTelemetry (OTEL), not the Langfuse SDK directly. OTEL auto-instruments the app (HTTP, DB, etc.), exports general traces to the environment's OTEL collector, and adds Langfuse as a span processor when `LANGFUSE_*` values are configured. This gives us unified tracing while still letting the app run when Langfuse is not available in an environment.
6
6
 
7
7
  ## When to Use Langfuse
8
8
 
@@ -18,24 +18,28 @@ Langfuse is an open-source LLM observability platform. It captures traces, spans
18
18
 
19
19
  ### OpenTelemetry + Langfuse Span Processor
20
20
 
21
- The template uses Next.js's instrumentation hook (called on server startup) to bootstrap OTEL with Langfuse:
21
+ The template uses Next.js's instrumentation hook (called on server startup) to bootstrap OTEL with both the environment collector and optional Langfuse:
22
22
 
23
23
  ```typescript
24
24
  import { LangfuseSpanProcessor } from "@langfuse/otel";
25
- import { NodeSDK } from "@opentelemetry/sdk-node";
25
+ import { NodeSDK, tracing } from "@opentelemetry/sdk-node";
26
26
  import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
27
27
 
28
28
  const sdk = new NodeSDK({
29
- spanProcessors: [new LangfuseSpanProcessor({ baseUrl, publicKey, secretKey })],
29
+ spanProcessors: [
30
+ new tracing.BatchSpanProcessor(otlpTraceExporter),
31
+ new LangfuseSpanProcessor({ baseUrl, publicKey, secretKey }),
32
+ ],
30
33
  instrumentations: [getNodeAutoInstrumentations()],
31
34
  });
32
35
  sdk.start();
33
36
  ```
34
37
 
35
38
  - `getNodeAutoInstrumentations()` automatically instruments HTTP calls, database queries, and other standard Node.js operations.
36
- - `LangfuseSpanProcessor` forwards spans to Langfuse. LLM spans (from the Vercel AI SDK or OpenAI SDK) are recognized and displayed as generations in the Langfuse UI.
39
+ - The OTLP span processor forwards general server traces to the environment collector.
40
+ - `LangfuseSpanProcessor` forwards only AI SDK spans to Langfuse when all `LANGFUSE_*` variables are set. General HTTP, DB, and server spans stay in the OTEL/LGTM pipeline and are not sent to Langfuse by default.
37
41
 
38
- **You do not need to manually instrument LLM calls.** If you use the Vercel AI SDK (`ai` package) or the OpenAI SDK, OTEL picks them up automatically.
42
+ **You do not need to manually instrument LLM calls.** Use `LLMService` from `src/services/llm/LLMService.ts`; it calls the Vercel AI SDK with telemetry enabled and provider/model metadata attached.
39
43
 
40
44
  ### LangfuseService (Direct API)
41
45
 
@@ -58,12 +62,9 @@ This is a singleton — `create()` returns the same instance every time. It grac
58
62
 
59
63
  ### Percepta's Internal Instance
60
64
 
61
- Percepta runs a shared Langfuse instance. To get keys:
65
+ Percepta runs a shared Langfuse instance. For `percepta-test` deploys, the generated Ryvn installation uses the shared demo project by inheriting `LANGFUSE_PUBLIC_KEY` and sensitive `LANGFUSE_SECRET_KEY` from the `demos-commons` variable group.
62
66
 
63
- 1. Ask your team lead for access to the Percepta Langfuse instance
64
- 2. Log into the Langfuse dashboard
65
- 3. Go to **Settings → API Keys** and create a new key pair
66
- 4. You'll get a **public key** and **secret key**
67
+ For non-demo environments, verify the target Ryvn environment has a Langfuse installation or shared Langfuse project, then store the project keys in an environment-scoped variable group or installation secrets.
67
68
 
68
69
  ### Self-Hosted / Langfuse Cloud
69
70
 
@@ -75,10 +76,10 @@ For external projects, you can:
75
76
 
76
77
  ### Option 1: Use Percepta's Langfuse (recommended)
77
78
 
78
- Set the keys in `.env.local` pointing to the shared instance:
79
+ Set the keys in `.env.local` if you want local traces to go to the shared instance:
79
80
 
80
81
  ```bash
81
- LANGFUSE_BASE_URL=https://langfuse.internal.percepta.ai # ask team for actual URL
82
+ LANGFUSE_BASE_URL=https://langfuse.percepta-test.aitco.dev
82
83
  LANGFUSE_PUBLIC_KEY=pk-lf-...
83
84
  LANGFUSE_SECRET_KEY=sk-lf-...
84
85
  ```
@@ -104,7 +105,7 @@ LANGFUSE_SECRET_KEY=sk-lf-...
104
105
 
105
106
  ### Option 3: Skip Langfuse entirely
106
107
 
107
- Simply don't set the `LANGFUSE_*` env vars. The instrumentation gracefully skips the Langfuse span processor, and `LangfuseService.isConfigured()` returns `false`. OTEL still instruments the app for general tracing.
108
+ Simply don't set the `LANGFUSE_*` env vars. This is the default local development path unless you are specifically debugging LLM telemetry. The instrumentation gracefully skips the Langfuse span processor, and `LangfuseService.isConfigured()` returns `false`. OTEL still instruments the app for general tracing when an OTLP collector endpoint is configured.
108
109
 
109
110
  ## Environment Variables
110
111