- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
705 lines
18 KiB
Markdown
705 lines
18 KiB
Markdown
---
|
|
name: zero-script-qa
|
|
classification: workflow
|
|
classification-reason: Process automation persists regardless of model advancement
|
|
deprecation-risk: none
|
|
description: |
|
|
Zero Script QA — test without scripts using structured JSON logging and Docker monitoring.
|
|
Triggers: zero-script-qa, log testing, docker logs, QA, 제로 스크립트 QA.
|
|
context: fork
|
|
agent: hmcmkit:qa-monitor
|
|
user-invocable: true
|
|
hooks:
|
|
Stop:
|
|
- type: command
|
|
command: "node ${HMCMKIT_PLUGIN_ROOT}/scripts/qa-stop.js"
|
|
timeout: 10000
|
|
---
|
|
|
|
# Zero Script QA Expert Knowledge
|
|
|
|
## Overview
|
|
|
|
Zero Script QA is a methodology that verifies features through **structured logs** and **real-time monitoring** without writing test scripts.
|
|
|
|
```
|
|
Traditional: Write test code → Execute → Check results → Maintain
|
|
Zero Script: Build log infrastructure → Manual UX test → AI log analysis → Auto issue detection
|
|
```
|
|
|
|
## Core Principles
|
|
|
|
### 1. Log Everything
|
|
- All API calls (including 200 OK)
|
|
- All errors
|
|
- All important business events
|
|
- Entire flow trackable via Request ID
|
|
|
|
### 2. Structured JSON Logs
|
|
- Parseable JSON format
|
|
- Consistent fields (timestamp, level, request_id, message, data)
|
|
- Different log levels per environment
|
|
|
|
### 3. Real-time Monitoring
|
|
- Docker log streaming
|
|
- Antigravity Platform analyzes in real-time
|
|
- Immediate issue detection and documentation
|
|
|
|
---
|
|
|
|
## Logging Architecture
|
|
|
|
### JSON Log Format Standard
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2026-01-08T10:30:00.000Z",
|
|
"level": "INFO",
|
|
"service": "api",
|
|
"request_id": "req_abc123",
|
|
"message": "API Request completed",
|
|
"data": {
|
|
"method": "POST",
|
|
"path": "/api/users",
|
|
"status": 200,
|
|
"duration_ms": 45
|
|
}
|
|
}
|
|
```
|
|
|
|
### Required Log Fields
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| timestamp | ISO 8601 | Time of occurrence |
|
|
| level | string | DEBUG, INFO, WARNING, ERROR |
|
|
| service | string | Service name (api, web, worker, etc.) |
|
|
| request_id | string | Request tracking ID |
|
|
| message | string | Log message |
|
|
| data | object | Additional data (optional) |
|
|
|
|
### Log Level Policy
|
|
|
|
| Environment | Minimum Level | Purpose |
|
|
|-------------|---------------|---------|
|
|
| Local | DEBUG | Development and QA |
|
|
| Staging | DEBUG | QA and integration testing |
|
|
| Production | INFO | Operations monitoring |
|
|
|
|
---
|
|
|
|
## Request ID Propagation
|
|
|
|
### Concept
|
|
|
|
```
|
|
Client → API Gateway → Backend → Database
|
|
↓ ↓ ↓ ↓
|
|
req_abc req_abc req_abc req_abc
|
|
|
|
Trackable with same Request ID across all layers
|
|
```
|
|
|
|
### Implementation Patterns
|
|
|
|
#### 1. Request ID Generation (Entry Point)
|
|
```typescript
|
|
// middleware.ts
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
export function generateRequestId(): string {
|
|
return `req_${uuidv4().slice(0, 8)}`;
|
|
}
|
|
|
|
// Propagate via header
|
|
headers['X-Request-ID'] = requestId;
|
|
```
|
|
|
|
#### 2. Request ID Extraction and Propagation
|
|
```typescript
|
|
// API client
|
|
const requestId = headers['X-Request-ID'] || generateRequestId();
|
|
|
|
// Include in all logs
|
|
logger.info('Processing request', { request_id: requestId });
|
|
|
|
// Include in header when calling downstream services
|
|
await fetch(url, {
|
|
headers: { 'X-Request-ID': requestId }
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Backend Logging (FastAPI)
|
|
|
|
### Logging Middleware
|
|
|
|
```python
|
|
# middleware/logging.py
|
|
import logging
|
|
import time
|
|
import uuid
|
|
import json
|
|
from fastapi import Request
|
|
|
|
class JsonFormatter(logging.Formatter):
|
|
def format(self, record):
|
|
log_record = {
|
|
"timestamp": self.formatTime(record),
|
|
"level": record.levelname,
|
|
"service": "api",
|
|
"request_id": getattr(record, 'request_id', 'N/A'),
|
|
"message": record.getMessage(),
|
|
}
|
|
if hasattr(record, 'data'):
|
|
log_record["data"] = record.data
|
|
return json.dumps(log_record)
|
|
|
|
class LoggingMiddleware:
|
|
async def __call__(self, request: Request, call_next):
|
|
request_id = request.headers.get('X-Request-ID', f'req_{uuid.uuid4().hex[:8]}')
|
|
request.state.request_id = request_id
|
|
|
|
start_time = time.time()
|
|
|
|
# Request logging
|
|
logger.info(
|
|
f"Request started",
|
|
extra={
|
|
'request_id': request_id,
|
|
'data': {
|
|
'method': request.method,
|
|
'path': request.url.path,
|
|
'query': str(request.query_params)
|
|
}
|
|
}
|
|
)
|
|
|
|
response = await call_next(request)
|
|
|
|
duration = (time.time() - start_time) * 1000
|
|
|
|
# Response logging (including 200 OK!)
|
|
logger.info(
|
|
f"Request completed",
|
|
extra={
|
|
'request_id': request_id,
|
|
'data': {
|
|
'status': response.status_code,
|
|
'duration_ms': round(duration, 2)
|
|
}
|
|
}
|
|
)
|
|
|
|
response.headers['X-Request-ID'] = request_id
|
|
return response
|
|
```
|
|
|
|
### Business Logic Logging
|
|
|
|
```python
|
|
# services/user_service.py
|
|
def create_user(data: dict, request_id: str):
|
|
logger.info("Creating user", extra={
|
|
'request_id': request_id,
|
|
'data': {'email': data['email']}
|
|
})
|
|
|
|
# Business logic
|
|
user = User(**data)
|
|
db.add(user)
|
|
db.commit()
|
|
|
|
logger.info("User created", extra={
|
|
'request_id': request_id,
|
|
'data': {'user_id': user.id}
|
|
})
|
|
|
|
return user
|
|
```
|
|
|
|
---
|
|
|
|
## Frontend Logging (Next.js)
|
|
|
|
### Logger Module
|
|
|
|
```typescript
|
|
// lib/logger.ts
|
|
type LogLevel = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
|
|
|
|
interface LogData {
|
|
request_id?: string;
|
|
[key: string]: any;
|
|
}
|
|
|
|
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
DEBUG: 0,
|
|
INFO: 1,
|
|
WARNING: 2,
|
|
ERROR: 3,
|
|
};
|
|
|
|
const MIN_LEVEL = process.env.NODE_ENV === 'production' ? 'INFO' : 'DEBUG';
|
|
|
|
function log(level: LogLevel, message: string, data?: LogData) {
|
|
if (LOG_LEVELS[level] < LOG_LEVELS[MIN_LEVEL]) return;
|
|
|
|
const logEntry = {
|
|
timestamp: new Date().toISOString(),
|
|
level,
|
|
service: 'web',
|
|
request_id: data?.request_id || 'N/A',
|
|
message,
|
|
data: data ? { ...data, request_id: undefined } : undefined,
|
|
};
|
|
|
|
console.log(JSON.stringify(logEntry));
|
|
}
|
|
|
|
export const logger = {
|
|
debug: (msg: string, data?: LogData) => log('DEBUG', msg, data),
|
|
info: (msg: string, data?: LogData) => log('INFO', msg, data),
|
|
warning: (msg: string, data?: LogData) => log('WARNING', msg, data),
|
|
error: (msg: string, data?: LogData) => log('ERROR', msg, data),
|
|
};
|
|
```
|
|
|
|
### API Client Integration
|
|
|
|
```typescript
|
|
// lib/api-client.ts
|
|
import { logger } from './logger';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
export async function apiClient<T>(
|
|
endpoint: string,
|
|
options: RequestInit = {}
|
|
): Promise<T> {
|
|
const requestId = `req_${uuidv4().slice(0, 8)}`;
|
|
const startTime = Date.now();
|
|
|
|
logger.info('API Request started', {
|
|
request_id: requestId,
|
|
method: options.method || 'GET',
|
|
endpoint,
|
|
});
|
|
|
|
try {
|
|
const response = await fetch(`/api${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Request-ID': requestId,
|
|
...options.headers,
|
|
},
|
|
});
|
|
|
|
const duration = Date.now() - startTime;
|
|
const data = await response.json();
|
|
|
|
// Log 200 OK too!
|
|
logger.info('API Request completed', {
|
|
request_id: requestId,
|
|
status: response.status,
|
|
duration_ms: duration,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
logger.error('API Request failed', {
|
|
request_id: requestId,
|
|
status: response.status,
|
|
error: data.error,
|
|
});
|
|
throw new ApiError(data.error);
|
|
}
|
|
|
|
return data;
|
|
} catch (error) {
|
|
logger.error('API Request error', {
|
|
request_id: requestId,
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Nginx JSON Logging
|
|
|
|
### nginx.conf Configuration
|
|
|
|
```nginx
|
|
http {
|
|
log_format json_combined escape=json '{'
|
|
'"timestamp":"$time_iso8601",'
|
|
'"level":"INFO",'
|
|
'"service":"nginx",'
|
|
'"request_id":"$http_x_request_id",'
|
|
'"message":"HTTP Request",'
|
|
'"data":{'
|
|
'"remote_addr":"$remote_addr",'
|
|
'"method":"$request_method",'
|
|
'"uri":"$request_uri",'
|
|
'"status":$status,'
|
|
'"body_bytes_sent":$body_bytes_sent,'
|
|
'"request_time":$request_time,'
|
|
'"upstream_response_time":"$upstream_response_time",'
|
|
'"http_referer":"$http_referer",'
|
|
'"http_user_agent":"$http_user_agent"'
|
|
'}'
|
|
'}';
|
|
|
|
access_log /var/log/nginx/access.log json_combined;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Docker-Based QA Workflow
|
|
|
|
### docker-compose.yml Configuration
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
services:
|
|
api:
|
|
build: ./backend
|
|
environment:
|
|
- LOG_LEVEL=DEBUG
|
|
- LOG_FORMAT=json
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
web:
|
|
build: ./frontend
|
|
environment:
|
|
- NODE_ENV=development
|
|
depends_on:
|
|
- api
|
|
|
|
nginx:
|
|
image: nginx:alpine
|
|
volumes:
|
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
|
ports:
|
|
- "80:80"
|
|
depends_on:
|
|
- api
|
|
- web
|
|
```
|
|
|
|
### Real-time Log Monitoring
|
|
|
|
```bash
|
|
# Stream all service logs
|
|
docker compose logs -f
|
|
|
|
# Specific service only
|
|
docker compose logs -f api
|
|
|
|
# Filter errors only
|
|
docker compose logs -f | grep '"level":"ERROR"'
|
|
|
|
# Track specific Request ID
|
|
docker compose logs -f | grep 'req_abc123'
|
|
```
|
|
|
|
---
|
|
|
|
## QA Automation Workflow
|
|
|
|
### 1. Start Environment
|
|
```bash
|
|
# Start development environment
|
|
docker compose up -d
|
|
|
|
# Start log monitoring (Antigravity Platform monitors)
|
|
docker compose logs -f
|
|
```
|
|
|
|
### 2. Manual UX Testing
|
|
```
|
|
User tests actual features in browser:
|
|
1. Sign up attempt
|
|
2. Login attempt
|
|
3. Use core features
|
|
4. Test edge cases
|
|
```
|
|
|
|
### 3. Antigravity Platform Log Analysis
|
|
```
|
|
Antigravity Platform in real-time:
|
|
1. Monitor log stream
|
|
2. Detect error patterns
|
|
3. Detect abnormal response times
|
|
4. Track entire flow via Request ID
|
|
5. Auto-document issues
|
|
```
|
|
|
|
### 4. Issue Documentation
|
|
```markdown
|
|
# QA Issue Report
|
|
|
|
## Issues Found
|
|
|
|
### ISSUE-001: Insufficient error handling on login failure
|
|
- **Request ID**: req_abc123
|
|
- **Severity**: Medium
|
|
- **Reproduction path**: Login → Wrong password
|
|
- **Log**:
|
|
```json
|
|
{"level":"ERROR","message":"Login failed","data":{"error":"Invalid credentials"}}
|
|
```
|
|
- **Problem**: Error message not user-friendly
|
|
- **Recommended fix**: Add error code to message mapping
|
|
```
|
|
|
|
---
|
|
|
|
## Issue Detection Patterns
|
|
|
|
### 1. Error Detection
|
|
```json
|
|
{"level":"ERROR","message":"..."}
|
|
```
|
|
→ Report immediately
|
|
|
|
### 2. Slow Response Detection
|
|
```json
|
|
{"data":{"duration_ms":3000}}
|
|
```
|
|
→ Warning when exceeding 1000ms
|
|
|
|
### 3. Consecutive Failure Detection
|
|
```
|
|
3+ consecutive failures on same endpoint
|
|
```
|
|
→ Report potential system issue
|
|
|
|
### 4. Abnormal Status Codes
|
|
```json
|
|
{"data":{"status":500}}
|
|
```
|
|
→ Report 5xx errors immediately
|
|
|
|
---
|
|
|
|
## Phase Integration
|
|
|
|
| Phase | Zero Script QA Integration |
|
|
|-------|---------------------------|
|
|
| Phase 4 (API) | API response logging verification |
|
|
| Phase 6 (UI) | Frontend logging verification |
|
|
| Phase 7 (Security) | Security event logging verification |
|
|
| Phase 8 (Review) | Log quality review |
|
|
| Phase 9 (Deployment) | Production log level configuration |
|
|
|
|
---
|
|
|
|
## Iterative Test Cycle Pattern
|
|
|
|
Based on bkamp.ai notification feature development:
|
|
|
|
### Example: 8-Cycle Test Process
|
|
|
|
| Cycle | Pass Rate | Bug Found | Fix Applied |
|
|
|-------|-----------|-----------|-------------|
|
|
| 1st | 30% | DB schema mismatch | Schema migration |
|
|
| 2nd | 45% | NULL handling missing | Add null checks |
|
|
| 3rd | 55% | Routing error | Fix deeplinks |
|
|
| 4th | 65% | Type mismatch | Fix enum types |
|
|
| 5th | 70% | Calculation error | Fix count logic |
|
|
| 6th | 75% | Event missing | Add event triggers |
|
|
| 7th | 82% | Cache sync issue | Fix cache invalidation |
|
|
| 8th | **89%** | Stable | Final polish |
|
|
|
|
### Cycle Workflow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Iterative Test Cycle │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Cycle N: │
|
|
│ 1. Run test script (E2E or manual) │
|
|
│ 2. AI Agent monitors logs in real-time │
|
|
│ 3. Record pass/fail results │
|
|
│ 4. AI Agent identifies root cause of failures │
|
|
│ 5. Fix code immediately (hot reload) │
|
|
│ 6. Document: Cycle N → Bug → Fix │
|
|
│ │
|
|
│ Repeat until acceptable pass rate (>85%) │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### E2E Test Script Template
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# E2E Test Script Template
|
|
|
|
API_URL="http://localhost:8000"
|
|
TOKEN="your-test-token"
|
|
|
|
PASS_COUNT=0
|
|
FAIL_COUNT=0
|
|
SKIP_COUNT=0
|
|
|
|
GREEN='\033[0;32m'
|
|
RED='\033[0;31m'
|
|
YELLOW='\033[0;33m'
|
|
NC='\033[0m'
|
|
|
|
test_feature_action() {
|
|
echo -n "Testing: Feature action... "
|
|
|
|
response=$(curl -s -X POST "$API_URL/api/v1/feature/action" \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"param": "value"}')
|
|
|
|
if [[ "$response" == *"expected_result"* ]]; then
|
|
echo -e "${GREEN}✅ PASS${NC}"
|
|
((PASS_COUNT++))
|
|
else
|
|
echo -e "${RED}❌ FAIL${NC}"
|
|
echo "Response: $response"
|
|
((FAIL_COUNT++))
|
|
fi
|
|
}
|
|
|
|
# Run all tests
|
|
test_feature_action
|
|
# ... more tests
|
|
|
|
# Summary
|
|
echo ""
|
|
echo "═══════════════════════════════════════"
|
|
echo "Test Results:"
|
|
echo -e " ${GREEN}✅ PASS: $PASS_COUNT${NC}"
|
|
echo -e " ${RED}❌ FAIL: $FAIL_COUNT${NC}"
|
|
echo -e " ${YELLOW}⏭️ SKIP: $SKIP_COUNT${NC}"
|
|
echo "═══════════════════════════════════════"
|
|
```
|
|
|
|
### Test Cycle Documentation Template
|
|
|
|
```markdown
|
|
# Feature Test Results - Cycle N
|
|
|
|
## Summary
|
|
- **Date**: YYYY-MM-DD
|
|
- **Feature**: {feature name}
|
|
- **Pass Rate**: N%
|
|
- **Tests**: X passed / Y total
|
|
|
|
## Results
|
|
|
|
| Test Case | Status | Notes |
|
|
|-----------|--------|-------|
|
|
| Test 1 | ✅ | |
|
|
| Test 2 | ❌ | {error description} |
|
|
| Test 3 | ⏭️ | {skip reason} |
|
|
|
|
## Bugs Found
|
|
|
|
### BUG-001: {Title}
|
|
- **Root Cause**: {description}
|
|
- **Fix**: {what was changed}
|
|
- **Files**: `path/to/file.py:123`
|
|
|
|
## Next Cycle Plan
|
|
- {what to test next}
|
|
```
|
|
|
|
---
|
|
|
|
## Checklist
|
|
|
|
### Logging Infrastructure
|
|
- [ ] JSON log format applied
|
|
- [ ] Request ID generation and propagation
|
|
- [ ] Log level settings per environment
|
|
- [ ] Docker logging configuration
|
|
|
|
### Backend Logging
|
|
- [ ] Logging middleware implemented
|
|
- [ ] All API calls logged (including 200 OK)
|
|
- [ ] Business logic logging
|
|
- [ ] Detailed error logging
|
|
|
|
### Frontend Logging
|
|
- [ ] Logger module implemented
|
|
- [ ] API client integration
|
|
- [ ] Error boundary logging
|
|
|
|
### QA Workflow
|
|
- [ ] Docker Compose configured
|
|
- [ ] Real-time monitoring ready
|
|
- [ ] Issue documentation template ready
|
|
|
|
---
|
|
|
|
## Auto-Apply Rules
|
|
|
|
### When Building Logging Infrastructure
|
|
|
|
When implementing API/Backend:
|
|
1. Suggest logging middleware creation
|
|
2. Suggest JSON format logger setup
|
|
3. Add Request ID generation/propagation logic
|
|
|
|
When implementing Frontend:
|
|
1. Suggest Logger module creation
|
|
2. Suggest logging integration with API client
|
|
3. Suggest including Request ID header
|
|
|
|
### When Performing QA
|
|
|
|
On test request:
|
|
1. Guide to run `docker compose logs -f`
|
|
2. Request manual UX testing from user
|
|
3. Real-time log monitoring
|
|
4. Document issues immediately when detected
|
|
5. Provide fix suggestions
|
|
|
|
### Issue Detection Thresholds
|
|
|
|
| Severity | Condition | Action |
|
|
|----------|-----------|--------|
|
|
| Critical | `level: ERROR` or `status: 5xx` | Immediate report |
|
|
| Critical | `duration_ms > 3000` | Immediate report |
|
|
| Critical | 3+ consecutive failures | Immediate report |
|
|
| Warning | `status: 401, 403` | Warning report |
|
|
| Warning | `duration_ms > 1000` | Warning report |
|
|
| Info | Missing log fields | Note for improvement |
|
|
| Info | Request ID not propagated | Note for improvement |
|
|
|
|
### Required Logging Locations
|
|
|
|
#### Backend (FastAPI/Express)
|
|
```
|
|
✅ Request start (method, path, params)
|
|
✅ Request complete (status, duration_ms)
|
|
✅ Major business logic steps
|
|
✅ Detailed info on errors
|
|
✅ Before/after external API calls
|
|
✅ DB queries (in development)
|
|
```
|
|
|
|
#### Frontend (Next.js/React)
|
|
```
|
|
✅ API call start
|
|
✅ API response received (status, duration)
|
|
✅ Detailed info on errors
|
|
✅ Important user actions
|
|
```
|