# 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