@codepassion/skills 1.2.0 → 1.4.0
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/README.md +6 -1
- package/SKILLS.md +6 -1
- package/bin/c9n-skills.js +28 -3
- package/install.sh +25 -2
- package/package.json +1 -1
- package/skills/c9n/SKILL.md +279 -0
- package/skills/c9n-pipeline/SKILL.md +128 -0
- package/skills/c9n-pipeline/templates/Dockerfile.api +49 -0
- package/skills/c9n-pipeline/templates/Dockerfile.service +45 -0
- package/skills/c9n-pipeline/templates/Dockerfile.web +50 -0
- package/skills/c9n-pipeline/templates/dockerignore +18 -0
- package/skills/c9n-pipeline/templates/workflows/_build-job.partial.yml +13 -0
- package/skills/c9n-pipeline/templates/workflows/_deploy-job.partial.yml +17 -0
- package/skills/c9n-pipeline/templates/workflows/_migration-check.partial.yml +16 -0
- package/skills/c9n-pipeline/templates/workflows/ci.yml +38 -0
- package/skills/c9n-pipeline/templates/workflows/deployment.yml +21 -0
- package/skills/c9n-sentry/REFERENCE.md +172 -0
- package/skills/c9n-sentry/SKILL.md +53 -0
- package/skills/c9n-spec/SKILL.md +92 -0
- package/skills/c9n-spec/templates/.github/pull_request_template.md +29 -0
- package/skills/c9n-spec/templates/AGENTS.md +179 -0
- package/skills/c9n-spec/templates/CLAUDE.md +6 -0
- package/skills/c9n-spec/templates/CONTEXT.md +53 -0
- package/skills/c9n-spec/templates/DESIGN.md +73 -0
- package/skills/c9n-spec/templates/docs/SRS.md +37 -0
- package/skills/c9n-spec/templates/docs/adr/0001-spec-driven-iso29110-docs.md +44 -0
- package/skills/c9n-spec/templates/docs/adr/_TEMPLATE.md +26 -0
- package/skills/c9n-spec/templates/docs/agents/domain.md +43 -0
- package/skills/c9n-spec/templates/docs/agents/issue-tracker.md +22 -0
- package/skills/c9n-spec/templates/docs/agents/triage-labels.md +27 -0
- package/skills/c9n-spec/templates/docs/architecture.md +50 -0
- package/skills/c9n-spec/templates/docs/features/_TEMPLATE.md +34 -0
- package/skills/c9n-spec/templates/docs/test-plan.md +30 -0
- package/skills/c9n-spec/templates/docs/traceability.md +27 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# {{PACKAGE_NAME}} (Next.js app) — Bun multi-stage build.
|
|
3
|
+
# Public env is read at RUNTIME via next-runtime-env (<PublicEnvScript>), so this
|
|
4
|
+
# image bakes no NEXT_PUBLIC_* — Coolify injects them at run, like the API image.
|
|
5
|
+
# Generated by c9n-pipeline; see the ADR this skill points you to.
|
|
6
|
+
# -----------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
FROM oven/bun:{{BUN_VERSION}} AS base
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
# Install dependencies with bun
|
|
12
|
+
FROM base AS deps
|
|
13
|
+
COPY package.json bun.lock ./
|
|
14
|
+
{{WORKSPACE_MANIFEST_COPIES}}
|
|
15
|
+
COPY packages/ ./packages/
|
|
16
|
+
RUN bun install
|
|
17
|
+
|
|
18
|
+
# Rebuild the source code only when needed
|
|
19
|
+
FROM base AS builder
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
22
|
+
COPY . .
|
|
23
|
+
|
|
24
|
+
# Next.js expects {{APP_DIR}}/.env to exist; intentionally empty — no public env
|
|
25
|
+
# is baked at build (it is injected at runtime via next-runtime-env).
|
|
26
|
+
RUN touch {{APP_DIR}}/.env
|
|
27
|
+
|
|
28
|
+
RUN cd {{APP_DIR}} && bun run build
|
|
29
|
+
|
|
30
|
+
# Production image — serve the Next.js standalone output
|
|
31
|
+
FROM base AS runner
|
|
32
|
+
WORKDIR /app
|
|
33
|
+
|
|
34
|
+
ENV NODE_ENV=production \
|
|
35
|
+
PORT={{APP_PORT}} \
|
|
36
|
+
HOSTNAME="0.0.0.0"
|
|
37
|
+
|
|
38
|
+
RUN groupadd --system --gid 1001 nodejs && \
|
|
39
|
+
useradd --system --uid 1001 nextjs
|
|
40
|
+
|
|
41
|
+
# Leverage Next.js output file tracing to keep the image small
|
|
42
|
+
COPY --from=builder --chown=nextjs:nodejs /app/{{APP_DIR}}/.next/standalone ./
|
|
43
|
+
COPY --from=builder --chown=nextjs:nodejs /app/{{APP_DIR}}/.next/static ./{{APP_DIR}}/.next/static
|
|
44
|
+
COPY --from=builder --chown=nextjs:nodejs /app/{{APP_DIR}}/public ./{{APP_DIR}}/public
|
|
45
|
+
|
|
46
|
+
USER nextjs
|
|
47
|
+
|
|
48
|
+
EXPOSE {{APP_PORT}}
|
|
49
|
+
|
|
50
|
+
CMD ["bun", "{{APP_DIR}}/server.js"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# One BUILD job per app. Alpha only — the image is built once at the tag push
|
|
2
|
+
# and redeployed to Beta/Production by base tag (no rebuild). {{APP_KEY}} is a
|
|
3
|
+
# lowercase id unique per app (e.g. api, web). {{ORG_REF}} is the org workflow
|
|
4
|
+
# ref (e.g. turborepo) — keep it identical across every job in this file.
|
|
5
|
+
build-{{APP_KEY}}-alpha:
|
|
6
|
+
name: Build
|
|
7
|
+
if: github.event_name != 'release' && startsWith(github.ref, 'refs/tags/v')
|
|
8
|
+
uses: codepassion-team/cicd/.github/workflows/build.yml@{{ORG_REF}}
|
|
9
|
+
with:
|
|
10
|
+
environment: Alpha
|
|
11
|
+
build_path: ./{{APP_DIR}}
|
|
12
|
+
package_name: {{PACKAGE_NAME}}
|
|
13
|
+
secrets: inherit
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# One DEPLOY job per app per environment. {{DEPLOY_NAME}} is the org deploy.yml
|
|
2
|
+
# name input that resolves the Coolify UUID var: it is uppercased to
|
|
3
|
+
# ${{DEPLOY_NAME_UPPER}}_UUID, so it MUST be a key the org deploy.yml wires
|
|
4
|
+
# (currently only API and WEB). Stamp the right {{IF_CONDITION}} per environment
|
|
5
|
+
# from c9n-semver-deployment:
|
|
6
|
+
# Alpha -> needs: build-{{APP_KEY}}-alpha ; if: github.event_name != 'release' && startsWith(github.ref, 'refs/tags/v')
|
|
7
|
+
# Beta -> (no needs) ; if: github.event_name == 'release' && github.event.release.prerelease == true && startsWith(github.event.release.tag_name, 'v')
|
|
8
|
+
# Production -> (no needs) ; if: github.event_name == 'release' && github.event.release.prerelease == false && startsWith(github.event.release.tag_name, 'v')
|
|
9
|
+
deploy-{{APP_KEY}}-{{ENV_KEY}}:
|
|
10
|
+
name: Deploy
|
|
11
|
+
if: {{IF_CONDITION}}
|
|
12
|
+
{{NEEDS_LINE}}
|
|
13
|
+
uses: codepassion-team/cicd/.github/workflows/deploy.yml@{{ORG_REF}}
|
|
14
|
+
with:
|
|
15
|
+
name: {{DEPLOY_NAME}}
|
|
16
|
+
environment: {{ENVIRONMENT}}
|
|
17
|
+
secrets: inherit
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Inject in place of {{MIGRATION_CHECK}} in ci.yml ONLY when a DB is detected
|
|
2
|
+
# (a migration-generate script exists in some package.json AND a migrations dir
|
|
3
|
+
# exists). Substitute {{MIGRATION_SCRIPT}} and {{MIGRATIONS_DIR}} with the real
|
|
4
|
+
# detected names — do not assume drizzle:generate / packages/supabase/migrations.
|
|
5
|
+
# If no DB, {{MIGRATION_CHECK}} is the empty string and ci.yml ends at Build.
|
|
6
|
+
- name: Verify migrations are up to date
|
|
7
|
+
run: bun run {{MIGRATION_SCRIPT}} -- --name check
|
|
8
|
+
- name: Check for uncommitted migration changes
|
|
9
|
+
run: |
|
|
10
|
+
if git diff --name-only --exit-code {{MIGRATIONS_DIR}}; then
|
|
11
|
+
echo "Migrations are up to date"
|
|
12
|
+
else
|
|
13
|
+
echo "Detected uncommitted migration changes. Run 'bun run {{MIGRATION_SCRIPT}}' locally and commit the result."
|
|
14
|
+
git diff {{MIGRATIONS_DIR}}
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: ['main', 'develop']
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, synchronize]
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Check
|
|
10
|
+
timeout-minutes: 15
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
env:
|
|
13
|
+
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
14
|
+
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
15
|
+
steps:
|
|
16
|
+
- name: Check out code
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 2
|
|
20
|
+
- name: Cache turbo build setup
|
|
21
|
+
uses: actions/cache@v4
|
|
22
|
+
with:
|
|
23
|
+
path: .turbo
|
|
24
|
+
key: ${{ runner.os }}-turbo-${{ github.sha }}
|
|
25
|
+
restore-keys: |
|
|
26
|
+
${{ runner.os }}-turbo-
|
|
27
|
+
- uses: oven-sh/setup-bun@v2
|
|
28
|
+
- name: Setup Node.js environment
|
|
29
|
+
uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: 24
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: bun install
|
|
34
|
+
- name: Check types
|
|
35
|
+
run: bun run check-types
|
|
36
|
+
- name: Build
|
|
37
|
+
run: bun run build
|
|
38
|
+
{{MIGRATION_CHECK}}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Deployment
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- 'v*.*.*'
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
# Assembled by c9n-pipeline from the partials below. The triggers and the
|
|
12
|
+
# event->environment mapping follow c9n-semver-deployment (single source of
|
|
13
|
+
# truth): tag push -> Alpha, prerelease -> Beta, release -> Production.
|
|
14
|
+
#
|
|
15
|
+
# Stamp one BUILD job per app (Alpha only — built once, redeployed by base tag),
|
|
16
|
+
# then one DEPLOY job per app per environment the project actually has. Prune
|
|
17
|
+
# environments the project does not use.
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
{{BUILD_JOBS}}
|
|
21
|
+
{{DEPLOY_JOBS}}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Sentry Bun+Elysia+Next.js — Reference
|
|
2
|
+
|
|
3
|
+
## package.json (`packages/sentry/`)
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"name": "@repo/sentry",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
"./bun": { "import": "./src/bun.ts" },
|
|
11
|
+
"./client": { "import": "./src/client.ts" },
|
|
12
|
+
"./server": { "import": "./src/server.ts" },
|
|
13
|
+
"./edge": { "import": "./src/edge.ts" },
|
|
14
|
+
"./pdpa": { "import": "./src/pdpa.ts" }
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@sentry/elysia": "^10",
|
|
18
|
+
"@sentry/nextjs": "^10"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"elysia": "*",
|
|
22
|
+
"next-runtime-env": "*"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## PDPA scrubber
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// src/pdpa.ts
|
|
31
|
+
import type { Event } from "@sentry/core"
|
|
32
|
+
|
|
33
|
+
const PII_KEY = /phone|otp|lat|lng|latitude|longitude|member_?id|token|secret|password/i
|
|
34
|
+
const DROP_HEADERS = ["authorization", "cookie", "set-cookie"]
|
|
35
|
+
|
|
36
|
+
function redactKeys(obj: unknown): unknown {
|
|
37
|
+
if (!obj || typeof obj !== "object") return obj
|
|
38
|
+
if (Array.isArray(obj)) return obj.map(redactKeys)
|
|
39
|
+
return Object.fromEntries(
|
|
40
|
+
Object.entries(obj as Record<string, unknown>).map(([k, v]) => [
|
|
41
|
+
k,
|
|
42
|
+
PII_KEY.test(k) ? "[redacted]" : redactKeys(v)
|
|
43
|
+
])
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function pdpaBeforeSend(event: Event): Event | null {
|
|
48
|
+
if (event.request) {
|
|
49
|
+
event.request.data = undefined
|
|
50
|
+
event.request.query_string = undefined
|
|
51
|
+
event.request.cookies = undefined
|
|
52
|
+
if (event.request.headers) {
|
|
53
|
+
DROP_HEADERS.forEach(h => { delete (event.request!.headers as Record<string, string>)[h] })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (event.extra) event.extra = redactKeys(event.extra) as Record<string, unknown>
|
|
57
|
+
return event
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## src/bun.ts
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
export * from "@sentry/elysia"
|
|
65
|
+
import * as Sentry from "@sentry/elysia"
|
|
66
|
+
import { pdpaBeforeSend } from "./pdpa.js"
|
|
67
|
+
|
|
68
|
+
export function initBun(): void {
|
|
69
|
+
const dsn = process.env.SENTRY_DSN
|
|
70
|
+
if (!dsn) return
|
|
71
|
+
Sentry.init({
|
|
72
|
+
dsn,
|
|
73
|
+
environment: process.env.ENVIRONMENT || "development",
|
|
74
|
+
tracesSampleRate: 0.1,
|
|
75
|
+
sendDefaultPii: false,
|
|
76
|
+
beforeSend: pdpaBeforeSend,
|
|
77
|
+
initialScope: { tags: { app: "api" } }
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## src/client.ts (runtime env — ADR 0005)
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
export * from "@sentry/nextjs"
|
|
86
|
+
import * as Sentry from "@sentry/nextjs"
|
|
87
|
+
import { env } from "next-runtime-env" // ← NOT process.env
|
|
88
|
+
import { pdpaBeforeSend } from "./pdpa.js"
|
|
89
|
+
|
|
90
|
+
export function initClient(): void {
|
|
91
|
+
const dsn = env("NEXT_PUBLIC_SENTRY_DSN") // runtime, not build-time
|
|
92
|
+
if (!dsn) return
|
|
93
|
+
Sentry.init({
|
|
94
|
+
dsn,
|
|
95
|
+
environment: env("NEXT_PUBLIC_ENVIRONMENT") || "development",
|
|
96
|
+
tracesSampleRate: 0.1,
|
|
97
|
+
sendDefaultPii: false,
|
|
98
|
+
beforeSend: pdpaBeforeSend,
|
|
99
|
+
tags: { app: "web" }
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## apps/api/src/index.ts wiring
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import "./lib/sentry.js" // ← must be FIRST import
|
|
108
|
+
// ... other imports ...
|
|
109
|
+
import { withElysia } from "@repo/sentry/bun"
|
|
110
|
+
|
|
111
|
+
const app = withElysia(new Elysia()) // wraps for auto error capture
|
|
112
|
+
.use(...)
|
|
113
|
+
// remove any console.error in onError — withElysia handles it
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## apps/web/next.config.js
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
import { withSentryConfig } from "@repo/sentry/client"
|
|
120
|
+
const nextConfig = { transpilePackages: ["@repo/sentry"], ... }
|
|
121
|
+
export default withSentryConfig(nextConfig, {
|
|
122
|
+
silent: true,
|
|
123
|
+
hideSourceMaps: true,
|
|
124
|
+
tunnelRoute: "/monitoring" // guards against in-app-browser/CSP drops
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## apps/web/instrumentation.ts
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
export async function register() {
|
|
132
|
+
if (process.env.NEXT_RUNTIME === "nodejs") {
|
|
133
|
+
await import("./sentry.server.config")
|
|
134
|
+
}
|
|
135
|
+
if (process.env.NEXT_RUNTIME === "edge") {
|
|
136
|
+
await import("./sentry.edge.config")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export { captureRequestError as onRequestError } from "@sentry/nextjs"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## apps/web/instrumentation-client.ts
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { initClient, captureRouterTransitionStart } from "@repo/sentry/client"
|
|
146
|
+
initClient()
|
|
147
|
+
export const onRouterTransitionStart = captureRouterTransitionStart
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## turbo.json globalEnv additions
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
"globalEnv": ["SENTRY_DSN", "ENVIRONMENT"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Do NOT add `NEXT_PUBLIC_*` to globalEnv — they are runtime-injected via `PublicEnvScript`.
|
|
157
|
+
|
|
158
|
+
## Sentry project setup
|
|
159
|
+
|
|
160
|
+
1. sentry.io → **Create Project** → platform: **Next.js** → name: `<project>`
|
|
161
|
+
2. Copy DSN from **Project Settings → Client Keys (DSN)**
|
|
162
|
+
3. Same DSN in both Coolify services (one project, two env var names)
|
|
163
|
+
4. Redeploy both services after setting vars
|
|
164
|
+
|
|
165
|
+
## ADR checklist (write ADR when adopting)
|
|
166
|
+
|
|
167
|
+
- Why `@sentry/elysia` not `@sentry/bun` + custom plugin
|
|
168
|
+
- Why `@sentry/elysia` not `@sentry/node` (compiled binary landmine)
|
|
169
|
+
- Client env split reasoning (ADR 0005 or equivalent)
|
|
170
|
+
- PDPA/privacy scrubbing rationale
|
|
171
|
+
- Alpha-state risk acceptance if applicable
|
|
172
|
+
- Generic env naming rationale
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: c9n-sentry
|
|
3
|
+
description: Scaffold Sentry error tracking in a Bun + ElysiaJS + Next.js monorepo via a shared @repo/sentry package. Use when user wants to add Sentry, set up error tracking, or wire error monitoring to an ElysiaJS API and/or Next.js frontend in a Turborepo monorepo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sentry — Bun + Elysia + Next.js
|
|
7
|
+
|
|
8
|
+
## Three landmines (check first)
|
|
9
|
+
|
|
10
|
+
1. **`@sentry/node` under `bun --compile` fails silently** — valid DSN, zero events. Use `@sentry/elysia` (built on `@sentry/bun`). Never let `@sentry/node` load on the API.
|
|
11
|
+
2. **Runtime public env** — if the project uses `next-runtime-env` (ADR 0005 pattern), the browser DSN MUST use `env("NEXT_PUBLIC_SENTRY_DSN")` not `process.env`. Server/edge/api use `process.env`. Wrong = undefined DSN in prod = dead Sentry.
|
|
12
|
+
3. **PDPA / PII** — add `beforeSend` scrubber + `sendDefaultPii: false`. Strip request body/query/cookies, drop auth headers, recursively redact PII keys. See [REFERENCE.md](REFERENCE.md#pdpa-scrubber).
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
packages/sentry/ ← @repo/sentry (shared)
|
|
18
|
+
src/pdpa.ts ← beforeSend scrubber (shared by all inits)
|
|
19
|
+
src/bun.ts ← initBun() for apps/api
|
|
20
|
+
src/client.ts ← initClient() for browser (uses env() not process.env)
|
|
21
|
+
src/server.ts ← initServer() for Next.js server runtime
|
|
22
|
+
src/edge.ts ← initEdge() for Next.js edge runtime
|
|
23
|
+
package.json ← exports map: ./bun ./client ./server ./edge ./pdpa
|
|
24
|
+
apps/api/src/lib/sentry.ts ← import { initBun } from "@repo/sentry/bun"; initBun()
|
|
25
|
+
apps/api/src/index.ts ← first import: "./lib/sentry.js", then withElysia(new Elysia())
|
|
26
|
+
apps/web/instrumentation.ts ← register() → dynamic import server/edge config
|
|
27
|
+
apps/web/instrumentation-client.ts ← initClient()
|
|
28
|
+
apps/web/sentry.server.config.ts / sentry.edge.config.ts
|
|
29
|
+
apps/web/next.config.js ← withSentryConfig + transpilePackages: ["@repo/sentry"]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Env vars
|
|
33
|
+
|
|
34
|
+
| Service | Key | Read by |
|
|
35
|
+
|---------|-----|---------|
|
|
36
|
+
| API (Coolify) | `SENTRY_DSN`, `ENVIRONMENT` | `process.env` |
|
|
37
|
+
| Web (Coolify) | `NEXT_PUBLIC_SENTRY_DSN`, `NEXT_PUBLIC_ENVIRONMENT` | `env()` from next-runtime-env (client) / `process.env` (server/edge) |
|
|
38
|
+
|
|
39
|
+
One Sentry project, same DSN in both vars. Add `initialScope: { tags: { app: "api" } }` to `initBun()` so api vs web events are distinguishable in the shared project.
|
|
40
|
+
|
|
41
|
+
## Verification checklist
|
|
42
|
+
|
|
43
|
+
- [ ] `bun install --frozen-lockfile` clean
|
|
44
|
+
- [ ] `bun run check-types` (api + web) clean
|
|
45
|
+
- [ ] `bun run build --compile` on API — confirms `@sentry/node` never loaded (the landmine test)
|
|
46
|
+
- [ ] Web Sentry bundle builds green
|
|
47
|
+
- [ ] After deploy: `window.__ENV.NEXT_PUBLIC_SENTRY_DSN` populated in devtools (runtime env proof)
|
|
48
|
+
- [ ] Trigger a test error → event appears in Sentry tagged correct environment
|
|
49
|
+
- [ ] Open event payload — no phone/otp/lat/lng/member_id (PDPA proof)
|
|
50
|
+
|
|
51
|
+
## Reference
|
|
52
|
+
|
|
53
|
+
See [REFERENCE.md](REFERENCE.md) for full file contents, PDPA scrubber code, and `turbo.json` config.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: c9n-spec
|
|
3
|
+
description: Scaffold the C9N spec-driven, ISO/IEC 29110-aligned agentic documentation system INTO a repo — AGENTS.md (protocol + stack) with a CLAUDE.md bridge, CONTEXT.md glossary, DESIGN.md, the docs/ tree (SRS, architecture, traceability, test-plan, ADRs, feature stubs, agent docs), and the PR-template merge gate. Use when bootstrapping a new C9N project, "init docs", "set up the spec-driven docs", "add the agentic workflow files", or porting the ddg-jewelry doc system to another repo. Complements c9n-deliverables (that generates docs OUT of a codebase; this seeds the doc system INTO one).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Bootstraps the Code Passion (C9N) spec-driven development doc system into a target repo. Drops
|
|
7
|
+
a self-contained, agent-readable documentation skeleton so a fresh project starts with the same
|
|
8
|
+
approval gates, traceability, and glossary discipline as the reference implementation
|
|
9
|
+
(ddg-jewelry). Fully generic — adapt the placeholders to whatever stack the project uses.
|
|
10
|
+
|
|
11
|
+
Bundled `templates/` are the source skeleton. **Copy them, then substitute tokens** — never edit
|
|
12
|
+
the templates in place for a specific project.
|
|
13
|
+
|
|
14
|
+
## What gets scaffolded
|
|
15
|
+
|
|
16
|
+
| File | Role |
|
|
17
|
+
|------|------|
|
|
18
|
+
| `AGENTS.md` | **Canonical** agent context: the full ISO/IEC 29110 spec-driven protocol (inlined, so the repo is self-contained) + C9N stack + module patterns. References the sibling `c9n-*` convention skills rather than copying them. |
|
|
19
|
+
| `CLAUDE.md` | Bridge that `@AGENTS.md`-imports the canonical file (so Claude Code's memory auto-loads the protocol; other tools read AGENTS.md directly). |
|
|
20
|
+
| `CONTEXT.md` | Ubiquitous-language glossary skeleton (no implementation detail). |
|
|
21
|
+
| `DESIGN.md` | Visual design-system rules — **only if the repo has a frontend.** |
|
|
22
|
+
| `docs/SRS.md` | Requirements master registry (feature short-codes + system NFRs). |
|
|
23
|
+
| `docs/architecture.md` | Software-design as-is map. |
|
|
24
|
+
| `docs/traceability.md` | Requirement → design → test master index. |
|
|
25
|
+
| `docs/test-plan.md` | Master test strategy. |
|
|
26
|
+
| `docs/adr/0001-spec-driven-iso29110-docs.md` | Seed ADR: why this doc system exists. |
|
|
27
|
+
| `docs/adr/_TEMPLATE.md` | ADR format for future decisions. |
|
|
28
|
+
| `docs/features/_TEMPLATE.md` | Per-feature deep-doc stub (Requirements · Design · Test Plan · Test Cases · Traceability). |
|
|
29
|
+
| `docs/agents/{issue-tracker,triage-labels,domain}.md` | Agent-process docs (GitHub `gh`, triage vocab, domain-doc consumption). |
|
|
30
|
+
| `.github/pull_request_template.md` | The Definition-of-Done merge gate. |
|
|
31
|
+
|
|
32
|
+
## Workflow
|
|
33
|
+
|
|
34
|
+
Run each step in order; this is a table of contents — the rules are below.
|
|
35
|
+
|
|
36
|
+
1. **Detect repo shape.** Read the target's root `package.json`/workspaces. Decide:
|
|
37
|
+
monorepo vs single app; **frontend present?** (`apps/web`, or `next`/`astro`/`vite`/`react`
|
|
38
|
+
in any `package.json`) → include `DESIGN.md`. **Backend/API present?** → keep the API
|
|
39
|
+
module-patterns section in AGENTS.md; otherwise tell the user it's deletable.
|
|
40
|
+
2. **No-clobber.** For every target path, **skip if it already exists** — never overwrite. Collect
|
|
41
|
+
skipped paths for the final report. (Re-running the skill must be safe and idempotent.)
|
|
42
|
+
3. **Copy templates** into the target repo, substituting tokens (below). Skip `DESIGN.md` if no
|
|
43
|
+
frontend. Skip the API section guidance if no backend.
|
|
44
|
+
4. **Optional scan — OFF by default.** Only if the user **explicitly** asks to seed from code
|
|
45
|
+
("scan the codebase", "seed the glossary", "reverse-document"): read the codebase and fill
|
|
46
|
+
- `CONTEXT.md` — first real glossary terms (actors, core entities) from the domain;
|
|
47
|
+
- `docs/architecture.md` — the actual module map + data-model relationships;
|
|
48
|
+
- `docs/features/` — one as-built stub per shipped module (low-confidence, no test cases),
|
|
49
|
+
and register each in `docs/SRS.md` with a fresh short-code.
|
|
50
|
+
Without this request, leave the placeholders untouched.
|
|
51
|
+
5. **Report.** Print created vs skipped files, then next steps: pick the first feature, write its
|
|
52
|
+
SRS section (Gate 1), get sign-off, then Design + Test Plan (Gate 2) — no production code
|
|
53
|
+
before Gate 2.
|
|
54
|
+
|
|
55
|
+
## Token substitution
|
|
56
|
+
|
|
57
|
+
When copying, replace these placeholders:
|
|
58
|
+
|
|
59
|
+
| Token | Source |
|
|
60
|
+
|-------|--------|
|
|
61
|
+
| `{{PROJECT_NAME}}` | Target root dir name, or `package.json` `name` (humanized). |
|
|
62
|
+
| `{{YEAR}}` / `{{DATE}}` | Current year / ISO date (for the seed ADR). |
|
|
63
|
+
| `{{DECIDER}}` | Ask, or default to the repo owner. |
|
|
64
|
+
| Stack rows in AGENTS.md | Keep C9N defaults; trim rows the detected stack doesn't use. |
|
|
65
|
+
|
|
66
|
+
## Conventions (referenced, not copied)
|
|
67
|
+
|
|
68
|
+
The scaffolded `AGENTS.md` points at the sibling C9N skills for engineering conventions — it does
|
|
69
|
+
**not** inline them, so they have a single source of truth:
|
|
70
|
+
|
|
71
|
+
- Commits → `c9n-commit-messages` · Branching → `c9n-git-branching` · PRs → `c9n-pull-requests`
|
|
72
|
+
- Clean code → `c9n-clean-code-typescript` · Lint/types → `c9n-typescript-eslint`
|
|
73
|
+
- Formatting → `c9n-code-formatting` · Review → `c9n-code-review`
|
|
74
|
+
|
|
75
|
+
For C9N-internal repos every dev has these installed, so the references always resolve. If you
|
|
76
|
+
scaffold into a repo where they may not be installed, tell the user to
|
|
77
|
+
`npx @codepassion/skills install claude`.
|
|
78
|
+
|
|
79
|
+
## Gotchas
|
|
80
|
+
|
|
81
|
+
- **Combined install mode does not carry `templates/`.** `install.sh combined` copies only the
|
|
82
|
+
generated `skills/c9n` SKILL.md body; the bundled templates are absent. Install `c9n-spec` via
|
|
83
|
+
`install.sh claude` (or `project`/`codex`) so the templates come along. Same limitation as
|
|
84
|
+
`c9n-deliverables`' scripts.
|
|
85
|
+
- **AGENTS.md vs CLAUDE.md.** AGENTS.md is the real file. CLAUDE.md exists only so Claude Code's
|
|
86
|
+
memory auto-load reaches the protocol — it does so via the **`@AGENTS.md` import** (Claude Code's
|
|
87
|
+
memory-import syntax), not a markdown link (a link is inert and pulls nothing into context). Don't
|
|
88
|
+
duplicate content into CLAUDE.md. If a target tool doesn't support `@`-imports, it reads AGENTS.md
|
|
89
|
+
directly anyway, so nothing is lost.
|
|
90
|
+
- **Don't grade the stubs.** If you run the optional scan, as-built stubs describe *current*
|
|
91
|
+
behavior at low confidence with **no** test cases — deepen on touch, per the protocol. Don't
|
|
92
|
+
invent acceptance criteria for code you only read.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!-- See AGENTS.md › Spec-Driven Development Protocol for the workflow and behavior-change rule. -->
|
|
2
|
+
|
|
3
|
+
## What & why
|
|
4
|
+
|
|
5
|
+
<!-- Ticket ID + one-line summary. Link the approved SRS/Design if this is a feature. -->
|
|
6
|
+
|
|
7
|
+
## Spec-driven gate
|
|
8
|
+
|
|
9
|
+
- [ ] For a **new feature**: SRS approved (Gate 1) and Design + Test Plan approved (Gate 2) **before** this code.
|
|
10
|
+
- [ ] This PR links the relevant `docs/features/<name>.md` (or master SRS section).
|
|
11
|
+
|
|
12
|
+
## Behavior-change check
|
|
13
|
+
|
|
14
|
+
> Does this change **observable behavior, a requirement, an interface, or a test outcome**?
|
|
15
|
+
|
|
16
|
+
- [ ] **No** — refactor / rename / dep-bump / typo / format. Doc updates N/A. _(skip the next section)_
|
|
17
|
+
- [ ] **Yes** — complete the doc-update checklist below.
|
|
18
|
+
|
|
19
|
+
### Doc updates (only if "Yes" above)
|
|
20
|
+
|
|
21
|
+
- [ ] SRS updated (new/changed requirement) — `docs/SRS.md` or `docs/features/<name>.md`
|
|
22
|
+
- [ ] Design updated (behavior/architecture) — `docs/architecture.md` or feature `## Design` (+ ADR if hard-to-reverse)
|
|
23
|
+
- [ ] Test cases updated/added (incl. regression for bug fixes)
|
|
24
|
+
- [ ] Traceability updated — `docs/traceability.md` or feature `## Traceability`
|
|
25
|
+
|
|
26
|
+
## Verification
|
|
27
|
+
|
|
28
|
+
- [ ] Tests pass locally
|
|
29
|
+
- [ ] Lint/format clean (no semicolons, double quotes)
|