RƏSƏDXANA

Şablonlar

Dərslər «qaydalar faylı yaz», «CI-a qapı qoy», «çıxışı müqaviləyə bağla» deyir. Bunları heç görməmiş birinə daha çox mətn yox, faylın özü lazımdır — bir baxışda oxunacaq qədər qısa, kor-koranə yapışdırılmayacaq qədər konkret.

Layihə qaydaları

Nə vaxt: Yeni layihənin ilk günü, agent ilk dəfə işə salınmazdan əvvəl.

CLAUDE.md
# <project>

## Commands
npm run dev            # local
npm run check:all      # must be green before any commit
npm run build          # before any deploy

## Structure
src/lib      data and logic, no JSX
src/app      routes; one view component per route
workers/     the API; nothing here imports from src/

## Invariants
- Identity comes only from the verified session. Never from body or query.
- Every user-visible string is localised; no bare text in components.
- Money and counts are integers in the smallest unit.

## Never
- Never commit to master; work on a branch.
- Never deploy without being asked.
- Never add a dependency without saying what it costs and what it replaces.

## Learned the hard way
- A vote count kept beside its rows drifted from them. Derive, don't store.
- A cookie with SameSite=Lax failed in production: the API is another domain.

Dəyişdir: Əmrləri və qovluqları öz layihənə görə dəyiş. «Çətinliklə öyrənilənlər» bölməsini boş başlat və hər xətadan sonra bir sətir əlavə et — bu bölmə zamanla ən dəyərli hissə olur.

Dərs: Alətin mimarisi: agentin hüdudları

İcazə sərhədi

Nə vaxt: Agentə hansı əmrləri soruşmadan işlətməyə icazə verdiyini təyin edəndə.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "Bash(npm run check:*)",
      "Bash(npm run build)",
      "Bash(git status)",
      "Bash(git diff:*)"
    ],
    "ask": [
      "Bash(git push:*)",
      "Bash(npm install:*)",
      "Bash(npx wrangler deploy:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Read(./.env*)"
    ]
  }
}

Dəyişdir: Yoxlama və oxuma əmrlərini sərbəst burax. Canlıya çıxan, silən və ya məlumat göndərən hər şey soruşsun. Sirr fayllarını oxumağı bağla.

Dərs: Alətin mimarisi: agentin hüdudları

Ötürmə qeydi

Nə vaxt: Sessiya uzananda və ya günün sonunda, təmiz sessiyaya keçməzdən əvvəl.

docs/handoff.md
# Handoff — <date>

Done
- <what is finished, with the commit>

Left
- <the next step, in one line>

Decided (do not re-open without a reason)
- <decision> because <reason>

Watch out
- <the trap this area has>

Run before continuing
- npm run check:all

Dəyişdir: Qısa saxla. «Qərar verildi» bölməsi ən vacibidir: onsuz növbəti sessiya eyni müzakirəni yenidən açır.

Dərs: Kontekst işin özüdür

Şərtnamə

Nə vaxt: Kod yazdırmazdan əvvəl, hər yeni funksiya üçün.

docs/spec-<feature>.md
# <feature>

Goal        <one sentence>
Non-goals   <what this will not do>

Data
- <field>: <type>, and what an empty value means

API
- <METHOD> <path> — who may call it, what it returns on 401 / 403 / 4xx / 5xx

Edge cases
- offline, slow network, double click, two tabs, stale data

Acceptance (checked by hand)
- [ ] <observable behaviour>
- [ ] <observable behaviour>

Steps (each ends in something checkable)
1. <step>
2. <step>

Dəyişdir: Qeyri-məqsədləri boş buraxma — kapsamın sürüşməsini dayandıran yeganə sətir odur.

Dərs: Əvvəl şərtnamə, sonra kiçik addımlar

Endpoint sınağı

Nə vaxt: Yeni endpoint yazılandan sonra, «işləyir» deməzdən əvvəl.

scripts/trial.sh
API=https://api.example.dev

# 1 — no session at all: expect 401
curl -s -o /dev/null -w "no session:    %{http_code}\n" \
  -X POST "$API/api/vote" -d '{"postId":"p1"}'

# 2 — signed in as somebody else's id in the body: expect 401/403, never 200
curl -s -o /dev/null -w "someone else:  %{http_code}\n" \
  -X POST "$API/api/vote" -H "Authorization: Bearer $OTHER" \
  -d '{"postId":"p1","voterId":"not-mine"}'

# 3 — signed in as me: expect 200
curl -s -o /dev/null -w "me:            %{http_code}\n" \
  -X POST "$API/api/vote" -H "Authorization: Bearer $MINE" -d '{"postId":"p1"}'

Dəyişdir: Token-ləri mühit dəyişənindən götür, fayla yazma. İkinci sınaq ən vacibidir: 200 qayıdırsa, kimlik gövdədən oxunur.

Dərs: İnterfeys kilid deyil

CI-da AI qapısı

Nə vaxt: PR açılanda avtomatik işləsin deyə — AI kodu nə qədər sürətli gəlsə, qapı bir o qədər lazımdır.

.github/workflows/checks.yml
name: checks
on: [push, pull_request]

jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22, cache: npm }
      - run: npm ci
      - run: npx tsc --noEmit
      - run: npx eslint .
      - run: npm run check:all          # your own invariants
      - name: no identity from the body
        run: |
          ! grep -rn "body.userId\|searchParams.get(\"user" src workers \
            || (echo "identity must come from the session" && exit 1)

Dəyişdir: Son addım nümunədir: öz invariantını qoy. Qapı yalnız o zaman qapıdır ki, qaydanı qəsdən pozanda qırmızı olsun — bir dəfə sına.

Dərs: Layihəni qoruyan yoxlamalar

PR-da AI review siyahısı

Nə vaxt: AI ilə yazılmış dəyişikliyi birləşdirməzdən əvvəl — öz PR-ın olsa belə.

.github/pull_request_template.md
## What and why
<one paragraph; why, not what>

## Evidence
- [ ] A check that failed before this change and passes now: <name>
- [ ] Verified in the running app / against the deployed API: <how>

## Audit
- [ ] Identity comes only from the session; no id from body or query
- [ ] Ownership or membership checked server-side for every resource id
- [ ] Every opened listener / timer / subscription is closed
- [ ] No query inside a loop; new tables have a retention rule
- [ ] Optimistic updates roll back and say why
- [ ] Nothing mechanical (escapes, encodings, generated files) changed silently

## Risk
<what breaks if this is wrong, and how to roll back>

Dəyişdir: Siyahını öz layihənin xətalarına görə qısalt və uzat. Heç vaxt işə düşməyən bənd silinir; iki dəfə işə düşən bənd yoxlamaya çevrilir.

Dərs: İki prompt, bir fayl

Müqavilə testi

Nə vaxt: Sistemin bir hissəsi modelin cavabından asılı olanda.

src/lib/answer-contract.ts
import { z } from "zod";

export const Answer = z.object({
  title: z.string().min(1).max(120),
  steps: z.array(z.string().min(1)).min(1).max(10),
  confidence: z.number().min(0).max(1).optional(),
});
export type Answer = z.infer<typeof Answer>;

export function parseAnswer(raw: string):
  | { ok: true; value: Answer }
  | { ok: false; reason: string } {
  try {
    const parsed = Answer.safeParse(JSON.parse(raw));
    return parsed.success
      ? { ok: true, value: parsed.data }
      : { ok: false, reason: parsed.error.issues[0].message };
  } catch {
    return { ok: false, reason: "not JSON" };
  }
}

/* The test, in whatever runner you use:
   - a valid answer passes
   - a renamed field ("instructions") fails
   - a missing field fails
   - prose around the JSON fails
   - an empty answer fails
   Each failure must reach the fallback path, not a crash. */

Dəyişdir: Sxemi öz sahələrinə görə yaz. Vacib olan parse deyil, uğursuzluq yolu: müqavilə pozulanda istifadəçi nə görür?

Dərs: Model dəyişəndə: müqavilə testləri

Deploy runbook-u

Nə vaxt: İlk deploy-dan əvvəl yazılır, hər deploy-da oxunur.

docs/runbook.md
# Runbook

## Deploy
1. npm run check:all        (green)
2. npm run build
3. <deploy command>
4. Verify live: open <url>, and curl the two endpoints below
5. Watch logs for 5 minutes

## Rollback  (rehearsed on <date>, takes ~<n> minutes)
<command>

## Differences between local and production
- domains:  local <...>  production <...>
- cookies:  SameSite <...> because the API is <same|another> domain
- limits:   CPU <...> per request, memory <...>

## Secrets (names only)
- <NAME> — set in <platform>, rotated <when>

## Cost to watch monthly
- requests, database size, AI API calls (model, count, estimate)

Dəyişdir: Rollback sətrini boş buraxma və bir dəfə məşq et: vaxtı ölç və bura yaz. Ən pis anda oxunacaq yeganə rəqəm odur.

Dərs: Deploy, sirlər, müşahidə