323 lines
14 KiB
Markdown
323 lines
14 KiB
Markdown
# rules.md
|
|
|
|
**必须强制执行的约束。违反时立即终止并报错。**
|
|
|
|
---
|
|
|
|
## Code Style - Imports
|
|
|
|
### Python
|
|
```python
|
|
# Group order: stdlib → third-party → local
|
|
import json
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
import redis
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.config import settings
|
|
from app.models.user import User
|
|
from app.schemas.user import UserCreate
|
|
```
|
|
|
|
### TypeScript
|
|
```typescript
|
|
// Group order: React → Third-party → Local
|
|
import React, { useState, useEffect } from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import axios from 'axios';
|
|
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import { api } from '@/services/api';
|
|
```
|
|
|
|
**Rules:**
|
|
- Sort alphabetically within groups
|
|
- Use absolute imports for external packages, relative for local modules
|
|
- **NEVER** use wildcard imports (`from module import *`)
|
|
|
|
---
|
|
|
|
## Code Style - Formatting
|
|
|
|
- **Python:** 4-space indentation, Black formatter, max line 100
|
|
- **TypeScript:** 2-space indentation, Prettier, max line 100
|
|
- Run formatter **before committing**
|
|
- No trailing whitespace
|
|
- Empty line at end of file
|
|
|
|
---
|
|
|
|
## Code Style - Type Hints
|
|
|
|
```python
|
|
# Use strict typing - NO Any
|
|
from typing import List, Dict, Optional, Union
|
|
from datetime import datetime
|
|
|
|
def get_gpu_clusters(
|
|
country: Optional[str] = None,
|
|
min_gpu_count: int = 0,
|
|
) -> List[Dict[str, Union[str, int, float]]]:
|
|
...
|
|
```
|
|
|
|
**Rules:**
|
|
- Use type hints throughout
|
|
- **NEVER** use `Any` - use `unknown` or specific unions
|
|
- Define interfaces/types for all data structures
|
|
- Generic types preferred over type casting
|
|
|
|
---
|
|
|
|
## Code Style - Naming Conventions
|
|
|
|
| Pattern | Usage | Example |
|
|
|---------|-------|---------|
|
|
| `camelCase` | Variables, functions, methods | `gpuCluster`, `getData()` |
|
|
| `PascalCase` | Classes, components, types | `GPUCluster`, `DataSourceConfig` |
|
|
| `SCREAMING_SNAKE_CASE` | Constants, env vars | `API_KEY`, `DATABASE_URL` |
|
|
| `kebab-case` | File names, CSS | `data-source-config.css` |
|
|
|
|
**Rules:**
|
|
- Descriptive names - avoid abbreviations except well-known ones (id, ok, err)
|
|
- Max function length: 50 lines
|
|
- Max file length: 500 lines
|
|
|
|
---
|
|
|
|
## Code Style - Error Handling
|
|
|
|
```python
|
|
# Use custom exceptions
|
|
class DataSourceError(Exception):
|
|
"""Raised when data source fetch fails"""
|
|
pass
|
|
|
|
# Proper error handling with logging
|
|
try:
|
|
data = await fetch_data(source)
|
|
except requests.RequestException as e:
|
|
logger.error(f"Failed to fetch from {source}: {e}")
|
|
raise DataSourceError(f"Source {source} unavailable") from e
|
|
```
|
|
|
|
**Rules:**
|
|
- **NEVER swallow errors silently**
|
|
- Use custom exceptions for domain errors
|
|
-区分可恢复错误和不可恢复错误
|
|
- Log errors with appropriate level (warn/error)
|
|
- Include context in all error messages
|
|
- Propagate errors to caller unless explicitly handled
|
|
|
|
---
|
|
|
|
## Security - NON-NEGOTIABLE
|
|
|
|
- **NEVER** commit `.env`, secrets, keys, or credentials
|
|
- Use environment variables for all credentials
|
|
- Validate and sanitize all user inputs
|
|
- Use parameterized queries for database operations (SQL injection prevention)
|
|
- JWT tokens with short expiration (15 min)
|
|
- Redis for token blacklist (logout support)
|
|
- Hash passwords with bcrypt/argon2 - **NEVER** store plain text
|
|
|
|
---
|
|
|
|
## Git Workflow
|
|
|
|
```bash
|
|
# Create feature branch
|
|
git checkout -b feature/data-collector-huggingface
|
|
|
|
# Commit message format
|
|
git commit -m "feat: add Hugging Face data collector"
|
|
git commit -m "fix: resolve WebSocket heartbeat timeout"
|
|
git commit -m "docs: update API documentation"
|
|
|
|
# Before opening PR
|
|
git fetch origin && git rebase origin/main
|
|
./.venv/bin/python -m pytest -s backend/tests && bun run build
|
|
```
|
|
|
|
**Rules:**
|
|
- Feature branches from main
|
|
- Clear commit messages - "Add user authentication", not "fix"
|
|
- **NEVER** force push to main
|
|
- Run tests and lint **before** committing
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
**Rules:**
|
|
- Verify package legitimacy before adding
|
|
- Prefer well-maintained, widely-used libraries
|
|
- Pin dependency versions in `pyproject.toml`, `uv.lock`, and `package.json`
|
|
- Review security advisories with `pip-audit` and `bun audit`
|
|
- Frontend package management and script execution use **Bun only**
|
|
- Frontend commands must use `bun install` / `bun run ...`
|
|
- **NEVER** use `npm` / `pnpm` / `yarn` for the frontend project
|
|
- **NEVER** add unknown packages
|
|
|
|
---
|
|
|
|
## Data Collector Pattern - MANDATORY
|
|
|
|
```python
|
|
class BaseCollector:
|
|
async def fetch(self) -> List[Dict]:
|
|
"""Fetch data from source"""
|
|
...
|
|
|
|
def transform(self, raw_data: Dict) -> NormalizedData:
|
|
"""Transform to internal format"""
|
|
...
|
|
|
|
async def run(self):
|
|
"""Full pipeline: fetch -> transform -> save"""
|
|
raw = await self.fetch()
|
|
data = self.transform(raw)
|
|
await self.save(data)
|
|
```
|
|
|
|
**Rules:**
|
|
- Each data source has its own collector class
|
|
- Collectors **MUST** inherit from `BaseCollector`
|
|
- Implement `fetch()` and `transform()` methods
|
|
- Support incremental and full sync modes
|
|
|
|
---
|
|
|
|
## WebSocket Communication - MANDATORY
|
|
|
|
```python
|
|
# Data frame format
|
|
{
|
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
"type": "update", # or "full"
|
|
"payload": {
|
|
"gpu_clusters": [...],
|
|
"submarine_cables": [...],
|
|
"ixp_nodes": [...]
|
|
}
|
|
}
|
|
|
|
# Heartbeat every 30 seconds
|
|
```
|
|
|
|
**Rules:**
|
|
- UE5 communicates via WebSocket (not REST)
|
|
- Send data frames at configured intervals (default: 5 minutes)
|
|
- Include camera position in control frames
|
|
- Support auto-cruise and manual interaction modes
|
|
|
|
---
|
|
|
|
## General Guidelines
|
|
|
|
- Keep functions small and focused (single responsibility)
|
|
- Write self-documenting code; comment **why**, not what
|
|
- One concern per file/module
|
|
- Dependency injection for testability
|
|
- Feature flags for incomplete features
|
|
- Use config files for environment-specific settings
|
|
|
|
## Code Hygiene - MANDATORY
|
|
|
|
- Maintain a single source of truth for business state. Frontend temporary state, cached state, and persisted backend state must not evolve into parallel truths.
|
|
- Transitional paths are temporary. Once a new implementation is stable, remove old branches, old interfaces, old mocks, and compatibility layers instead of letting them linger.
|
|
- Repeated logic must be extracted. If request flow, response handling, auth/header assembly, validation, or state reconciliation appears more than once or twice, promote it into a helper or shared layer.
|
|
- Repeated backend resource lookup and response assembly must be centralized. Avoid scattering the same `load -> validate -> transform -> respond` pattern across multiple handlers or services.
|
|
- Default values, system prompts, placeholder structures, and other fixed constants must be centralized rather than re-declared in multiple states or code paths.
|
|
- When debugging layout, scrolling, or overflow issues, first inspect structural ownership of height, width, and overflow before applying isolated style patches.
|
|
- Distinct interaction modes must have explicit structure and state semantics. View, edit, loading, error, stopped, and retry states should not be forced through the exact same markup or logic path.
|
|
- Presentation state must not pretend to be business state. UI animation, phase labels, and optimistic display layers must defer to real persisted or backend task state when it exists.
|
|
- Responsive adaptations must preserve the primary action path. Reflow is fine; losing or displacing the main user action is not.
|
|
- After large feature commits, perform an explicit cleanup pass for dead code, temporary branches, duplicated helpers, stale interfaces, and naming drift before considering the work complete.
|
|
- If a file or module starts accumulating repeated patterns or mixed responsibilities, stop and refactor before continuing to add more features on top.
|
|
- Public interfaces, persisted fields, and state structures must have a current owner and caller. If something is no longer used, delete it instead of keeping it “just in case”.
|
|
|
|
---
|
|
|
|
## Query Performance - MANDATORY
|
|
|
|
- **NEVER** load whole tables into Python just to do filtering, pagination, counting, dedupe, or summary aggregation
|
|
- Filters, sorting, pagination, `count`, `distinct`, and grouped statistics **MUST** be pushed down to the database whenever the ORM/query builder can express them
|
|
- Summary/dashboard endpoints should prefer dedicated aggregate queries or aggregate endpoints, not multiple full-table scans
|
|
- For hot paths, avoid selecting large JSON/text payload columns unless the response really needs them
|
|
- If an endpoint returns a list, default to database-side pagination instead of `scalars().all()` followed by Python slicing
|
|
- When you suspect a query is slow, first check for:
|
|
- full-table ORM loads
|
|
- Python-side post-filtering
|
|
- repeated summary queries that can be merged
|
|
- repeated per-request recomputation that should be cached or aggregated once
|
|
|
|
---
|
|
|
|
## Release Workflow - MANDATORY
|
|
|
|
- When the user asks to `发版`, `bump version`, `release`, or `推送发布类改动`, treat it as a release workflow, not a plain commit
|
|
- Apply repository versioning rules consistently:
|
|
- `feature` -> `+0.1.0`
|
|
- `bugfix` -> `+0.0.1`
|
|
- `docs / maintenance / refactor` do **NOT** bump version unless the user explicitly wants a release anyway
|
|
- A release bump **MUST** update all version-bearing files together:
|
|
- `VERSION`
|
|
- `frontend/package.json`
|
|
- `pyproject.toml`
|
|
- `uv.lock`
|
|
- A release bump **MUST** update release records together:
|
|
- `docs/CHANGELOG.md`
|
|
- `docs/version-history.md`
|
|
- Before committing a release, verify the target version appears consistently in all required files
|
|
- Before pushing a release, run the smallest relevant validation available for the changed scope and report what was or was not validated
|
|
- If runtime output directories are part of the feature flow, confirm they are ignored appropriately so release commits do not accidentally include generated artifacts
|
|
- If asked to commit/push release work, do **NOT** skip changelog or version-history updates just because the code changes are small
|
|
- Use the repo skill at `/home/ray/dev/linkong/planet/.codex/skills/release-workflow/SKILL.md` whenever performing a release workflow for this repository
|
|
|
|
---
|
|
|
|
## Country Data Validation - MANDATORY
|
|
|
|
- **ALL** data sources that carry a country, region, or territory field (API responses, GeoJSON, CSVs, scraped data, third-party enrichment) **MUST** have their country values validated against the project's canonical country dictionary at `backend/app/core/countries.py` before being stored or displayed
|
|
- Use `normalize_country(value)` from `countries.py` as the single gate. If it returns `None`, the value is unrecognized and must be logged and rejected or flagged — **NEVER** silently pass it through
|
|
- The dictionary encodes official political positions (e.g., Taiwan → 中国(台湾), Kosovo → 塞尔维亚, Gaza → 巴勒斯坦). Do **NOT** override these with raw source data labels
|
|
- When integrating a new data source, run a pre-flight check: extract all distinct country values from the source and verify each one resolves via `normalize_country`. Fix unresolved values before wiring up the collector
|
|
- Geographic boundary data (GeoJSON, shapefiles, tilesets) must be post-processed to align feature names and hover labels with the dictionary. The Natural Earth `ne_110m_admin_0_countries` dataset downloaded from GitHub was used as the base for the frontend boundary layer; political corrections were applied manually
|
|
- If a new country alias needs to be added to the dictionary, add it to `COUNTRY_ENTRIES` in `countries.py` — **NEVER** scatter aliases across individual collectors or API handlers
|
|
- Frontend hover tooltips and info cards that display country names must source the name from the canonical dictionary (via `NAME_ZH` after normalization), not raw source strings
|
|
|
|
---
|
|
|
|
## Frontend Layout - MANDATORY
|
|
|
|
- Backend/admin pages must be designed as a `single-screen workspace` first, not as a long vertically stacked document
|
|
- In common desktop viewports, users should be able to see:
|
|
- page header
|
|
- summary/controls
|
|
- the main work area
|
|
- The main work area must get the majority of the available height; secondary cards must not crowd it out
|
|
- If a card or panel would be compressed until text, controls, or tables become unreadable, stop shrinking it and give that region an internal scrollbar instead
|
|
- On small screens, high browser zoom, or reduced viewport height, switch to a compact mode or horizontal summary scrolling before allowing important content to be crushed
|
|
- Overflow ownership must be explicit:
|
|
- parent height chain must be valid
|
|
- height-constrained flex parents need `min-height: 0`
|
|
- only the intended scroll node should own `overflow: auto`
|
|
- Do **NOT** rely on `overflow: hidden` as the final fix for a crowded layout unless another child container is explicitly responsible for scrolling
|
|
- For tabs:
|
|
- hidden tab panes must stay hidden
|
|
- do not override library hidden-pane selectors in a way that makes inactive content visible
|
|
- each tab must define its own scroll strategy instead of inheriting a one-size-fits-all table layout
|
|
- For long-form content such as AI briefs, logs, markdown, raw JSON, or help text:
|
|
- prefer normal document flow inside the content block
|
|
- if height is constrained, use a stable minimum readable height plus scrolling
|
|
- do not let flex compression collapse the readable area into a thin strip
|
|
- Avoid brittle viewport sizing:
|
|
- prefer `height: 100%` chains over naive `100vh/100vw` usage in embedded/admin shells
|
|
- verify layouts under browser zoom `125%` and `150%`
|
|
- Avoid using wrapper components with implicit layout behavior, such as `Space`, for height-critical scroll regions unless their generated DOM is fully accounted for
|
|
- Any UI state that hides data or a layer must also reconcile related hover/lock/tooltip/selection state so hidden content is not still “active” in the UI
|