update the repo
This commit is contained in:
19
AGENT_SKILL/INTRODUCTION.md
Normal file
19
AGENT_SKILL/INTRODUCTION.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# VKIST MSK Pilot — Shared Agent Enforcement Skills
|
||||
|
||||
## Goal
|
||||
Create up to 5 shareable agent skills under `PILOT_PROJECT/AGENT_SKILL/` that automatically guide coding agents (Claude Code, Gemini CLI, KILO) to follow project conventions from `README.md` and architecture specs.
|
||||
|
||||
## How to add a new skill?
|
||||
1. Create a new folder under AGENT_SKILL/<skill_name>/
|
||||
2. Add SKILL.md, EXAMPLES.md, CHECKLIST.md
|
||||
3. Update this INTRODUCTION.md index
|
||||
4. Open a PR with at least one EXAMPLES.md entry per rule
|
||||
|
||||
## Skills Index
|
||||
| Skill | Covers |
|
||||
|-------|--------|
|
||||
| file_naming_convention | File/dir names, forbidden extensions, LEGACY/ policy |
|
||||
| coding_convention | Python PEP8, TS Airbnb, docstrings, error handling, RBAC |
|
||||
| interface_contract_hygiene | Contract files, versioning, breaking-change policy |
|
||||
| secrets_and_phi_safety | Secrets loading, PHI exclusion, CI safety, gitignore |
|
||||
| design_pattern_compliance | Layered arch, DI, Zustand, Terraform IaC, pinning |
|
||||
45
AGENT_SKILL/coding_convention/CHECKLIST.md
Normal file
45
AGENT_SKILL/coding_convention/CHECKLIST.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Checklist: coding_convention
|
||||
|
||||
Instructions: For each item below, mark whether the change complies with the rule.
|
||||
|
||||
## Python Conventions
|
||||
- [ ] File names use snake_case (e.g., `patient_service.py`)
|
||||
- [ ] Class names use PascalCase (e.g., `PatientService`)
|
||||
- [ ] Function and variable names use snake_case (e.g., `get_patient_by_id`)
|
||||
- [ ] Constants use UPPER_SNAKE_CASE (e.g., `MAX_PATIENTS_PER_PAGE`)
|
||||
- [ ] Line length does not exceed 100 characters
|
||||
- [ ] Every public function, class, and module has a docstring
|
||||
- [ ] Docstrings follow Google/NumPy style (Args, Returns, Side Effects)
|
||||
- [ ] No `print()` statements; logging is used instead
|
||||
- [ ] Exceptions are typed and not overly broad (avoid bare `except:`)
|
||||
- [ ] FastAPI routers use `APIRouter(prefix=...)`
|
||||
|
||||
## TypeScript/React Conventions
|
||||
- [ ] File names use snake_case (e.g., `patient_list.tsx`)
|
||||
- [ ] Component names use PascalCase (e.g., `PatientList`)
|
||||
- [ ] Hook names use `useSnakeCase` (e.g., `usePatientData`)
|
||||
- [ ] Follows Airbnb JavaScript/TypeScript style guide
|
||||
- [ ] No `console.log()` in production code (use proper logging)
|
||||
|
||||
## Error Handling
|
||||
- [ ] No `print()` or `console.log()` for error reporting
|
||||
- [ ] Errors are logged at appropriate level (debug/info/warn/error)
|
||||
- [ ] Custom exceptions are used where appropriate
|
||||
- [ ] Exceptions are not swallowed without handling
|
||||
|
||||
## RBAC
|
||||
- [ ] Only use role constants: `RO_RADIOLOGIST`, `RO_THERAPIST`
|
||||
- [ ] No hard-coded role strings in conditionals
|
||||
- [ ] Role checks are centralized where possible
|
||||
|
||||
## Configuration
|
||||
- [ ] No hard-coded endpoints, API keys, or configuration values
|
||||
- [ ] Configuration loaded via environment variables or secrets/
|
||||
- [ ] `secrets/` directory is properly gitignored
|
||||
- [ ] No secrets in code, logs, or comments
|
||||
|
||||
## Commit Messages
|
||||
- [ ] Commit message follows Conventional Commits format
|
||||
- [ ] Type is one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
|
||||
- [ ] Scope is provided when applicable
|
||||
- [ ] Subject is imperative, lowercase, no trailing period
|
||||
139
AGENT_SKILL/coding_convention/EXAMPLES.md
Normal file
139
AGENT_SKILL/coding_convention/EXAMPLES.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Examples: coding_convention
|
||||
|
||||
## Correct Examples
|
||||
|
||||
### Python
|
||||
```python
|
||||
"""
|
||||
Module: patient_service
|
||||
Purpose: Handle patient-related business logic.
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from models.patient import PatientModel
|
||||
from services.database import DatabaseService
|
||||
|
||||
router = APIRouter(prefix="/patients", tags=["patients"])
|
||||
|
||||
|
||||
class PatientService:
|
||||
"""Service for managing patient records.
|
||||
|
||||
Attributes:
|
||||
db: Database service instance
|
||||
"""
|
||||
|
||||
def __init__(self, db: DatabaseService):
|
||||
self.db = db
|
||||
|
||||
def get_patient_by_id(self, patient_id: str) -> Optional[PatientModel]:
|
||||
"""Retrieve a patient by their ID.
|
||||
|
||||
Args:
|
||||
patient_id: Unique identifier for the patient
|
||||
|
||||
Returns:
|
||||
PatientModel if found, None otherwise
|
||||
|
||||
Side Effects:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
return self.db.get_patient(patient_id)
|
||||
except DatabaseError as e:
|
||||
logger.error(f"Failed to retrieve patient {patient_id}: {e}")
|
||||
raise
|
||||
```
|
||||
|
||||
### TypeScript/React
|
||||
```typescript
|
||||
import { create } from 'zustand';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
// Hook name follows useSnakeCase
|
||||
const usePatientStore = create<PatientState>((set, get) => ({
|
||||
patients: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
|
||||
fetchPatients: async () => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const response = await api.getPatients();
|
||||
set({ patients: response.data, loading: false });
|
||||
} catch (err) {
|
||||
set({ error: err.message, loading: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export const PatientList: React.FC = () => {
|
||||
const { patients, loading, error } = usePatientStore();
|
||||
|
||||
// Component name: PascalCase
|
||||
if (loading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error}</div>;
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{patients.map(patient => (
|
||||
<li key={patient.id}>{patient.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
```python
|
||||
# Good: Using logging instead of print
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def process_data(data: dict) -> dict:
|
||||
try:
|
||||
# Process data
|
||||
result = transform_data(data)
|
||||
return result
|
||||
except ValidationError as e:
|
||||
logger.warning(f"Validation failed: {e}")
|
||||
raise InvalidInputError(str(e)) from e
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error in process_data: {e}")
|
||||
raise
|
||||
```
|
||||
|
||||
### RBAC
|
||||
```python
|
||||
# Good: Using role constants
|
||||
from auth.roles import RO_RADIOLOGIST, RO_THERAPIST
|
||||
|
||||
def can_read_patient_records(user_role: str) -> bool:
|
||||
return user_role in [RO_RADIOLOGIST, RO_THERAPIST]
|
||||
|
||||
# Bad: Hard-coded role strings
|
||||
def can_read_patient_records_bad(user_role: str) -> bool:
|
||||
return user_role in ["radiologist", "therapist"] # VIOLATION
|
||||
```
|
||||
|
||||
### Configuration
|
||||
```python
|
||||
# Good: Loading from environment
|
||||
import os
|
||||
|
||||
API_BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:8000")
|
||||
MAX_UPLOAD_SIZE = int(os.environ.get("MAX_UPLOAD_SIZE", "10485760")) # 10MB
|
||||
|
||||
# Bad: Hard-coded values
|
||||
API_BASE_URL = "http://localhost:8000" # VIOLATION
|
||||
```
|
||||
|
||||
### Commit Messages
|
||||
```
|
||||
feat(auth): add JWT refresh token endpoint
|
||||
fix(patient-api): fix patient ID validation typo
|
||||
docs(api): update patient endpoint documentation
|
||||
refactor(core): extract database connection logic
|
||||
```
|
||||
59
AGENT_SKILL/coding_convention/SKILL.md
Normal file
59
AGENT_SKILL/coding_convention/SKILL.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Skill: coding_convention
|
||||
|
||||
## Trigger Conditions
|
||||
Any code edit or creation (files with extensions: .py, .js, .ts, .tsx, .java, .cpp, .h, etc.)
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. Python Conventions
|
||||
- Follow PEP 8 with line length ≤ 100 characters
|
||||
- Use `ruff`-compatible formatting
|
||||
- Class names: `PascalCase`
|
||||
- Function and variable names: `snake_case`
|
||||
- Constants: `UPPER_SNAKE_CASE`
|
||||
- FastAPI routers should be grouped under `APIRouter(prefix=...)`
|
||||
- Every public function, class, and module must have a docstring:
|
||||
- Purpose: brief description
|
||||
- Parameters: list with types and descriptions
|
||||
- Return: return type and description
|
||||
- Side-effects: note any side effects (if applicable)
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 2. TypeScript/JavaScript Conventions
|
||||
- Follow Airbnb JavaScript Style Guide
|
||||
- Use ESLint with `eslint-config-airbnb` as base
|
||||
- Component names: `PascalCase`
|
||||
- Hook names: `useSnakeCase` (e.g., `useUserData`)
|
||||
- File names: `snake_case.tsx` or `snake_case.ts`
|
||||
- Use TypeScript strict mode
|
||||
- Prefer interfaces over types for object shapes
|
||||
- Export constants as `const` in UPPER_SNAKE_CASE
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 3. Error Handling
|
||||
- Never use `print()` for logging or debugging
|
||||
- Use appropriate logging levels: `debug`, `info`, `warn`, `error`
|
||||
- Raise typed exceptions (custom exceptions preferred over generic Exception)
|
||||
- Handle exceptions at appropriate levels; do not swallow exceptions silently
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 4. RBAC (Role-Based Access Control)
|
||||
- Use only declared role constants for permission checks:
|
||||
- `RO_RADIOLOGIST` (Read-Only Radiologist)
|
||||
- `RO_THERAPIST` (Read-Only Therapist)
|
||||
- Do not hard-code role strings in conditionals
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 5. Configuration Management
|
||||
- No hard-coded endpoints, API keys, or configuration values
|
||||
- Load configuration from environment variables via `os.environ.get()` (Python) or `process.env` (Node.js)
|
||||
- Secrets must be loaded from the `secrets/` directory or environment variables only
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 6. Commit Messages
|
||||
- Follow Conventional Commits format: `<type>(<scope>): <subject>`
|
||||
- Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
|
||||
- Scope should be the module or component affected (optional but recommended)
|
||||
- Subject should be imperative, lowercase, no trailing period
|
||||
- Body and footer optional but recommended for complex changes
|
||||
- Severity: `[WARN]`
|
||||
51
AGENT_SKILL/design_pattern_compliance/CHECKLIST.md
Normal file
51
AGENT_SKILL/design_pattern_compliance/CHECKLIST.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Checklist: design_pattern_compliance
|
||||
|
||||
## Layered Architecture (Backend)
|
||||
- [ ] No direct calls from presentation layer to data access layer
|
||||
- [ ] Controllers only handle HTTP concerns (validation, serialization, routing)
|
||||
- [ ] Business logic resides in service layer
|
||||
- [ ] Data access layer handles only persistence concerns
|
||||
- [ ] No upward dependencies (data layer does not depend on service layer)
|
||||
|
||||
## Dependency Injection
|
||||
- [ ] No direct instantiation of infrastructure dependencies (e.g., `new MongoClient()`)
|
||||
- [ ] Dependencies are injected (constructor, setter, or framework DI)
|
||||
- [ ] Classes depend on interfaces/protocols, not concrete implementations
|
||||
- [ ] Framework-appropriate DI mechanisms are used (e.g., FastAPI Depends, Spring @Autowired)
|
||||
|
||||
## Interface-Driven Development
|
||||
- [ ] Business logic depends on abstractions (interfaces/repositories)
|
||||
- [ ] No direct infrastructure calls in business logic (no `boto3`, `psycopg2.connect()`, etc. in services)
|
||||
- [ ] Concrete implementations exist for interfaces but are injected, not instantiated internally
|
||||
|
||||
## Frontend State Management (Zustand)
|
||||
- [ ] Global shared state is managed in Zustand stores
|
||||
- [ ] Component state (`useState`) is used only for:
|
||||
- Form inputs and UI controls
|
||||
- Temporary UI state (loading states for local effects, animation states)
|
||||
- Data that is truly isolated to a single component
|
||||
- [ ] Store follows naming convention: `use[Domain]Store`
|
||||
- [ ] Store actions are named as verbs
|
||||
- [ ] No business logic in components that should be in store actions
|
||||
|
||||
## Infrastructure as Code
|
||||
- [ ] Terraform configuration uses a remote backend (GCS, S3, etc.)
|
||||
- [ ] No local `terraform.tfstate` files committed
|
||||
- [ ] Resources are named with environment identifier (e.g., `${var.environment}`)
|
||||
- [ ] IAM roles follow least privilege principle (specific permissions, no wildcards)
|
||||
- [ ] Separate configurations or workspaces for different environments
|
||||
- [ ] All providers and modules have explicit version pins
|
||||
- [ ] Terraform files are formatted (`terraform fmt`)
|
||||
|
||||
## Dependency Pinning
|
||||
- [ ] Python: `requirements.txt` or `pyproject.toml` contains exact versions (no `~`, `>`, `<` without corresponding lock file)
|
||||
- [ ] Python: If using `pyproject.toml` with poetry or pipenv, lock file (`poetry.lock` or `Pipfile.lock`) is present
|
||||
- [ ] Node.js: `package-lock.json` or `yarn.lock` is present and matches `package.json`
|
||||
- [ ] Other languages: Equivalent lock files are present and committed
|
||||
- [ ] No unpinned dependencies that could cause non-reproducible builds
|
||||
|
||||
## Code Quality
|
||||
- [ ] Duplicated code has been considered for extraction
|
||||
- [ ] Preference given to composition over inheritance where applicable
|
||||
- [ ] Utility functions are placed in appropriate shared locations
|
||||
- [ ] No "God classes" or objects with too many responsibilities
|
||||
363
AGENT_SKILL/design_pattern_compliance/EXAMPLES.md
Normal file
363
AGENT_SKILL/design_pattern_compliance/EXAMPLES.md
Normal file
@@ -0,0 +1,363 @@
|
||||
# Examples: design_pattern_compliance
|
||||
|
||||
## Correct Examples
|
||||
|
||||
### Layered Architecture (Python/FastAPI)
|
||||
```python
|
||||
# GOOD: Proper layer separation
|
||||
# presentation layer (api/patients.py)
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from services.patient_service import PatientService
|
||||
from models.patient import PatientResponse
|
||||
|
||||
router = APIRouter(prefix="/patients", tags=["patients"])
|
||||
|
||||
def get_patient_service() -> PatientService:
|
||||
# Dependency injection - could come from a DI container
|
||||
return PatientService(
|
||||
repository=PatientRepository() # In practice, this would be injected
|
||||
)
|
||||
|
||||
@router.get("/{patient_id}", response_model=PatientResponse)
|
||||
def get_patient(
|
||||
patient_id: str,
|
||||
service: PatientService = Depends(get_patient_service)
|
||||
):
|
||||
"""Controller handles only HTTP concerns."""
|
||||
patient = service.get_patient_by_id(patient_id)
|
||||
if not patient:
|
||||
raise HTTPException(status_code=404, detail="Patient not found")
|
||||
return patient
|
||||
|
||||
|
||||
# service layer (services/patient_service.py)
|
||||
from repositories.patient_repository import PatientRepository
|
||||
from models.patient import PatientModel
|
||||
|
||||
class PatientService:
|
||||
def __init__(self, repository: PatientRepository):
|
||||
self.repository = repository # Dependency injected
|
||||
|
||||
def get_patient_by_id(self, patient_id: str) -> Optional[PatientModel]:
|
||||
# Business logic lives here
|
||||
return self.repository.get_by_id(patient_id)
|
||||
|
||||
|
||||
# data access layer (repositories/patient_repository.py)
|
||||
from models.patient import PatientModel
|
||||
|
||||
class PatientRepository:
|
||||
def get_by_id(self, patient_id: str) -> Optional[PatientModel]:
|
||||
# Data access concerns only
|
||||
# In real implementation, this would query a database
|
||||
pass
|
||||
```
|
||||
|
||||
### Dependency Injection with Interfaces
|
||||
```python
|
||||
# GOOD: Interface-based dependencies
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Protocol
|
||||
|
||||
# Define interface (protocol)
|
||||
class PatientRepository(Protocol):
|
||||
def get_by_id(self, patient_id: str) -> Optional[PatientModel]: ...
|
||||
def save(self, patient: PatientModel) -> bool: ...
|
||||
|
||||
# Concrete implementation
|
||||
class PostgreSQLPatientRepository:
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
|
||||
def get_by_id(self, patient_id: str) -> Optional[PatientModel]:
|
||||
# Implementation details
|
||||
pass
|
||||
|
||||
# Service depends on interface, not concrete class
|
||||
class PatientService:
|
||||
def __init__(self, repository: PatientRepository): # Accepts any implementation
|
||||
self.repository = repository
|
||||
|
||||
def get_patient(self, patient_id: str) -> Optional[PatientModel]:
|
||||
return self.repository.get_by_id(patient_id)
|
||||
|
||||
# Usage with dependency injection
|
||||
def get_patient_service() -> PatientService:
|
||||
repo = PostgreSQLPatientRepository(os.getenv("DB_CONNECTION"))
|
||||
return PatientService(repository)
|
||||
```
|
||||
|
||||
### Frontend Zustand Store
|
||||
```javascript
|
||||
// GOOD: Proper Zustand store usage
|
||||
import { create } from 'zustand';
|
||||
|
||||
// Patient store slice
|
||||
const usePatientStore = create((set, get) => ({
|
||||
// State
|
||||
patients: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
|
||||
// Actions
|
||||
setPatients: (patients) => set({ patients }),
|
||||
fetchPatients: async () => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const response = await api.getPatients();
|
||||
set({ patients: response.data, loading: false });
|
||||
} catch (err) {
|
||||
set({ error: err.message, loading: false });
|
||||
}
|
||||
},
|
||||
addPatient: (patient) => set((state) => ({
|
||||
patients: [...state.patients, patient],
|
||||
loading: false,
|
||||
error: null
|
||||
})),
|
||||
}));
|
||||
|
||||
// Component using store - ONLY for UI state
|
||||
import { useState } from 'react';
|
||||
import { usePatientStore } from '../store/patientStore';
|
||||
|
||||
export const PatientForm: React.FC = () => {
|
||||
// LOCAL UI state only - correct use of useState
|
||||
const [name, setName] = useState('');
|
||||
const [dob, setDob] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
// GLOBAL state from store
|
||||
const { patients, loading, error, addPatient } = usePatientStore();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
try {
|
||||
await addPatient({ id: Date.now(), name, dob });
|
||||
setName('');
|
||||
setDob('');
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* Form fields using local state */}
|
||||
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" />
|
||||
<input value={dob} onChange={(e) => setDob(e.target.value)} placeholder="Date of Birth" />
|
||||
<button type="submit" disabled={submitting}>
|
||||
{submitting ? 'Adding...' : 'Add Patient'}
|
||||
</button>
|
||||
|
||||
{/* Displaying global state */}
|
||||
{loading && <p>Saving...</p>}
|
||||
{error && <p>Error: {error}</p>}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Infrastructure as Code (Terraform)
|
||||
```hcl
|
||||
# GOOD: Proper Terraform practices
|
||||
terraform {
|
||||
# REQUIRED: Remote backend
|
||||
backend "gcs" {
|
||||
bucket = "vkist-terraform-state"
|
||||
prefix = "env/${var.environment}"
|
||||
}
|
||||
|
||||
# REQUIRED: Versioned providers
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "~> 4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Deployment environment"
|
||||
type = string
|
||||
validation {
|
||||
condition = ["dev", "staging", "prod"].contains(var.upper)
|
||||
error_message = "Environment must be either dev, staging, or prod."
|
||||
}
|
||||
}
|
||||
|
||||
# RESOURCE NAMING with environment
|
||||
resource "google_compute_instance" "vm" {
|
||||
name = "vkist-${var.environment}-backend"
|
||||
machine_type = "e2-medium"
|
||||
|
||||
# ... other configuration
|
||||
}
|
||||
|
||||
# LEAST PRIVILEGE IAM
|
||||
resource "google_service_account" "backend" {
|
||||
account_id = "vkist-backend-${var.environment}"
|
||||
display_name = "VKIST Backend Service Account"
|
||||
}
|
||||
|
||||
# Grant only necessary roles
|
||||
resource "google_project_iam_member" "storage_reader" {
|
||||
role = "roles/storage.objectViewer"
|
||||
member = "serviceAccount:${google_service_account.backend.email}"
|
||||
}
|
||||
|
||||
# VERSIONED MODULES
|
||||
module "network" {
|
||||
source = "terraform-google-modules/network/google"
|
||||
version = "~> 5.0"
|
||||
|
||||
project_id = var.project_id
|
||||
network_name = "vkist-${var.environment}-net"
|
||||
# ... other parameters
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency Pinning
|
||||
```txt
|
||||
# GOOD: requirements.txt with exact versions
|
||||
fastapi==0.104.1
|
||||
uvicorn[standard]==0.24.0
|
||||
pydantic==2.5.0
|
||||
sqlalchemy==2.0.23
|
||||
alembic==1.13.1
|
||||
python-multipart==0.0.6
|
||||
```
|
||||
|
||||
```json
|
||||
// GOOD: package.json with package-lock.json
|
||||
{
|
||||
"name": "vkist-frontend",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"zustand": "^4.4.1",
|
||||
"axios": "^1.6.2"
|
||||
}
|
||||
}
|
||||
// package-lock.json ensures exact versions are installed
|
||||
```
|
||||
|
||||
## Incorrect Examples (to avoid)
|
||||
|
||||
### Layer Violation
|
||||
```python
|
||||
# BAD: Controller directly accessing repository (skipping service layer)
|
||||
from fastapi import APIRouter, Depends
|
||||
from repositories.patient_repository import PatientRepository
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/patients/{id}")
|
||||
def get_patient(patient_id: str, repo: PatientRepository = Depends()):
|
||||
# VIOLATION: Presentation layer accessing data layer directly
|
||||
return repo.get_by_id(patient_id)
|
||||
```
|
||||
|
||||
### Direct Infrastructure Instantiation
|
||||
```python
|
||||
# BAD: Service creating its own database connection
|
||||
import psycopg2
|
||||
import os
|
||||
|
||||
class PatientService:
|
||||
def __init__(self):
|
||||
# VIOLATION: Direct instantiation of infrastructure
|
||||
self.conn = psycopg2.connect(
|
||||
host=os.getenv("DB_HOST"),
|
||||
database=os.getenv("DB_NAME"),
|
||||
user=os.getenv("DB_USER"),
|
||||
password=os.getenv("DB_PASSWORD")
|
||||
)
|
||||
|
||||
def get_patient(self, patient_id: str):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("SELECT * FROM patients WHERE id = %s", (patient_id,))
|
||||
return cursor.fetchone()
|
||||
```
|
||||
|
||||
### Frontend Anti-pattern: State in Components
|
||||
```javascript
|
||||
// BAD: Using useState for shared data that should be in store
|
||||
import { useState, useEffect } from 'react';
|
||||
import { fetchPatients } from '../api/patientApi';
|
||||
|
||||
export const PatientList: React.FC = () => {
|
||||
// WRONG: This state is shared and should be in a store
|
||||
const [patients, setPatients] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadPatients = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await fetchPatients();
|
||||
setPatients(data); // This state might be needed by other components
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadPatients();
|
||||
}, []);
|
||||
|
||||
// ... render logic
|
||||
};
|
||||
|
||||
// If another component needs patients data, it would have to fetch again
|
||||
// or receive it via props - inefficient and not scalable
|
||||
```
|
||||
|
||||
### Infrastructure Mistakes
|
||||
```hcl
|
||||
# BAD: Local state storage (should be remote)
|
||||
terraform {
|
||||
# MISSING: backend configuration
|
||||
# state will be stored locally - VIOLATION
|
||||
}
|
||||
|
||||
# BAD: Hard-coded environment (should be variable)
|
||||
resource "google_compute_network" "vpc" {
|
||||
name = "vkist-prod-network" # What if we want to deploy to staging?
|
||||
}
|
||||
|
||||
# BAD: Overly permissive IAM
|
||||
resource "google_project_iam_member" "admin" {
|
||||
role = "roles/editor" # Too permissive - VIOLATION of least privilege
|
||||
member = "user:admin@example.com"
|
||||
}
|
||||
|
||||
# BAD: Unpinned module version
|
||||
module "network" {
|
||||
source = "terraform-google-modules/network/google"
|
||||
# MISSING: version parameter - could get breaking changes on next run
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency Issues
|
||||
```txt
|
||||
# BAD: Unpinned dependencies in requirements.txt
|
||||
fastapi
|
||||
uvicorn
|
||||
pydantic
|
||||
# Any of these could break on next pip install
|
||||
```
|
||||
|
||||
```json
|
||||
// BAD: Missing or outdated lock file
|
||||
{
|
||||
"name": "my-app",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21"
|
||||
// No package-lock.json - versions not locked
|
||||
}
|
||||
}
|
||||
```
|
||||
96
AGENT_SKILL/design_pattern_compliance/SKILL.md
Normal file
96
AGENT_SKILL/design_pattern_compliance/SKILL.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Skill: design_pattern_compliance
|
||||
|
||||
## Trigger Conditions
|
||||
- Creation of a new class, function, or module
|
||||
- Significant refactoring of existing code (architectural changes)
|
||||
- Adding new infrastructure (Terraform modules)
|
||||
- Creating new state slices
|
||||
)
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. Layered Architecture (Backend)
|
||||
- **Direction of Dependencies**:
|
||||
- Presentation layer (API controllers) → Service layer → Data Access layer
|
||||
- No skipping layers (e.g., controller directly accessing repository)
|
||||
- No upward dependencies (data layer should not depend on service layer)
|
||||
- **Separation of Concerns**:
|
||||
- Controllers handle HTTP concerns only (validation, serialization, routing)
|
||||
- Services contain business logic and orchestration
|
||||
- Data Access/Repositories handle persistence concerns only
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 2. Dependency Injection and Inversion of Control
|
||||
- **No Direct Instantiation**:
|
||||
- Classes should not instantiate their dependencies directly (especially infrastructure clients)
|
||||
- Dependencies should be injected via constructor or setter
|
||||
- **Interface-Based Dependencies**:
|
||||
- Depend on abstractions (interfaces/protocols) rather than concrete implementations
|
||||
- Example: Service depends on `DatabaseRepository` interface, not `PostgreSQLRepository` directly
|
||||
- **Framework-Compatible DI**:
|
||||
- Use framework-provided dependency injection (e.g., FastAPI's `Depends`, Spring `@Autowired`)
|
||||
- Avoid service locator pattern
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 3. Interface-Driven Development
|
||||
- **Program to Interfaces**:
|
||||
- Concrete classes should implement defined interfaces
|
||||
- Clients should depend on interfaces, not implementations
|
||||
- **No Concrete Infra in Business Logic**:
|
||||
- Business logic should not contain direct calls to infrastructure (e.g., `boto3.client()`, `psycopg2.connect()`)
|
||||
- Infrastructure access should be abstracted behind repository/service interfaces
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 4. Frontend State Management (Zustand)
|
||||
- **Store Slices**:
|
||||
- Global state should be split into logical slices using Zustand
|
||||
- Each slice corresponds to a domain (e.g., `usePatientStore`, `useAppointmentStore`)
|
||||
- **No Inline State for Shared Data**:
|
||||
- Component state (`useState`) should only be used for:
|
||||
- UI-specific state (form inputs, toggles, animation states)
|
||||
- Data that is not shared across components
|
||||
- Any data that is shared across components or needed by multiple hooks should be in a store
|
||||
- **Store Structure**:
|
||||
- Follow consistent naming: `use[Domain]Store`
|
||||
- Actions should be named as verbs (e.g., `setPatients`, `fetchAppointments`)
|
||||
- Selectors should be derived and memoized where appropriate
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 5. Infrastructure as Code (IaC) Best Practices
|
||||
- **Terraform State**:
|
||||
- All Terraform configurations must use a remote backend (GCS recommended for this project)
|
||||
- No local `terraform.tfstate` files in the repository
|
||||
- **Resource Naming**:
|
||||
- Use consistent naming conventions that include environment (e.g., `vkist-${var.environment}-service`)
|
||||
- **Least Privilege IAM**:
|
||||
- IAM roles and policies must grant only the permissions necessary
|
||||
- Avoid using wildcard permissions (`*`) or overly broad roles
|
||||
- **Environment Separation**:
|
||||
- Use separate workspaces or directories for different environments (e.g., `env/staging`, `env/prod`)
|
||||
- Do not use the same resources for different environments
|
||||
- **Version Pinning**:
|
||||
- All external dependencies must be version-pinned:
|
||||
- Terraform modules: `source = "terraform-google-modules/network/google//version"`
|
||||
- Provider versions explicitly set
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 6. Dependency Pinning
|
||||
- **Python**:
|
||||
- `requirements.txt` or `pyproject.toml` must contain exact versions (no wildcards)
|
||||
- Use `pip freeze > requirements.txt` for deterministic builds
|
||||
- **Node.js**:
|
||||
- `package-lock.json` or `yarn.lock` must be committed and up to date
|
||||
- Dependencies in `package.json` should use caret (`^`) or tilde (`~`) ranges appropriately, but lock file ensures reproducibility
|
||||
- **Other Ecosystems**:
|
||||
- Equivalent lock files must be present and committed (e.g., `Pipfile.lock`, `poetry.lock`, `Cargo.lock`)
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 7. Code Reusability and Abstraction
|
||||
- **Avoid Duplication**:
|
||||
- Similar code in two or more places should be extracted to a shared utility or component
|
||||
- **Prefer Composition Over Inheritance**:
|
||||
- Especially in complex domain models, favor composing behavior over deep inheritance hierarchies
|
||||
- **Utility Functions**:
|
||||
- Place truly generic utilities in shared locations (e.g., `utils/`, `lib/`)
|
||||
- Avoid creating utility classes with only static methods if not needed in the language
|
||||
- Severity: `[WARN]`
|
||||
18
AGENT_SKILL/file_naming_convention/CHECKLIST.md
Normal file
18
AGENT_SKILL/file_naming_convention/CHECKLIST.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Checklist: file_naming_convention
|
||||
|
||||
## File/Directory Creation/Rename/Move
|
||||
- [ ] All new files and directories use `snake_case` (lowercase, digits, underscores only)
|
||||
- [ ] No spaces or special characters in file/directory names
|
||||
- [ ] Hyphens (`-`) used only in `docker-compose.yaml`, `docker-compose.yml`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`
|
||||
- [ ] `UPPER_SNAKE_CASE` used only for root-level configuration constants (e.g., `CONFIG.json`, `Dockerfile`)
|
||||
- [ ] Forbidden extensions (`.pdf`, `.pptx`, `.docx`, `.xlsx`, `.csv`, `.zip`, `.pth`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.tiff`, `.webp`, `.mp4`, `.mov`, `.avi`, `.dcm`, `.nii`, `.nii.gz`) are not present in the repository
|
||||
- [ ] Any file under `LEGACY/` that is modified includes an inline comment `# LEGACY:` explaining the change
|
||||
- [ ] Test files follow the pattern: `tests/<same_path_as_source>/test_<source_filename>`
|
||||
|
||||
## Verification Steps
|
||||
1. Check all created/renamed/moved files for snake_case naming
|
||||
2. Verify hyphen usage is limited to allowed files: docker-compose.yaml/.yml, package-lock.json, yarn.lock, pnpm-lock.yaml
|
||||
3. Confirm UPPER_SNAKE_CASE appears only for root config files
|
||||
4. Scan for forbidden file extensions
|
||||
5. Inspect any changes in LEGACY/ directory for required comment
|
||||
6. Validate test file placement matches source file structure
|
||||
74
AGENT_SKILL/file_naming_convention/EXAMPLES.md
Normal file
74
AGENT_SKILL/file_naming_convention/EXAMPLES.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Examples: file_naming_convention
|
||||
|
||||
## Correct Examples
|
||||
|
||||
### File Naming
|
||||
- `patient_data.csv` → ❌ (forbidden extension, should be external)
|
||||
- `patient_data.json` → ✅ (allowed extension, snake_case)
|
||||
- `Dockerfile` → ✅ (UPPER_SNAKE_CASE allowed for root config)
|
||||
- `docker-compose.yaml` → ✅ (hyphen allowed exception)
|
||||
- `docker-compose.yml` → ✅ (hyphen allowed exception)
|
||||
- `package-lock.json` → ✅ (hyphen allowed exception)
|
||||
- `yarn.lock` → ✅ (hyphen allowed exception)
|
||||
- `pnpm-lock.yaml` → ✅ (hyphen allowed exception)
|
||||
- `config.json` → ✅ (snake_case, allowed extension)
|
||||
- `api/v1/patient_routes.py` → ✅ (snake_case in path)
|
||||
|
||||
### Directory Naming
|
||||
- `data_models/` → ✅
|
||||
- `api_v2/` → ✅
|
||||
- `legacy_data/` → ✅ (but see LEGACY/ rule below)
|
||||
|
||||
### LEGACY/ Directory
|
||||
```python
|
||||
# LEGACY: Temporary fix for migration issue #123
|
||||
# TODO: Remove after migration complete
|
||||
legacy_code_here()
|
||||
```
|
||||
→ ✅ (includes required comment)
|
||||
|
||||
### Test Files
|
||||
```
|
||||
src/
|
||||
models/
|
||||
patient_model.py
|
||||
tests/
|
||||
models/
|
||||
test_patient_model.py
|
||||
```
|
||||
→ ✅ (test file mirrors source path)
|
||||
|
||||
## Incorrect Examples
|
||||
|
||||
### File Naming
|
||||
- `PatientData.json` → ❌ (PascalCase, should be snake_case)
|
||||
- `patient-data.json` → ❌ (hyphen not allowed except docker-compose and lockfiles)
|
||||
- `patient data.json` → ❌ (spaces not allowed)
|
||||
- `patient.PDF` → ❌ (forbidden extension)
|
||||
- `secret_key.pem` → ❌ (forbidden extension)
|
||||
- `package_lock.json` → ❌ (should be package-lock.json with hyphens)
|
||||
- `yarn_lock` → ❌ (should be yarn.lock with hyphen and extension)
|
||||
- `docker_compose.yaml` → ❌ (should be docker-compose.yaml with hyphens)
|
||||
|
||||
### Directory Naming
|
||||
- `DataModels/` → ❌ (PascalCase)
|
||||
- `data-models/` → ❌ (hyphens not allowed)
|
||||
- `data models/` → ❌ (spaces)
|
||||
|
||||
### LEGACY/ Directory
|
||||
```python
|
||||
# Modified legacy function
|
||||
def old_function():
|
||||
pass
|
||||
```
|
||||
→ ❌ (missing `# LEGACY:` comment)
|
||||
|
||||
### Test Files
|
||||
```
|
||||
src/
|
||||
utils/
|
||||
helper.py
|
||||
tests/
|
||||
test_helper.py # ❌ Incorrect: should be tests/utils/test_helper.py
|
||||
```
|
||||
→ ❌ (test file does not mirror source path)
|
||||
40
AGENT_SKILL/file_naming_convention/SKILL.md
Normal file
40
AGENT_SKILL/file_naming_convention/SKILL.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Skill: file_naming_convention
|
||||
|
||||
## Trigger Conditions
|
||||
Any file or directory create, rename, or move operation.
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. File and Directory Naming
|
||||
- All files and directories must use `snake_case` (lowercase letters, digits, underscores only).
|
||||
- No spaces or special characters (except hyphens in specific cases).
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 2. Hyphen Usage Exception
|
||||
- Hyphens (`-`) are allowed ONLY in the following specific filenames (where changing the name would break standard tooling):
|
||||
- `docker-compose.yaml`, `docker-compose.yml`
|
||||
- `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`
|
||||
- Files beginning with '.' (like `.gitignore`, `.env`, `.npmrc`) are exempt from snake_case requirements for the prefix portion, but the suffix should follow naming conventions where practical.
|
||||
- Any other use of hyphens in file or directory names is prohibited.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 3. Uppercase Reserved for Root Constants
|
||||
- `UPPER_SNAKE_CASE` is reserved only for root-level configuration constants (e.g., `CONFIG.json`, `Dockerfile`).
|
||||
- Using `UPPER_SNAKE_CASE` for regular files or directories is prohibited.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 4. Forbidden Extensions
|
||||
The following file extensions are forbidden in the repository (must be stored externally and referenced by URL):
|
||||
- `.pdf`, `.pptx`, `.docx`, `.xlsx`, `.csv`, `.zip`, `.pth`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.tiff`, `.webp`, `.mp4`, `.mov`, `.avi`, `.dcm`, `.nii`, `.nii.gz`
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 5. LEGACY/ Directory Policy
|
||||
- The `LEGACY/` directory is read-only.
|
||||
- Any modification to files under `LEGACY/` must include an inline comment `# LEGACY:` explaining why the change is necessary.
|
||||
- Failure to include this comment when modifying LEGACY/ files is a violation.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 6. Test File Naming
|
||||
- Test files must mirror the source file path in a parallel `tests/` directory.
|
||||
- Example: `src/module.py` → `tests/test_module.py`
|
||||
- Severity: `[WARN]`
|
||||
29
AGENT_SKILL/interface_contract_hygiene/CHECKLIST.md
Normal file
29
AGENT_SKILL/interface_contract_hygiene/CHECKLIST.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Checklist: interface_contract_hygiene
|
||||
|
||||
## New Module/Package Creation
|
||||
- [ ] New module/package includes a `spec/` directory
|
||||
- [ ] `spec/interface-contract.md` file exists in the new module
|
||||
- [ ] Contract file is located at `<module_path>/spec/interface-contract.md`
|
||||
|
||||
## Contract Content Verification
|
||||
- [ ] Contract includes a clear **Purpose** section
|
||||
- [ ] Contract identifies an **Owner** (team or individual)
|
||||
- [ ] Contract defines the **Boundary** (includes/excludes)
|
||||
- [ ] Contract specifies a **Breaking-change Policy**
|
||||
- [ ] Contract lists **Consumers** (other modules/systems that use this)
|
||||
- [ ] Contract provides **References** to related documentation
|
||||
- [ ] Contract follows the same format/structure as existing contracts in the same domain
|
||||
|
||||
## Contract Modification (Existing Modules)
|
||||
When modifying a public API (changing function signatures, adding/removing endpoints, etc.):
|
||||
- [ ] Breaking changes follow the versioning policy:
|
||||
- Adding features: MINOR version bump
|
||||
- Removing/changing types: MAJOR version with migration path and 60-day notice
|
||||
- [ ] Deprecation warnings are added for removed/changed interfaces
|
||||
- [ ] Migration documentation is provided for breaking changes
|
||||
|
||||
## Template Consistency Check
|
||||
Before creating a new contract:
|
||||
- [ ] Reviewed at least two existing interface-contract.md files in the same domain (backend/frontend/infra)
|
||||
- [ ] Matched section headings and formatting conventions
|
||||
- [ ] Used similar language and level of detail
|
||||
161
AGENT_SKILL/interface_contract_hygiene/EXAMPLES.md
Normal file
161
AGENT_SKILL/interface_contract_hygiene/EXAMPLES.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Examples: interface_contract_hygiene
|
||||
|
||||
## Correct Examples
|
||||
|
||||
### Backend Service Contract Example
|
||||
File: `backend/services/patient_service/spec/interface_contract.md`
|
||||
|
||||
```markdown
|
||||
# Patient Service Interface Contract
|
||||
|
||||
## Purpose
|
||||
Handles all patient-related business operations including registration, updates, and retrieval of patient records. Provides a clean API for frontend and other backend services.
|
||||
|
||||
## Owner
|
||||
Platform Team (platform-team@vkist.example.com)
|
||||
|
||||
## Boundary
|
||||
**Includes:**
|
||||
- Patient data validation
|
||||
- Appointment scheduling logic
|
||||
- Insurance verification workflows
|
||||
- HL7 message parsing for patient demographics
|
||||
|
||||
**Excludes:**
|
||||
- Image storage and retrieval (handled by Imaging Service)
|
||||
- Payment processing (handled by Billing Service)
|
||||
- User authentication (handled by Auth Service)
|
||||
|
||||
## Breaking-change Policy
|
||||
- **MINOR version**: Adding new optional fields to patient schema, new endpoints for non-core features
|
||||
- **MAJOR version**: Removing fields, changing data types of existing fields, removing endpoints
|
||||
- Requires 60-day deprecation period
|
||||
- Migration guide provided in release notes
|
||||
- Deprecation warnings added 30 days prior to removal
|
||||
|
||||
## Consumers
|
||||
- Frontend Patient Portal (`frontend/modules/patient_portal`)
|
||||
- Appointment Scheduling Service (`backend/services/scheduling`)
|
||||
- Billing Service (`backend/services/billing`)
|
||||
- Mobile App API (`backend/services/mobile-api`)
|
||||
|
||||
## References
|
||||
- [Patient Data Model](../models/patient_model.md)
|
||||
- [API Specification](api-spec.yaml)
|
||||
- [HL7 Interface Specification](../integrations/hl7-spec.md)
|
||||
```
|
||||
|
||||
### Frontend Module Contract Example
|
||||
File: `frontend/modules/appointment_scheduler/spec/interface_contract.md`
|
||||
|
||||
```markdown
|
||||
# Appointment Scheduler Module Contract
|
||||
|
||||
## Purpose
|
||||
Provides UI components and state management for scheduling, rescheduling, and cancelling patient appointments. Integrates with calendar services and sends notifications.
|
||||
|
||||
## Owner
|
||||
Frontend Team (frontend-team@vkist.example.com)
|
||||
|
||||
## Boundary
|
||||
**Includes:**
|
||||
- Appointment booking form components
|
||||
- Calendar view widgets
|
||||
- Conflict detection logic
|
||||
- Notification scheduling (email/SMS)
|
||||
- Form validation for appointment constraints
|
||||
|
||||
**Excludes:**
|
||||
- Patient identity verification (handled by Auth Module)
|
||||
- Payment collection for appointments (handled by Billing Module)
|
||||
- Actual calendar sync with external systems (handled by Calendar Integration Service)
|
||||
|
||||
## Breaking-change Policy
|
||||
- **MINOR**: Adding new optional props to components, new utility functions
|
||||
- **MAJOR**: Removing props, changing prop types, removing components
|
||||
- Requires minor version bump in package.json
|
||||
- Migration guide in CHANGELOG.md
|
||||
- Deprecated props logged as warnings for 2 minor versions
|
||||
|
||||
## Consumers
|
||||
- Main App Layout (`frontend/layout/MainLayout`)
|
||||
- Patient Dashboard (`frontend/modules/patient_dashboard`)
|
||||
- Provider Portal (`frontend/modules/provider_portal`)
|
||||
|
||||
## References
|
||||
- [Component Library Guidelines](../../shared/components/README.md)
|
||||
- [API Contract with Backend](../services/appointment_service/spec/interface-contract.md)
|
||||
- [Accessibility Requirements](../../docs/accessibility.md)
|
||||
```
|
||||
|
||||
### Infrastructure Module Contract Example
|
||||
File: `infra/modules/networking/spec/interface_contract.md`
|
||||
|
||||
```markdown
|
||||
# Networking Module Contract
|
||||
|
||||
## Purpose
|
||||
Defines AWS/VPC networking infrastructure including subnets, route tables, security groups, and DNS configuration for the VKIST deployment.
|
||||
|
||||
## Owner
|
||||
Infrastructure Team (infra-team@vkist.example.com)
|
||||
|
||||
## Boundary
|
||||
**Includes:**
|
||||
- VPC creation and CIDR allocation
|
||||
- Public and private subnet configuration
|
||||
- Internet gateway and NAT gateway setup
|
||||
- Route table association
|
||||
- Security group baseline rules
|
||||
- DNS zone and record creation
|
||||
|
||||
**Excludes:**
|
||||
- Compute resources (EC2/EKS) (handled by compute modules)
|
||||
- Database infrastructure (handled by database module)
|
||||
- Application-level security (handled by respective service modules)
|
||||
|
||||
## Breaking-change Policy
|
||||
- **MINOR**: Adding new subnets, adding non-restrictive security group rules
|
||||
- **MAJOR**: Changing VPC CIDR, removing subnets, altering route tables that break connectivity
|
||||
- Requires terraform state migration plan
|
||||
- 30-day review period with stakeholders
|
||||
- Backward compatibility maintained via Terraform state import where possible
|
||||
|
||||
## Consumers
|
||||
- Compute Module (`infra/modules/compute`)
|
||||
- Database Module (`infra/modules/database`)
|
||||
- EKS Cluster Module (`infra/modules/eks`)
|
||||
- All environment-specific configurations (staging, production)
|
||||
|
||||
## References
|
||||
- [AWS VPC Best Practices](../docs/aws_vpc_best practices.md)
|
||||
- [Network Security Policy](../../docs/security/network_policy.md)
|
||||
- [Terraform Module Guidelines](../../modules/README.md)
|
||||
```
|
||||
|
||||
## Incorrect Examples (to avoid)
|
||||
|
||||
### Missing Contract
|
||||
```diff
|
||||
- // No spec/interface-contract.md exists for this new service
|
||||
+ mkdir -p backend/services/new_service
|
||||
+ touch backend/services/new_service/main.py
|
||||
```
|
||||
|
||||
### Incomplete Contract
|
||||
```markdown
|
||||
# Incomplete Service
|
||||
## Purpose
|
||||
Handles user notifications.
|
||||
|
||||
// Missing: Owner, Boundary, Breaking-change Policy, Consumers, References
|
||||
```
|
||||
|
||||
### Wrong Location
|
||||
```diff
|
||||
- // Contract should be in spec/ but is placed in root
|
||||
+ NEW SERVICE/
|
||||
+ interface_contract.md // WRONG LOCATION
|
||||
+ main.py
|
||||
+ requirements.txt
|
||||
```
|
||||
41
AGENT_SKILL/interface_contract_hygiene/SKILL.md
Normal file
41
AGENT_SKILL/interface_contract_hygiene/SKILL.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Skill: interface_contract_hygiene
|
||||
|
||||
## Trigger Conditions
|
||||
- Creation of a new module, package, or component
|
||||
- Any modification to a public API (function signatures, class interfaces, endpoints)
|
||||
- Creation of a new directory that represents a logical boundary (e.g., new service, new frontend module)
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. Interface Contract Requirement
|
||||
- Every module boundary MUST have an `interface_contract.md` file in its `spec/` directory.
|
||||
- Applies to: backend services, frontend modules, data access layers, infrastructure components.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 2. Contract Content Requirements
|
||||
Each `interface_contract.md` must include:
|
||||
- **Purpose**: Brief description of what the module does
|
||||
- **Owner**: Team or person responsible for the module
|
||||
- **Boundary**: Clear definition of what is inside and outside the module
|
||||
- **Breaking-change Policy**: How breaking changes are handled (versioning, deprecation)
|
||||
- **Consumers**: List of other modules or systems that use this module
|
||||
- **References**: Links to related documentation, diagrams, or external specifications
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 3. Versioning and Backward Compatibility
|
||||
- Adding new fields, endpoints, or optional parameters: MINOR version bump (backward compatible)
|
||||
- Removing or changing types of existing fields/endpoints: MAJOR version bump with:
|
||||
- Migration path documentation
|
||||
- 60-day deprecation notice minimum
|
||||
- Deprecation warnings in code
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 4. Template Consistency
|
||||
- When creating a new `interface_contract.md`, the author MUST review existing contracts in the same domain for naming and template consistency.
|
||||
- Use the same section headings and format as neighboring contracts.
|
||||
- Severity: `[WARN]`
|
||||
|
||||
### 5. Contract Location
|
||||
- Contract must be located at `<module_path>/spec/interface_contract.md`
|
||||
- If `spec/` directory does not exist, it must be created.
|
||||
- Severity: `[ERROR]`
|
||||
43
AGENT_SKILL/secrets_and_phi_safety/CHECKLIST.md
Normal file
43
AGENT_SKILL/secrets_and_phi_safety/CHECKLIST.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Checklist: secrets_and_phi_safety
|
||||
|
||||
## Secret Handling
|
||||
- [ ] No hard-coded secrets (API keys, passwords, tokens, keys) in source code
|
||||
- [ ] All secrets loaded via environment variables (`os.environ.get`, `process.env`, etc.)
|
||||
- [ ] No `.env` files containing secrets in the repository
|
||||
- [ ] No `secrets/` directory contents tracked by Git
|
||||
- [ ] No private keys (`*.pem`, `*.key`) or certificates in the repository
|
||||
- [ ] No model weights or training data files in the repository
|
||||
|
||||
## PHI Protection
|
||||
- [ ] No patient names, IDs, MRNs, SSNs, DOBs, addresses, or phone numbers in:
|
||||
- Console output (`print`, `console.log`)
|
||||
- Application logs (any logging level)
|
||||
- Error messages or exception text
|
||||
- HTTP response bodies (including error responses)
|
||||
- Debug output or test output
|
||||
- [ ] Any identifiers used in logs/tests are clearly synthetic (e.g., `SYNTH-*` prefix)
|
||||
- [ ] Image file paths in logs or responses do not contain actual patient identifiers
|
||||
|
||||
## Test Data and Fixtures
|
||||
- [ ] All test data uses synthetic identifiers:
|
||||
- Patient IDs start with `SYNTH-`
|
||||
- Medical record numbers are clearly fake
|
||||
- No real patient data or realistic facsimiles
|
||||
- [ ] Test fixtures do not contain actual PHI
|
||||
|
||||
## Secrets Directory Protection
|
||||
- [ ] The `secrets/` directory is listed in `.gitignore`
|
||||
- [ ] Before writing to `secrets/`, verified that `.gitignore` includes `secrets/`
|
||||
- [ ] No actual secrets stored in `secrets/` directory (use for non-sensitive configs only if at all)
|
||||
|
||||
## Configuration Safety
|
||||
- [ ] Configuration files do not contain plain-text secrets
|
||||
- [ ] Secrets in configs use environment variable substitution (e.g., `${VAR}` or `env.VAR`)
|
||||
- [ ] TLS certificate paths are configured via environment variables, not hard-coded
|
||||
- [ ] No connection strings with embedded credentials in config files
|
||||
|
||||
## Pre-commit Verification
|
||||
- [ ] Reviewed git diff for accidental secret inclusion
|
||||
- [ ] Checked for strings that resemble keys/tokens (long alphanumeric strings)
|
||||
- [ ] Verified no patient-like identifiers appear in changed files
|
||||
- [ ] Ran secret scanning tool if available (e.g., git-secrets, truffleHog)
|
||||
191
AGENT_SKILL/secrets_and_phi_safety/EXAMPLES.md
Normal file
191
AGENT_SKILL/secrets_and_phi_safety/EXAMPLES.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Examples: secrets_and_phi_safety
|
||||
|
||||
## Correct Examples
|
||||
|
||||
### Secret Loading
|
||||
```python
|
||||
# Good: Loading from environment variables
|
||||
import os
|
||||
|
||||
API_KEY = os.environ.get("EXTERNAL_API_KEY")
|
||||
if not API_KEY:
|
||||
raise ValueError("EXTERNAL_API_KEY environment variable not set")
|
||||
|
||||
DATABASE_URL = os.environ.get("DATABASE_URL")
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY", "default-dev-key-change-in-prod")
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Good: Node.js environment variables
|
||||
const apiKey = process.env.API_KEY;
|
||||
const dbPassword = process.env.DB_PASSWORD;
|
||||
|
||||
if (!apiKey || !dbPassword) {
|
||||
throw new Error('Missing required environment variables');
|
||||
}
|
||||
```
|
||||
|
||||
### PHI Protection in Logs
|
||||
```python
|
||||
# Good: Logging without PHI
|
||||
import logging
|
||||
import hashlib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def process_patient_record(patient_record):
|
||||
# Log only non-identifying information
|
||||
logger.info(
|
||||
f"Processing record for patient age group: {get_age_group(patient_record.age)}"
|
||||
)
|
||||
|
||||
# If logging an ID is absolutely necessary, use a hash
|
||||
patient_hash = hashlib.sha256(
|
||||
patient_record.medical_record_number.encode()
|
||||
).hexdigest()[:8]
|
||||
logger.debug(f"Processing hashed MRN: {patient_hash}...")
|
||||
|
||||
# Never do this:
|
||||
# logger.info(f"Processing patient {patient_record.name} (MRN: {patient_record.mrn})")
|
||||
# logger.error(f"Failed to process {patient_record.id}: {str(e)}") # if id is PHI
|
||||
```
|
||||
|
||||
### Test Data with Synthetic Identifiers
|
||||
```python
|
||||
# Good: Test fixtures use SYNTH-* prefix
|
||||
TEST_PATIENTS = [
|
||||
{
|
||||
"patient_id": "SYNTH-PAT-0001",
|
||||
"name": "John Doe", # Note: In real tests, even names should be fake/synthetic
|
||||
"mrn": "SYNTH-MRN-0001",
|
||||
"date_of_birth": "1980-01-01",
|
||||
# ... other test fields
|
||||
},
|
||||
{
|
||||
"patient_id": "SYNTH-PAT-0002",
|
||||
"name": "Jane Smith",
|
||||
"mrn": "SYNTH-MRN-0002",
|
||||
"date_of_birth": "1992-05-15",
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Configuration Files
|
||||
```yaml
|
||||
# Good: docker-compose.yml using environment variables
|
||||
version: '3.8'
|
||||
services:
|
||||
api:
|
||||
image: medical-api:latest
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
- API_KEY=${API_KEY}
|
||||
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||
secrets:
|
||||
- db_cert # Reference to external secret store, not file in repo
|
||||
|
||||
# Secrets are NOT stored in the repo
|
||||
# Use Docker secrets or Kubernetes secrets or similar external secret management
|
||||
```
|
||||
|
||||
```ini
|
||||
# Good: config.ini with environment variable substitution
|
||||
[database]
|
||||
url = ${DATABASE_URL}
|
||||
# or using os.path.expandvars equivalent
|
||||
```
|
||||
|
||||
### .gitignore Protection
|
||||
```gitignore
|
||||
# .gitignore at repository root
|
||||
# ... other entries
|
||||
secrets/
|
||||
*.env
|
||||
*.pem
|
||||
*.key
|
||||
```
|
||||
|
||||
### Safe Error Messages
|
||||
```python
|
||||
# Good: Generic error messages that don't leak PHI
|
||||
try:
|
||||
process_claim(claim_data)
|
||||
except ValidationError as e:
|
||||
# Log detailed error internally (without PHI)
|
||||
logger.warning(f"Claim validation failed: {e}")
|
||||
# Return generic message to user
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Claim validation failed. Please check the submitted information."
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error processing claim: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Internal server error. Please contact support."
|
||||
)
|
||||
```
|
||||
|
||||
## Incorrect Examples
|
||||
|
||||
### Secret Exposure
|
||||
```python
|
||||
# Bad: Hard-coded secret
|
||||
API_KEY = "sk_live_1234567890abcdef" # VIOLATION: exposed in code
|
||||
|
||||
# Bad: Secret in config file committed to repo
|
||||
# config.yaml
|
||||
# api_key: "actual_secret_key_here" # VIOLATION
|
||||
|
||||
# Bad: Using .env file that gets committed
|
||||
# .env (committed to git)
|
||||
# DATABASE_URL=postgres://user:password@localhost/db
|
||||
```
|
||||
|
||||
### PHI in Logs and Errors
|
||||
```python
|
||||
# Bad: Logging full patient identifier
|
||||
logger.error(f"Failed to update patient {patient.patient_id}: {str(e)}")
|
||||
# If patient_id is MRN or similar, this leaks PHI
|
||||
|
||||
# Bad: Error message with PHI
|
||||
raise ValueError(f"Invalid DOB for patient {patient.name}: {patient.dob}")
|
||||
|
||||
# Bad: Including PHI in HTTP response
|
||||
return {
|
||||
"error": f"Patient {patient.mrn} not found", # MRN is PHI
|
||||
"code": "PATIENT_NOT_FOUND"
|
||||
}
|
||||
```
|
||||
|
||||
### Real Data in Tests
|
||||
```python
|
||||
# Bad: Test using real patient-like data
|
||||
TEST_PATIENT = {
|
||||
"patient_id": "12345678", # Looks like real MRN
|
||||
"name": "John Doe", # Realistic name
|
||||
"ssn": "123-45-6789", # Actual SSN format
|
||||
"dob": "1980-01-01",
|
||||
}
|
||||
```
|
||||
|
||||
### Missing .gitignore Protection
|
||||
```gitignore
|
||||
# .gitmissing: secrets/ directory NOT ignored
|
||||
# This would allow secrets to be committed
|
||||
# (no entry for secrets/ here)
|
||||
```
|
||||
|
||||
### Insecure Configuration
|
||||
```yaml
|
||||
# Bad: Hard-coded secrets in config
|
||||
database:
|
||||
host: localhost
|
||||
port: 5432
|
||||
username: admin
|
||||
password: "super_secret_password" # VIOLATION
|
||||
|
||||
tls:
|
||||
cert_file: "/etc/ssl/certs/real-cert.pem" # Hard-coded path, should be env var
|
||||
key_file: "/etc/ssl/private/real-key.pem"
|
||||
```
|
||||
72
AGENT_SKILL/secrets_and_phi_safety/SKILL.md
Normal file
72
AGENT_SKILL/secrets_and_phi_safety/SKILL.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Skill: secrets_and_phi_safety
|
||||
|
||||
## Trigger Conditions
|
||||
Any code or configuration change involving:
|
||||
- Environment variable access
|
||||
- File I/O operations
|
||||
- Logging statements
|
||||
- HTTP request/response handling
|
||||
- Exception handling
|
||||
- Configuration loading
|
||||
- Secret management systems
|
||||
|
||||
## Rules
|
||||
|
||||
### 1. Secret Storage and Access
|
||||
- **Never commit** secrets to version control:
|
||||
- `.env` files
|
||||
- `secrets/` directory contents
|
||||
- Private keys (`*.pem`, `*.key`)
|
||||
- API keys, passwords, tokens
|
||||
- Model weights or training data
|
||||
- All secrets must be loaded via environment variables using:
|
||||
- `os.environ.get("KEY_NAME")` in Python
|
||||
- `process.env.KEY_NAME` in Node.js
|
||||
- Equivalent secure methods in other languages
|
||||
- Hard-coded literal secrets in source code are strictly prohibited.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 2. PHI (Protected Health Information) Protection
|
||||
- **Never log, print, or expose** PHI in any form:
|
||||
- Patient names, IDs, medical record numbers
|
||||
- Dates of birth, ages, genders
|
||||
- Addresses, phone numbers, email addresses
|
||||
- Image file paths or URLs that could identify patients
|
||||
- Diagnosis codes, procedure codes, or treatment details
|
||||
- This applies to:
|
||||
- Console logs (`print()`, `console.log()`)
|
||||
- Application logs (any logging framework)
|
||||
- Exception messages and stack traces
|
||||
- HTTP response bodies (including error responses)
|
||||
- Debug output or test output
|
||||
- CI/CD pipeline artifacts and logs
|
||||
- Use only synthetic/test data with `SYNTH-*` prefix for development and testing.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 3. Test Data and Fixtures
|
||||
- All test fixtures, mock data, and sample data must use synthetic identifiers:
|
||||
- Patient IDs must start with `SYNTH-` (e.g., `SYNTH-PAT-0001`)
|
||||
- Medical record numbers must be clearly fake
|
||||
- No real patient data or derivations thereof
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 4. Secrets Directory Protection
|
||||
- The `secrets/` directory must be ignored by Git:
|
||||
- `secrets/` must be listed in `.gitignore` at the repository root
|
||||
- Any attempt to write to `secrets/` directory must first verify that `.gitignore` contains `secrets/` (or `/*` negations, but effectively using a `secrets/` directory, verify `.gitignore` inclusion before first use
|
||||
- Never store actual secrets in the repository, even if encrypted or obfuscated.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 5. Configuration and Secrets Injection
|
||||
- Configuration files (e.g., `.yaml`, `.json`, `.ini`) must not contain secrets.
|
||||
- Use environment variable substitution in config files:
|
||||
- Example: `password: ${DB_PASSWORD}` or `password: env.DB_PASSWORD`
|
||||
- TLS/SSL certificate paths must come from environment variables, not be hardcoded.
|
||||
- Severity: `[ERROR]`
|
||||
|
||||
### 6. Audit and Detection
|
||||
- Before committing, run secret scanning tools if available (e.g., git-secrets, truffleHog, git-leaks)
|
||||
- Review diff for accidental inclusion of:
|
||||
- Strings resembling passwords, keys, or tokens
|
||||
- File paths that look like they could contain PHI
|
||||
- Any literal that looks like an ID number, SSN, MRN, etc.
|
||||
Reference in New Issue
Block a user