diff --git a/knowledge/curl-api-testing.md b/knowledge/curl-api-testing.md new file mode 100644 index 0000000..4e401dd --- /dev/null +++ b/knowledge/curl-api-testing.md @@ -0,0 +1,160 @@ +# curl — API Testing & Verification + +## Why Test With curl +- Every API endpoint must be testable with curl before marking a task done. +- curl is the ground truth — if curl can't hit it, the API doesn't work. +- Test locally first (`localhost`), then staging, then production. +- Save working curl commands as documentation for future reference. + +## Basic Patterns +```bash +# GET request +curl -s http://localhost:8080/health | jq . + +# POST with JSON body +curl -s -X POST http://localhost:8080/memory/ingest \ + -H "Content-Type: application/json" \ + -d '{"project": "poimen", "text": "test record", "kind": "L1"}' + +# PUT update +curl -s -X PUT http://localhost:8080/resource/123 \ + -H "Content-Type: application/json" \ + -d '{"field": "new_value"}' + +# DELETE +curl -s -X DELETE http://localhost:8080/resource/123 +``` + +## Authentication +```bash +# Bearer token (JWT) +curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/memory/query + +# Get JWT from Authentik +TOKEN=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \ + -d "grant_type=client_credentials" \ + -d "client_id=$CLIENT_ID" \ + -d "client_secret=$CLIENT_SECRET" | jq -r .access_token) + +# API key header +curl -s -H "X-API-Key: $API_KEY" http://localhost:8080/endpoint +``` + +## Response Inspection +```bash +# Status code only +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health + +# Headers + body +curl -sv http://localhost:8080/health 2>&1 + +# Response time +curl -s -o /dev/null -w "%{time_total}s" http://localhost:8080/health + +# Follow redirects +curl -sL http://localhost:8080/old-path + +# Pretty print JSON +curl -s http://localhost:8080/health | python3 -m json.tool +``` + +## Error Testing +```bash +# Expect 401 — missing auth +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/memory/query +# Should return: 401 + +# Expect 400 — bad request body +curl -s -X POST http://localhost:8080/memory/ingest \ + -H "Content-Type: application/json" \ + -d '{"invalid": true}' +# Should return: 400 + +# Expect 429 — rate limit +for i in $(seq 1 200); do + curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $TOKEN" \ + http://localhost:8080/memory/query -d '{"query":"test"}' +done | sort | uniq -c +# Should see 429 after limit exceeded + +# Expect 404 — not found +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/nonexistent +``` + +## Kubernetes Service Testing +```bash +# Port-forward to test in-cluster service +kubectl port-forward -n poimen svc/poimen-memory 8080:8080 & +curl -s http://localhost:8080/health + +# Direct pod exec curl +kubectl exec -n poimen deploy/poimen-memory -- curl -s http://localhost:8080/health + +# Test from inside cluster (debug pod) +kubectl run -it --rm curl-test --image=curlimages/curl -- \ + curl -s http://poimen-memory.poimen.svc.cluster.local:8080/health + +# Test ingress from outside +curl -sk https://api.riotpiao.com/health +``` + +## Verification Checklist +After completing any API work, verify with curl: +1. **Happy path** — correct input returns expected output and status code +2. **Auth required** — missing token returns 401, bad token returns 403 +3. **Validation** — bad input returns 400 with error message +4. **Not found** — missing resource returns 404 +5. **Idempotency** — same request twice returns same result (for POST with idempotency key) +6. **Rate limiting** — excessive requests return 429 +7. **Content type** — response has correct Content-Type header +8. **Latency** — response time within SLA (< 500ms for queries, < 50ms for health) + +## Scripted Verification +```bash +#!/bin/bash +# verify-api.sh — run after any API change +BASE="http://localhost:8080" +PASS=0; FAIL=0 + +check() { + local desc="$1" expected="$2" actual="$3" + if [ "$expected" = "$actual" ]; then + echo "✓ $desc"; ((PASS++)) + else + echo "✗ $desc (expected $expected, got $actual)"; ((FAIL++)) + fi +} + +check "health returns 200" "200" \ + "$(curl -s -o /dev/null -w '%{http_code}' $BASE/health)" + +check "query without auth returns 401" "401" \ + "$(curl -s -o /dev/null -w '%{http_code}' -X POST $BASE/memory/query)" + +check "ingest with bad body returns 400" "400" \ + "$(curl -s -o /dev/null -w '%{http_code}' -X POST $BASE/memory/ingest \ + -H 'Content-Type: application/json' -d '{}')" + +echo "---" +echo "$PASS passed, $FAIL failed" +[ $FAIL -eq 0 ] && exit 0 || exit 1 +``` + +## Data Piping +```bash +# Ingest from file +curl -s -X POST http://localhost:8080/memory/ingest \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" \ + -d @payload.json + +# Query and filter with jq +curl -s -H "Authorization: Bearer $TOKEN" \ + http://localhost:8080/memory/query \ + -d '{"project":"poimen","query":"rust error handling"}' | \ + jq '.results[] | {score, text}' + +# Chain: query → pipe to next call +CHUNK_ID=$(curl -s ... | jq -r '.results[0].id') +curl -s http://localhost:8080/memory/chunk/$CHUNK_ID +``` diff --git a/knowledge/tea-cli.md b/knowledge/tea-cli.md new file mode 100644 index 0000000..42e51f1 --- /dev/null +++ b/knowledge/tea-cli.md @@ -0,0 +1,145 @@ +# tea CLI — Gitea/Forgejo Command Line + +## Setup +```bash +# Login to Forgejo instance +tea login add --name riotpiao \ + --url https://git.riotpiao.com \ + --token + +# Verify +tea whoami +``` + +## Repository Operations +```bash +# List repos +tea repos ls + +# Clone +tea clone rock/poimen-memory + +# Open in browser +tea open +``` + +## Issues & Tickets +```bash +# List issues +tea issues ls --repo rock/poimen-memory +tea issues ls --state open +tea issues ls --labels bug + +# Create issue +tea issues create --title "Fix port conflict" --body "Port 8080 is in use" + +# Close issue +tea issues close 42 + +# Comment on issue +tea comments create 42 --body "Fixed in commit abc123" + +# Assign +tea issues edit 42 --assignees rock +``` + +## Pull Requests +```bash +# List PRs +tea pr ls --repo rock/poimen-memory +tea pr ls --state open + +# Create PR from current branch +tea pr create --title "feat: add learn command" --base main + +# Checkout a PR locally +tea pr checkout 15 + +# Merge PR +tea pr merge 15 --style squash + +# Review PR +tea pr review 15 --approve +tea pr review 15 --request-changes --body "Fix the unwrap()" +``` + +## CI/CD — Actions & Workflows +```bash +# List workflow runs +tea actions runs list --repo rock/poimen-memory +tea actions runs list --repo rock/poimen-memory --limit 5 + +# View specific run +tea actions runs view --repo rock/poimen-memory + +# List workflows +tea actions workflows list --repo rock/poimen-memory + +# Secrets management +tea actions secrets list --repo rock/poimen-memory +tea actions secrets create --repo rock/poimen-memory --name MY_SECRET --value "secret123" + +# Variables +tea actions variables list --repo rock/poimen-memory +``` + +## Releases +```bash +# List releases +tea releases ls --repo rock/poimen-memory + +# Create release +tea releases create --repo rock/poimen-memory \ + --tag v1.0.0 \ + --title "v1.0.0 — Production Release" \ + --note "First production release" +``` + +## Wiki +```bash +# List wiki pages +tea wiki ls --repo rock/poimen-memory + +# Create wiki page +tea wiki create --repo rock/poimen-memory \ + --title "Setup Guide" \ + --content "# Setup\n..." +``` + +## API Direct Access +```bash +# Raw API call (authenticated) +tea api /repos/rock/poimen-memory +tea api /repos/rock/poimen-memory/issues?state=open + +# POST via API +tea api --method POST /repos/rock/poimen-memory/issues \ + --body '{"title":"test","body":"test issue"}' +``` + +## Ticket Verification Workflow +After completing a task, verify the ticket is done: +```bash +# 1. Check CI passed +tea actions runs list --repo rock/poimen-memory --limit 1 +# Should show: ✓ completed / success + +# 2. Check issue is closed or PR merged +tea issues ls --repo rock/poimen-memory --state closed --limit 5 + +# 3. Verify the API endpoint works (curl the actual service) +curl -s http://localhost:8080/health | jq .status +# Should return: "ok" + +# 4. Tag release if milestone complete +tea releases create --tag v1.x.x --title "Milestone X complete" +``` + +## Useful Flags +- `--repo owner/name` — specify repo (or use current git context) +- `--output simple` — machine-readable output +- `--output yaml` — YAML format +- `--output json` — JSON format for piping to jq +- `--limit N` — limit results +- `--state open|closed|all` — filter by state +- `--fields name,status` — select columns diff --git a/knowledge/verify-done.md b/knowledge/verify-done.md new file mode 100644 index 0000000..01c3b60 --- /dev/null +++ b/knowledge/verify-done.md @@ -0,0 +1,136 @@ +# Verify Done — Confirming Task Completion + +## Principle +A task is NOT done until it's verified with real data against the real service. Code review + CI green is necessary but not sufficient. + +## Definition of Done Checklist +1. **Code compiles** — `cargo build` / `go build` passes with no errors +2. **Tests pass** — `cargo test` / `go test ./...` all green +3. **CI green** — `tea actions runs list` shows latest run succeeded +4. **API accessible** — `curl` the endpoint, get expected response +5. **Auth works** — requests without token return 401, with token return 200 +6. **Error paths tested** — bad input returns proper error codes +7. **Deployed** — ArgoCD synced, pod running, ingress reachable +8. **Documented** — endpoint added to API docs or CLAUDE.md + +## Verification Flow +``` +Code change → Push → CI passes → ArgoCD deploys → curl test → Done + ↓ + If fails → fix → repeat +``` + +## API Endpoint Verification Template +For every new or changed endpoint, run: +```bash +ENDPOINT="https://api.riotpiao.com" + +# 1. Is it alive? +curl -s -o /dev/null -w "%{http_code}" $ENDPOINT/health +# Expect: 200 + +# 2. Does the new endpoint exist? +curl -s -o /dev/null -w "%{http_code}" $ENDPOINT/new-endpoint +# Expect: NOT 404 + +# 3. Does auth gate work? +curl -s -o /dev/null -w "%{http_code}" $ENDPOINT/new-endpoint +# Expect: 401 (no token) + +# 4. Does it return correct data? +curl -s -H "Authorization: Bearer $TOKEN" $ENDPOINT/new-endpoint | jq . +# Expect: meaningful JSON response + +# 5. Does it handle bad input? +curl -s -o /dev/null -w "%{http_code}" -X POST $ENDPOINT/new-endpoint \ + -H "Content-Type: application/json" -d '{}' +# Expect: 400 +``` + +## CI Verification +```bash +# Check last CI run status +tea actions runs list --repo rock/poimen-memory --limit 1 + +# If failed, check logs +tea actions runs view --repo rock/poimen-memory + +# Check if ArgoCD synced +kubectl get application -n argocd poimen-memory-app -o jsonpath='{.status.sync.status}' +# Expect: Synced + +# Check pod health +kubectl get pods -n poimen | grep poimen-memory +# Expect: Running, no restarts +``` + +## Deployment Verification +```bash +# Pod running and ready +kubectl get pods -n poimen -l app.kubernetes.io/name=poimen-memory +# Expect: 1/1 Running + +# Service reachable internally +kubectl exec -n poimen deploy/poimen-memory -- curl -s http://localhost:8080/health + +# Ingress reachable externally +curl -sk https://api.riotpiao.com/health + +# Logs clean (no panics, no errors on startup) +kubectl logs -n poimen deploy/poimen-memory --tail=20 +``` + +## Git Commit Verification +Before pushing, confirm: +```bash +# Commit message follows conventional commits +git log -1 --oneline +# Expect: feat: / fix: / docs: / refactor: prefix + +# No secrets in diff +git diff --cached | grep -iE "password|secret|token|api_key" +# Expect: empty (no matches) + +# No .env or key files staged +git diff --cached --name-only | grep -iE "\.env|\.key|\.pem" +# Expect: empty +``` + +## Memory System Specific Verification +```bash +# After ingest changes +curl -s -X POST https://api.riotpiao.com/memory/ingest \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"project":"test","text":"verify ingest","kind":"L1"}' | jq . +# Expect: 201 with chunk ID + +# After query changes +curl -s -X POST https://api.riotpiao.com/memory/query \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"project":"test","query":"verify"}' | jq . +# Expect: 200 with results array + +# After context endpoint changes +curl -s -X POST https://api.riotpiao.com/memory/context \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"project":"test","tool":"cargo","task":"build"}' | jq . +# Expect: 200 with tier, lessons, budget + +# After rebuild changes +curl -s -X POST https://api.riotpiao.com/memory/rebuild \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"project":"test","dry_run":true}' | jq . +# Expect: 200 with records_processed count +``` + +## Anti-Patterns +- ❌ "CI passed so it's done" — CI doesn't test the real deployment +- ❌ "It works on my machine" — must work in-cluster +- ❌ Marking done without curl-testing the endpoint +- ❌ Skipping error path testing (400, 401, 404, 429) +- ❌ Not checking ArgoCD sync status after push +- ❌ Trusting `kubectl apply` over ArgoCD (let ArgoCD manage state) diff --git a/log/knowledge/learn/latest.jsonl b/log/knowledge/learn/latest.jsonl index 0daa906..41a8917 100644 --- a/log/knowledge/learn/latest.jsonl +++ b/log/knowledge/learn/latest.jsonl @@ -63,3 +63,34 @@ {"project":"knowledge","query":"solid-dry-principles:10","run":"latest","turn":10,"event_type":"learn","data":{"chunk_index":10,"level":"L1","sha256":"e5fb640cbedc9f409f3fd73978f0578f2af62c89df104ec3054d1f19579179f7","source":"knowledge/solid-dry-principles.md","text":"## Composition Over Inheritance\n- Prefer composing objects over class hierarchies.\n- In Rust: no inheritance. Composition is the default via struct fields + trait delegation.\n- `struct HttpServer { router: Router, auth: AuthMiddleware, rate_limiter: RateLimiter }`","total_chunks":13}} {"project":"knowledge","query":"solid-dry-principles:11","run":"latest","turn":11,"event_type":"learn","data":{"chunk_index":11,"level":"L1","sha256":"0606d00ead93bf2f15c0e75447791e02ada10a1eef34e8ea5de20f7d705642e8","source":"knowledge/solid-dry-principles.md","text":"## Law of Demeter\n- Only talk to your immediate friends. Don't chain: `a.b().c().d()`.\n- Tell, don't ask: `order.ship()` not `order.get_warehouse().get_shipping().create_label()`.\n- In Rust: expose methods that encapsulate internal structure.","total_chunks":13}} {"project":"knowledge","query":"solid-dry-principles:12","run":"latest","turn":12,"event_type":"learn","data":{"chunk_index":12,"level":"L1","sha256":"4eaad9bdaf561bc037d4bf306d8838bc7341f117ef3cd28cd9be5571d4262548","source":"knowledge/solid-dry-principles.md","text":"## Practical Application\n- Start concrete, extract abstractions when patterns emerge.\n- Refactor in small steps with tests as safety net.\n- Code review checklist: SRP violated? Unnecessary coupling? Duplicated knowledge? Over-engineered?","total_chunks":13}} +{"project":"knowledge","query":"curl-api-testing:0","run":"latest","turn":0,"event_type":"learn","data":{"chunk_index":0,"level":"L1","sha256":"9ecbcde1289b24e760f605561c56abebcfef2ca9f4b3c6416eff7cc1934b0f78","source":"knowledge/curl-api-testing.md","text":"# curl — API Testing & Verification","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:1","run":"latest","turn":1,"event_type":"learn","data":{"chunk_index":1,"level":"L1","sha256":"8591f6f335f1533290505b772f7867d7ee2ad5d0089f665947d5730c3669d39b","source":"knowledge/curl-api-testing.md","text":"## Why Test With curl\n- Every API endpoint must be testable with curl before marking a task done.\n- curl is the ground truth — if curl can't hit it, the API doesn't work.\n- Test locally first (`localhost`), then staging, then production.\n- Save working curl commands as documentation for future reference.","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:2","run":"latest","turn":2,"event_type":"learn","data":{"chunk_index":2,"level":"L1","sha256":"45ad8d80df104d0a775eb6463796a833550047103ccae29b1a5a2077241fc351","source":"knowledge/curl-api-testing.md","text":"## Basic Patterns\n```bash\n# GET request\ncurl -s http://localhost:8080/health | jq .\n\n# POST with JSON body\ncurl -s -X POST http://localhost:8080/memory/ingest \\\n -H \"Content-Type: application/json\" \\\n -d '{\"project\": \"poimen\", \"text\": \"test record\", \"kind\": \"L1\"}'\n\n# PUT update\ncurl -s -X PUT http://localhost:8080/resource/123 \\\n -H \"Content-Type: application/json\" \\\n -d '{\"field\": \"new_value\"}'\n\n# DELETE\ncurl -s -X DELETE http://localhost:8080/resource/123\n```","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:3","run":"latest","turn":3,"event_type":"learn","data":{"chunk_index":3,"level":"L1","sha256":"d62b7acda370c2dac68e079a45a5d3e53a4740893dff238315e85cc0ff51e0b7","source":"knowledge/curl-api-testing.md","text":"## Authentication\n```bash\n# Bearer token (JWT)\ncurl -s -H \"Authorization: Bearer $TOKEN\" http://localhost:8080/memory/query\n\n# Get JWT from Authentik\nTOKEN=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \\\n -d \"grant_type=client_credentials\" \\\n -d \"client_id=$CLIENT_ID\" \\\n -d \"client_secret=$CLIENT_SECRET\" | jq -r .access_token)\n\n# API key header\ncurl -s -H \"X-API-Key: $API_KEY\" http://localhost:8080/endpoint\n```","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:4","run":"latest","turn":4,"event_type":"learn","data":{"chunk_index":4,"level":"L1","sha256":"b4aa92ecf23be98e8b03fbbf14520a3914493b1bdbef4b6df3ee96232ee1cf2e","source":"knowledge/curl-api-testing.md","text":"## Response Inspection\n```bash\n# Status code only\ncurl -s -o /dev/null -w \"%{http_code}\" http://localhost:8080/health\n\n# Headers + body\ncurl -sv http://localhost:8080/health 2>&1\n\n# Response time\ncurl -s -o /dev/null -w \"%{time_total}s\" http://localhost:8080/health\n\n# Follow redirects\ncurl -sL http://localhost:8080/old-path\n\n# Pretty print JSON\ncurl -s http://localhost:8080/health | python3 -m json.tool\n```","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:5","run":"latest","turn":5,"event_type":"learn","data":{"chunk_index":5,"level":"L1","sha256":"00c2d2ecaedf760754881e0c937b59f8b951f3fb2290eceed5a64115912bda4c","source":"knowledge/curl-api-testing.md","text":"## Error Testing\n```bash\n# Expect 401 — missing auth\ncurl -s -o /dev/null -w \"%{http_code}\" http://localhost:8080/memory/query\n# Should return: 401\n\n# Expect 400 — bad request body\ncurl -s -X POST http://localhost:8080/memory/ingest \\\n -H \"Content-Type: application/json\" \\\n -d '{\"invalid\": true}'\n# Should return: 400\n\n# Expect 429 — rate limit\nfor i in $(seq 1 200); do\n curl -s -o /dev/null -w \"%{http_code}\\n\" -H \"Authorization: Bearer $TOKEN\" \\\n http://localhost:8080/memory/query -d '{\"query\":\"test\"}'\ndone | sort | uniq -c\n# Should see 429 after limit exceeded\n\n# Expect 404 — not found\ncurl -s -o /dev/null -w \"%{http_code}\" http://localhost:8080/nonexistent\n```","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:6","run":"latest","turn":6,"event_type":"learn","data":{"chunk_index":6,"level":"L1","sha256":"c5c5274b426bee99520b53a494c1fc7bb7eb74bda278118307699d5721d779a3","source":"knowledge/curl-api-testing.md","text":"## Kubernetes Service Testing\n```bash\n# Port-forward to test in-cluster service\nkubectl port-forward -n poimen svc/poimen-memory 8080:8080 &\ncurl -s http://localhost:8080/health\n\n# Direct pod exec curl\nkubectl exec -n poimen deploy/poimen-memory -- curl -s http://localhost:8080/health\n\n# Test from inside cluster (debug pod)\nkubectl run -it --rm curl-test --image=curlimages/curl -- \\\n curl -s http://poimen-memory.poimen.svc.cluster.local:8080/health\n\n# Test ingress from outside\ncurl -sk https://api.riotpiao.com/health\n```","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:7","run":"latest","turn":7,"event_type":"learn","data":{"chunk_index":7,"level":"L1","sha256":"892aa36e1cfad7cee79eda7df75a25df9d22ff6b9eb1637760ec92982e6bf038","source":"knowledge/curl-api-testing.md","text":"## Verification Checklist\nAfter completing any API work, verify with curl:\n1. **Happy path** — correct input returns expected output and status code\n2. **Auth required** — missing token returns 401, bad token returns 403\n3. **Validation** — bad input returns 400 with error message\n4. **Not found** — missing resource returns 404\n5. **Idempotency** — same request twice returns same result (for POST with idempotency key)\n6. **Rate limiting** — excessive requests return 429\n7. **Content type** — response has correct Content-Type header\n8. **Latency** — response time within SLA (< 500ms for queries, < 50ms for health)","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:8","run":"latest","turn":8,"event_type":"learn","data":{"chunk_index":8,"level":"L1","sha256":"c51a5781f8c34d9194d4ac5e6cb6de13bfbc9b84d0e06f524e9a8d479c8ee614","source":"knowledge/curl-api-testing.md","text":"## Scripted Verification\n```bash\n#!/bin/bash\n# verify-api.sh — run after any API change\nBASE=\"http://localhost:8080\"\nPASS=0; FAIL=0\n\ncheck() {\n local desc=\"$1\" expected=\"$2\" actual=\"$3\"\n if [ \"$expected\" = \"$actual\" ]; then\n echo \"✓ $desc\"; ((PASS++))\n else\n echo \"✗ $desc (expected $expected, got $actual)\"; ((FAIL++))\n fi\n}\n\ncheck \"health returns 200\" \"200\" \\\n \"$(curl -s -o /dev/null -w '%{http_code}' $BASE/health)\"\n\ncheck \"query without auth returns 401\" \"401\" \\\n \"$(curl -s -o /dev/null -w '%{http_code}' -X POST $BASE/memory/query)\"\n\ncheck \"ingest with bad body returns 400\" \"400\" \\\n \"$(curl -s -o /dev/null -w '%{http_code}' -X POST $BASE/memory/ingest \\\n -H 'Content-Type: application/json' -d '{}')\"\n\necho \"---\"\necho \"$PASS passed, $FAIL failed\"\n[ $FAIL -eq 0 ] && exit 0 || exit 1\n```","total_chunks":10}} +{"project":"knowledge","query":"curl-api-testing:9","run":"latest","turn":9,"event_type":"learn","data":{"chunk_index":9,"level":"L1","sha256":"e8e32cac4614b04e7df4cf0cf923c2225155c361060c5f49648bda79239fa301","source":"knowledge/curl-api-testing.md","text":"## Data Piping\n```bash\n# Ingest from file\ncurl -s -X POST http://localhost:8080/memory/ingest \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -d @payload.json\n\n# Query and filter with jq\ncurl -s -H \"Authorization: Bearer $TOKEN\" \\\n http://localhost:8080/memory/query \\\n -d '{\"project\":\"poimen\",\"query\":\"rust error handling\"}' | \\\n jq '.results[] | {score, text}'\n\n# Chain: query → pipe to next call\nCHUNK_ID=$(curl -s ... | jq -r '.results[0].id')\ncurl -s http://localhost:8080/memory/chunk/$CHUNK_ID\n```","total_chunks":10}} +{"project":"knowledge","query":"tea-cli:0","run":"latest","turn":0,"event_type":"learn","data":{"chunk_index":0,"level":"L1","sha256":"f6422b9218ba12e9826a237d4ae4fc36da9643b750fa7bcd02f29fa91667fb8a","source":"knowledge/tea-cli.md","text":"# tea CLI — Gitea/Forgejo Command Line","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:1","run":"latest","turn":1,"event_type":"learn","data":{"chunk_index":1,"level":"L1","sha256":"25dfbeb95e7841646e833e0de0ad6592838236436c495aaf2e547f7b9f8c8956","source":"knowledge/tea-cli.md","text":"## Setup\n```bash\n# Login to Forgejo instance\ntea login add --name riotpiao \\\n --url https://git.riotpiao.com \\\n --token \n\n# Verify\ntea whoami\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:2","run":"latest","turn":2,"event_type":"learn","data":{"chunk_index":2,"level":"L1","sha256":"fb250f6093342be0709daa5b8d2368cef007f91ee55490dda5388465dae5ce66","source":"knowledge/tea-cli.md","text":"## Repository Operations\n```bash\n# List repos\ntea repos ls\n\n# Clone\ntea clone rock/poimen-memory\n\n# Open in browser\ntea open\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:3","run":"latest","turn":3,"event_type":"learn","data":{"chunk_index":3,"level":"L1","sha256":"a5c77ed2128b946d903e503abeed5129f743ea6c7d0a1e02e5a6fae32d74b99d","source":"knowledge/tea-cli.md","text":"## Issues & Tickets\n```bash\n# List issues\ntea issues ls --repo rock/poimen-memory\ntea issues ls --state open\ntea issues ls --labels bug\n\n# Create issue\ntea issues create --title \"Fix port conflict\" --body \"Port 8080 is in use\"\n\n# Close issue\ntea issues close 42\n\n# Comment on issue\ntea comments create 42 --body \"Fixed in commit abc123\"\n\n# Assign\ntea issues edit 42 --assignees rock\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:4","run":"latest","turn":4,"event_type":"learn","data":{"chunk_index":4,"level":"L1","sha256":"9ec3fe3fe3d53241f09991f62cce4fe5abff9e99a3f7778b20189f04f49b6cb1","source":"knowledge/tea-cli.md","text":"## Pull Requests\n```bash\n# List PRs\ntea pr ls --repo rock/poimen-memory\ntea pr ls --state open\n\n# Create PR from current branch\ntea pr create --title \"feat: add learn command\" --base main\n\n# Checkout a PR locally\ntea pr checkout 15\n\n# Merge PR\ntea pr merge 15 --style squash\n\n# Review PR\ntea pr review 15 --approve\ntea pr review 15 --request-changes --body \"Fix the unwrap()\"\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:5","run":"latest","turn":5,"event_type":"learn","data":{"chunk_index":5,"level":"L1","sha256":"4847500431acbf2641a6ef5396166cdb316999f0d12a97856d9e9e39cfa3c5fe","source":"knowledge/tea-cli.md","text":"## CI/CD — Actions & Workflows\n```bash\n# List workflow runs\ntea actions runs list --repo rock/poimen-memory\ntea actions runs list --repo rock/poimen-memory --limit 5\n\n# View specific run\ntea actions runs view --repo rock/poimen-memory\n\n# List workflows\ntea actions workflows list --repo rock/poimen-memory\n\n# Secrets management\ntea actions secrets list --repo rock/poimen-memory\ntea actions secrets create --repo rock/poimen-memory --name MY_SECRET --value \"secret123\"\n\n# Variables\ntea actions variables list --repo rock/poimen-memory\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:6","run":"latest","turn":6,"event_type":"learn","data":{"chunk_index":6,"level":"L1","sha256":"a1b2e7855fbec4d603f343695f43f88ce2c226dd64c79ffdde74d8ef5aed7086","source":"knowledge/tea-cli.md","text":"## Releases\n```bash\n# List releases\ntea releases ls --repo rock/poimen-memory\n\n# Create release\ntea releases create --repo rock/poimen-memory \\\n --tag v1.0.0 \\\n --title \"v1.0.0 — Production Release\" \\\n --note \"First production release\"\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:7","run":"latest","turn":7,"event_type":"learn","data":{"chunk_index":7,"level":"L1","sha256":"a1dfa3250a846495a4c18eaffbc79056dd18df89f80525324f37acb9d799fd69","source":"knowledge/tea-cli.md","text":"## Wiki\n```bash\n# List wiki pages\ntea wiki ls --repo rock/poimen-memory\n\n# Create wiki page\ntea wiki create --repo rock/poimen-memory \\\n --title \"Setup Guide\" \\\n --content \"# Setup\\n...\"\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:8","run":"latest","turn":8,"event_type":"learn","data":{"chunk_index":8,"level":"L1","sha256":"21e4a3407188a0ef9d952c67a94adda8124744d34108e2bacce0d3e80e2fa7c6","source":"knowledge/tea-cli.md","text":"## API Direct Access\n```bash\n# Raw API call (authenticated)\ntea api /repos/rock/poimen-memory\ntea api /repos/rock/poimen-memory/issues?state=open\n\n# POST via API\ntea api --method POST /repos/rock/poimen-memory/issues \\\n --body '{\"title\":\"test\",\"body\":\"test issue\"}'\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:9","run":"latest","turn":9,"event_type":"learn","data":{"chunk_index":9,"level":"L1","sha256":"d93eebb42cfc4bb536b23d7e5bf7091c5213a59bad764ac1e32c2e3475049ead","source":"knowledge/tea-cli.md","text":"## Ticket Verification Workflow\nAfter completing a task, verify the ticket is done:\n```bash\n# 1. Check CI passed\ntea actions runs list --repo rock/poimen-memory --limit 1\n# Should show: ✓ completed / success\n\n# 2. Check issue is closed or PR merged\ntea issues ls --repo rock/poimen-memory --state closed --limit 5\n\n# 3. Verify the API endpoint works (curl the actual service)\ncurl -s http://localhost:8080/health | jq .status\n# Should return: \"ok\"\n\n# 4. Tag release if milestone complete\ntea releases create --tag v1.x.x --title \"Milestone X complete\"\n```","total_chunks":11}} +{"project":"knowledge","query":"tea-cli:10","run":"latest","turn":10,"event_type":"learn","data":{"chunk_index":10,"level":"L1","sha256":"23974031e10bd55fb41c2f4f5b3ab8a1b1b431d038d9d241e5ec7648af2ab54b","source":"knowledge/tea-cli.md","text":"## Useful Flags\n- `--repo owner/name` — specify repo (or use current git context)\n- `--output simple` — machine-readable output\n- `--output yaml` — YAML format\n- `--output json` — JSON format for piping to jq\n- `--limit N` — limit results\n- `--state open|closed|all` — filter by state\n- `--fields name,status` — select columns","total_chunks":11}} +{"project":"knowledge","query":"verify-done:0","run":"latest","turn":0,"event_type":"learn","data":{"chunk_index":0,"level":"L1","sha256":"f2350e4f9651681fadc58444daaf29d716ee1a4e43e284b4e42e3c122cfcb2fe","source":"knowledge/verify-done.md","text":"# Verify Done — Confirming Task Completion","total_chunks":10}} +{"project":"knowledge","query":"verify-done:1","run":"latest","turn":1,"event_type":"learn","data":{"chunk_index":1,"level":"L1","sha256":"605504294c3771888d3e1d8d66dca9e8fba9a101d51f9b0a0905ac354c68db00","source":"knowledge/verify-done.md","text":"## Principle\nA task is NOT done until it's verified with real data against the real service. Code review + CI green is necessary but not sufficient.","total_chunks":10}} +{"project":"knowledge","query":"verify-done:2","run":"latest","turn":2,"event_type":"learn","data":{"chunk_index":2,"level":"L1","sha256":"fe04a7472414971a4823f2dd5c3eda05ad3493955a7cc7f4c2a49f22d22e7865","source":"knowledge/verify-done.md","text":"## Definition of Done Checklist\n1. **Code compiles** — `cargo build` / `go build` passes with no errors\n2. **Tests pass** — `cargo test` / `go test ./...` all green\n3. **CI green** — `tea actions runs list` shows latest run succeeded\n4. **API accessible** — `curl` the endpoint, get expected response\n5. **Auth works** — requests without token return 401, with token return 200\n6. **Error paths tested** — bad input returns proper error codes\n7. **Deployed** — ArgoCD synced, pod running, ingress reachable\n8. **Documented** — endpoint added to API docs or CLAUDE.md","total_chunks":10}} +{"project":"knowledge","query":"verify-done:3","run":"latest","turn":3,"event_type":"learn","data":{"chunk_index":3,"level":"L1","sha256":"c382f438306e367545fea933072e8804f95d8b8ec8cb5511a9210a0081a7fff7","source":"knowledge/verify-done.md","text":"## Verification Flow\n```\nCode change → Push → CI passes → ArgoCD deploys → curl test → Done\n ↓\n If fails → fix → repeat\n```","total_chunks":10}} +{"project":"knowledge","query":"verify-done:4","run":"latest","turn":4,"event_type":"learn","data":{"chunk_index":4,"level":"L1","sha256":"a2c594aefc35b343153a14945c7c42a0cd8a86018c17bc7e854cd540a7d551fe","source":"knowledge/verify-done.md","text":"## API Endpoint Verification Template\nFor every new or changed endpoint, run:\n```bash\nENDPOINT=\"https://api.riotpiao.com\"\n\n# 1. Is it alive?\ncurl -s -o /dev/null -w \"%{http_code}\" $ENDPOINT/health\n# Expect: 200\n\n# 2. Does the new endpoint exist?\ncurl -s -o /dev/null -w \"%{http_code}\" $ENDPOINT/new-endpoint\n# Expect: NOT 404\n\n# 3. Does auth gate work?\ncurl -s -o /dev/null -w \"%{http_code}\" $ENDPOINT/new-endpoint\n# Expect: 401 (no token)\n\n# 4. Does it return correct data?\ncurl -s -H \"Authorization: Bearer $TOKEN\" $ENDPOINT/new-endpoint | jq .\n# Expect: meaningful JSON response\n\n# 5. Does it handle bad input?\ncurl -s -o /dev/null -w \"%{http_code}\" -X POST $ENDPOINT/new-endpoint \\\n -H \"Content-Type: application/json\" -d '{}'\n# Expect: 400\n```","total_chunks":10}} +{"project":"knowledge","query":"verify-done:5","run":"latest","turn":5,"event_type":"learn","data":{"chunk_index":5,"level":"L1","sha256":"04fc352fdec5bbd2998a83672301c81524725ed898f148f6347f0a1e93c69521","source":"knowledge/verify-done.md","text":"## CI Verification\n```bash\n# Check last CI run status\ntea actions runs list --repo rock/poimen-memory --limit 1\n\n# If failed, check logs\ntea actions runs view --repo rock/poimen-memory\n\n# Check if ArgoCD synced\nkubectl get application -n argocd poimen-memory-app -o jsonpath='{.status.sync.status}'\n# Expect: Synced\n\n# Check pod health\nkubectl get pods -n poimen | grep poimen-memory\n# Expect: Running, no restarts\n```","total_chunks":10}} +{"project":"knowledge","query":"verify-done:6","run":"latest","turn":6,"event_type":"learn","data":{"chunk_index":6,"level":"L1","sha256":"28fdf5743cfe78a0abdc83dc496fbc9eb49c5a066e2d8ef2340e45fe79872587","source":"knowledge/verify-done.md","text":"## Deployment Verification\n```bash\n# Pod running and ready\nkubectl get pods -n poimen -l app.kubernetes.io/name=poimen-memory\n# Expect: 1/1 Running\n\n# Service reachable internally\nkubectl exec -n poimen deploy/poimen-memory -- curl -s http://localhost:8080/health\n\n# Ingress reachable externally\ncurl -sk https://api.riotpiao.com/health\n\n# Logs clean (no panics, no errors on startup)\nkubectl logs -n poimen deploy/poimen-memory --tail=20\n```","total_chunks":10}} +{"project":"knowledge","query":"verify-done:7","run":"latest","turn":7,"event_type":"learn","data":{"chunk_index":7,"level":"L1","sha256":"dc3c71bd7455fddc2d271561e4d1e7f2f157c44b9e7156dd964117d863a7d8c0","source":"knowledge/verify-done.md","text":"## Git Commit Verification\nBefore pushing, confirm:\n```bash\n# Commit message follows conventional commits\ngit log -1 --oneline\n# Expect: feat: / fix: / docs: / refactor: prefix\n\n# No secrets in diff\ngit diff --cached | grep -iE \"password|secret|token|api_key\"\n# Expect: empty (no matches)\n\n# No .env or key files staged\ngit diff --cached --name-only | grep -iE \"\\.env|\\.key|\\.pem\"\n# Expect: empty\n```","total_chunks":10}} +{"project":"knowledge","query":"verify-done:8","run":"latest","turn":8,"event_type":"learn","data":{"chunk_index":8,"level":"L1","sha256":"3337996473ce8d78166fed6ef051b3af9d305be1510d249715772db3c5ad8430","source":"knowledge/verify-done.md","text":"## Memory System Specific Verification\n```bash\n# After ingest changes\ncurl -s -X POST https://api.riotpiao.com/memory/ingest \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"project\":\"test\",\"text\":\"verify ingest\",\"kind\":\"L1\"}' | jq .\n# Expect: 201 with chunk ID\n\n# After query changes\ncurl -s -X POST https://api.riotpiao.com/memory/query \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"project\":\"test\",\"query\":\"verify\"}' | jq .\n# Expect: 200 with results array\n\n# After context endpoint changes\ncurl -s -X POST https://api.riotpiao.com/memory/context \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"project\":\"test\",\"tool\":\"cargo\",\"task\":\"build\"}' | jq .\n# Expect: 200 with tier, lessons, budget\n\n# After rebuild changes\ncurl -s -X POST https://api.riotpiao.com/memory/rebuild \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"project\":\"test\",\"dry_run\":true}' | jq .\n# Expect: 200 with records_processed count\n```","total_chunks":10}} +{"project":"knowledge","query":"verify-done:9","run":"latest","turn":9,"event_type":"learn","data":{"chunk_index":9,"level":"L1","sha256":"2c19a95eba6e0f454324431f1ca416ddc6ce37b47e0227df14c718b3e5513fb8","source":"knowledge/verify-done.md","text":"## Anti-Patterns\n- ❌ \"CI passed so it's done\" — CI doesn't test the real deployment\n- ❌ \"It works on my machine\" — must work in-cluster\n- ❌ Marking done without curl-testing the endpoint\n- ❌ Skipping error path testing (400, 401, 404, 429)\n- ❌ Not checking ArgoCD sync status after push\n- ❌ Trusting `kubectl apply` over ArgoCD (let ArgoCD manage state)","total_chunks":10}}