release: bump version to 0.43.0
This commit is contained in:
720
rules.md
720
rules.md
@@ -1,381 +1,503 @@
|
||||
# rules.md
|
||||
---
|
||||
name: planet-rules
|
||||
description: Planet repository rules split into LLM-loadable modules.
|
||||
---
|
||||
|
||||
**必须强制执行的约束。违反时立即终止并报错。**
|
||||
# Planet Rules
|
||||
|
||||
**These rules are mandatory. If a requested action conflicts with this file, stop and report the conflict.**
|
||||
|
||||
## Loading Protocol
|
||||
|
||||
Read this top section first, then load only the modules relevant to the task.
|
||||
|
||||
Always load:
|
||||
|
||||
- `core`
|
||||
- `security`
|
||||
- `workflow`
|
||||
|
||||
Load selectively:
|
||||
|
||||
| Module | Load when |
|
||||
|--------|-----------|
|
||||
| `docs` | Writing, translating, linking, or publishing documentation |
|
||||
| `uiux` | Visual design, layout, interaction, accessibility, responsive behavior |
|
||||
| `frontend` | React, TypeScript, CSS, Vite, Bun, admin console, docs UI |
|
||||
| `backend` | FastAPI, SQLAlchemy, data collectors, database, API performance |
|
||||
| `earth` | 3D Earth, canvas/Three.js, BGP/vessel/satellite/cable layers, map icons |
|
||||
| `ai` | AI Provider, LLM gateway, prompts, model config, AI Playground |
|
||||
| `release` | Version bumps, changelog, version history, commit/tag/push release work |
|
||||
|
||||
Do not load the entire file by default for small tasks. Use `rg -n "^## Module:" rules.md` to find module boundaries, then read only the needed block.
|
||||
|
||||
---
|
||||
|
||||
## Code Style - Imports
|
||||
## Module: core
|
||||
|
||||
### Load When
|
||||
|
||||
Always.
|
||||
|
||||
### Must
|
||||
|
||||
- Keep functions small and focused; one concern per file/module.
|
||||
- Write self-documenting code; comments explain why, not what.
|
||||
- Prefer dependency injection for testability.
|
||||
- Use feature flags for incomplete features.
|
||||
- Use config files or environment variables for environment-specific settings.
|
||||
- Maintain one source of truth for business state. Temporary UI state, cached state, and persisted backend state must not become parallel truths.
|
||||
- Transitional paths are temporary. Once a new implementation is stable, remove old branches, old interfaces, old mocks, and compatibility layers.
|
||||
- Extract repeated request flow, response handling, auth/header assembly, validation, and state reconciliation into helpers or shared layers.
|
||||
- Centralize default values, system prompts, placeholder structures, and fixed constants.
|
||||
- Public interfaces, persisted fields, and state structures must have a current owner and caller. Delete unused ones.
|
||||
- After large feature work, run an explicit cleanup pass for dead code, duplicated helpers, stale interfaces, and naming drift.
|
||||
|
||||
### Code Style
|
||||
|
||||
- Python: 4-space indentation, Black style, max line length 100.
|
||||
- TypeScript: 2-space indentation, Prettier style, max line length 100.
|
||||
- No trailing whitespace.
|
||||
- Empty line at end of file.
|
||||
- Sort imports alphabetically inside groups.
|
||||
- Never use wildcard imports.
|
||||
- Avoid unclear abbreviations except common ones such as `id`, `ok`, `err`.
|
||||
- Prefer descriptive names.
|
||||
- Keep functions around 50 lines or less where practical.
|
||||
- Split files before they become mixed-responsibility modules.
|
||||
|
||||
### Import Order
|
||||
|
||||
Python:
|
||||
|
||||
### Python
|
||||
```python
|
||||
# Group order: stdlib → third-party → local
|
||||
# stdlib -> third-party -> local
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
import redis
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter
|
||||
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:
|
||||
|
||||
```typescript
|
||||
// Group order: React → Third-party → Local
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
// React -> third-party -> local
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { api } from '@/services/api';
|
||||
import axios from 'axios'
|
||||
|
||||
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 *`)
|
||||
### Type Rules
|
||||
|
||||
---
|
||||
- Use type hints throughout Python.
|
||||
- Define TypeScript interfaces/types for all structured data.
|
||||
- Avoid `Any`; use specific unions, generics, or `unknown` where appropriate.
|
||||
- Prefer typed helpers over repeated type casting.
|
||||
|
||||
## Code Style - Formatting
|
||||
### Verify
|
||||
|
||||
- **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
|
||||
- Use deterministic checks before broad manual inspection:
|
||||
|
||||
```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
|
||||
git diff --check
|
||||
rg -n "TODO|FIXME|console\.log|debugger|print\(" <changed-paths>
|
||||
```
|
||||
|
||||
**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
|
||||
## Module: security
|
||||
|
||||
**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
|
||||
### Load When
|
||||
|
||||
---
|
||||
Always.
|
||||
|
||||
## Data Collector Pattern - MANDATORY
|
||||
### Must
|
||||
|
||||
```python
|
||||
class BaseCollector:
|
||||
async def fetch(self) -> List[Dict]:
|
||||
"""Fetch data from source"""
|
||||
...
|
||||
- Never commit `.env`, secrets, keys, tokens, or credentials.
|
||||
- Use environment variables or the configured settings store for credentials.
|
||||
- Validate and sanitize user input.
|
||||
- Use parameterized database queries.
|
||||
- Never store plain-text passwords.
|
||||
- Hash passwords with bcrypt/argon2.
|
||||
- Use short-lived JWT tokens when auth tokens are involved.
|
||||
- Use token blacklist or equivalent revocation support for logout.
|
||||
- Do not expose full tokens in UI. Show only a short prefix and mask the rest.
|
||||
|
||||
def transform(self, raw_data: Dict) -> NormalizedData:
|
||||
"""Transform to internal format"""
|
||||
...
|
||||
### Verify
|
||||
|
||||
async def run(self):
|
||||
"""Full pipeline: fetch -> transform -> save"""
|
||||
raw = await self.fetch()
|
||||
data = self.transform(raw)
|
||||
await self.save(data)
|
||||
```bash
|
||||
git diff --name-only HEAD
|
||||
rg -n "api[_-]?key|client_secret|BEGIN .*PRIVATE KEY|AKIA[0-9A-Z]" .
|
||||
```
|
||||
|
||||
**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
|
||||
## Module: workflow
|
||||
|
||||
```python
|
||||
# Data frame format
|
||||
{
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"type": "update", # or "full"
|
||||
"payload": {
|
||||
"gpu_clusters": [...],
|
||||
"submarine_cables": [...],
|
||||
"ixp_nodes": [...]
|
||||
}
|
||||
}
|
||||
### Load When
|
||||
|
||||
# Heartbeat every 30 seconds
|
||||
Always.
|
||||
|
||||
### Git
|
||||
|
||||
- Do not revert user changes unless explicitly requested.
|
||||
- Do not force push to protected branches.
|
||||
- Use clear commit messages.
|
||||
- Run relevant tests and builds before committing.
|
||||
- Frontend package management must use Bun only.
|
||||
- Never use `npm`, `pnpm`, or `yarn` in the frontend project.
|
||||
- Verify package legitimacy before adding dependencies.
|
||||
- Prefer maintained, widely used libraries.
|
||||
- Pin dependency versions in `pyproject.toml`, `uv.lock`, and `package.json`.
|
||||
|
||||
### Deterministic Context
|
||||
|
||||
- Prefer compact CLI evidence over reading large files or full diffs:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git diff --stat HEAD
|
||||
git diff --name-only HEAD
|
||||
git diff --unified=0 HEAD -- <path>
|
||||
rg -n "<pattern>" <path>
|
||||
```
|
||||
|
||||
**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
|
||||
---
|
||||
|
||||
## Module: docs
|
||||
|
||||
### Load When
|
||||
|
||||
Writing, translating, linking, restructuring, or publishing docs.
|
||||
|
||||
### Must
|
||||
|
||||
- Chinese docs under `docs/technical/zh/` must be Chinese prose, not copied English placeholders.
|
||||
- Keep technical identifiers, API paths, config keys, code symbols, and product names in English where appropriate.
|
||||
- Explain why a change exists, not only what files changed.
|
||||
- Prefer updating an existing relevant doc over creating a duplicate.
|
||||
- Use `##` and `###` headings; avoid going deeper than three levels.
|
||||
- Use fenced code blocks with language tags.
|
||||
- Use tables when comparing options or listing parameters.
|
||||
- Do not reference PR numbers, issue numbers, or the current conversation.
|
||||
- Internal links inside `docs/technical/zh/` should point to `docs/technical/zh/...` unless intentionally linking to English-only docs.
|
||||
- Public Docs UI must only expose documents explicitly registered in `frontend/src/pages/Docs/docs-content.ts`.
|
||||
- Development plans and task notes under `docs/plans/` are not automatically public documentation.
|
||||
|
||||
### Required Content
|
||||
|
||||
- Background/problem.
|
||||
- Core design decisions and rationale.
|
||||
- Key snippets or focused examples.
|
||||
- Related files and each file's role.
|
||||
- Operational caveats or verification steps when relevant.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
git diff --stat HEAD
|
||||
git diff --name-only HEAD
|
||||
ls docs/technical/zh/
|
||||
rg -n "\]\(([^)]+)\)" docs/technical/zh/<doc>.md
|
||||
rg -n "/home/ray/dev/linkong/planet/docs/technical/(?!zh|en)" docs/technical/zh --pcre2
|
||||
```
|
||||
|
||||
Check zh/en duplicates:
|
||||
|
||||
```bash
|
||||
python - <<'PY'
|
||||
from pathlib import Path
|
||||
same = []
|
||||
for en in sorted(Path("docs/technical/en").glob("*.md")):
|
||||
zh = Path("docs/technical/zh") / en.name
|
||||
if zh.exists() and en.read_text() == zh.read_text():
|
||||
same.append(en.name)
|
||||
if same:
|
||||
raise SystemExit("identical en/zh docs: " + ", ".join(same))
|
||||
print("no identical en/zh docs")
|
||||
PY
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## General Guidelines
|
||||
## Module: uiux
|
||||
|
||||
- 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
|
||||
### Load When
|
||||
|
||||
## Code Hygiene - MANDATORY
|
||||
Changing layout, visual hierarchy, controls, interaction states, responsive behavior, or accessibility.
|
||||
|
||||
- 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”.
|
||||
### Must
|
||||
|
||||
- Backend/admin pages are single-screen workspaces first, not long landing pages.
|
||||
- Common desktop viewports should show the page header, summary/controls, and main work area.
|
||||
- The main work area gets most available height.
|
||||
- If text, controls, or tables become unreadable, give that region an internal scrollbar instead of crushing it.
|
||||
- Overflow ownership must be explicit:
|
||||
- parent height chain is valid
|
||||
- height-constrained flex parents use `min-height: 0`
|
||||
- only the intended scroll node owns `overflow: auto`
|
||||
- Do not use `overflow: hidden` as a final fix unless another child owns scrolling.
|
||||
- Tabs define their own scroll strategy; hidden panes must stay hidden.
|
||||
- Long-form content such as AI briefs, logs, Markdown, raw JSON, and help text should stay readable.
|
||||
- Prefer stable readable minimum heights plus scrolling for constrained content.
|
||||
- Avoid brittle `100vh/100vw` in embedded/admin shells; prefer `height: 100%` chains.
|
||||
- Verify layouts under browser zoom 125% and 150% when changing height-critical screens.
|
||||
- Avoid wrapper components with implicit layout behavior, such as `Space`, in height-critical scroll regions unless the generated DOM is accounted for.
|
||||
- Any UI state that hides data or a layer must also reconcile hover, lock, tooltip, and selection state.
|
||||
|
||||
### Visual Controls
|
||||
|
||||
- Use icons in buttons for common tools/actions when an established icon exists.
|
||||
- Keep icon-only buttons accessible with `aria-label` and `title`.
|
||||
- Use segmented controls for modes, switches/checkboxes for binary settings, sliders/inputs for numeric values, menus/selects for option sets, and tabs for views.
|
||||
- Do not put cards inside cards.
|
||||
- Do not use visible in-app text to explain obvious UI features or styling.
|
||||
- Text must fit within its parent on mobile and desktop.
|
||||
- Do not scale font size with viewport width.
|
||||
- Letter spacing should usually be `0`.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
changed=$(git diff --name-only HEAD -- frontend/src)
|
||||
[ -z "$changed" ] || rg -n "overflow|min-height|Space|Tabs|aria-label|title=" $changed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Query Performance - MANDATORY
|
||||
## Module: frontend
|
||||
|
||||
- **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:
|
||||
### Load When
|
||||
|
||||
Editing React, TypeScript, CSS, Vite, Bun, admin console, public Docs UI, or client-side services.
|
||||
|
||||
### Must
|
||||
|
||||
- Use Bun for frontend commands:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun run --cwd frontend build
|
||||
```
|
||||
|
||||
- Never use `npm`, `pnpm`, or `yarn`.
|
||||
- Keep shared behavior in reusable components/services, not page-local copies.
|
||||
- Prefer existing project components and patterns.
|
||||
- Keep page state, backend state, and persisted state clearly separated.
|
||||
- Presentation state must not pretend to be business state.
|
||||
- Loading, error, stopped, retry, edit, and view states need explicit semantics.
|
||||
- Responsive adaptations must preserve the primary action path.
|
||||
- Markdown rendering behavior belongs in the shared Markdown renderer, not individual docs.
|
||||
- Public Docs navigation must be whitelist-driven through metadata, not file-system fallback.
|
||||
|
||||
### TypeScript/CSS
|
||||
|
||||
- Define interfaces for API payloads and component props.
|
||||
- Avoid broad casts.
|
||||
- Prefer CSS classes over inline styles except for truly dynamic values.
|
||||
- For fixed-format UI elements, define stable dimensions with `aspect-ratio`, grid tracks, min/max constraints, or container-relative sizing.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
bun run --cwd frontend build
|
||||
git diff --check -- frontend
|
||||
rg -n "npm|pnpm|yarn" frontend package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module: backend
|
||||
|
||||
### Load When
|
||||
|
||||
Editing FastAPI, SQLAlchemy, collectors, database models, migrations, services, API routes, or performance-sensitive code.
|
||||
|
||||
### Must
|
||||
|
||||
- Use custom exceptions for domain errors.
|
||||
- Never swallow errors silently.
|
||||
- Distinguish recoverable and unrecoverable errors.
|
||||
- Log errors with useful context and appropriate level.
|
||||
- Propagate errors unless explicitly handled.
|
||||
- Repeated `load -> validate -> transform -> respond` flow should be centralized.
|
||||
- Each data source has its own collector class.
|
||||
- Collectors must inherit the repository's base collector abstraction when available.
|
||||
- Collectors implement `fetch()` and `transform()` or the current project-equivalent pipeline hooks.
|
||||
- Support incremental and full sync modes where the source allows it.
|
||||
|
||||
### Query Performance
|
||||
|
||||
- Never load whole tables into Python for filtering, pagination, counting, dedupe, or summary aggregation.
|
||||
- Push filters, sorting, pagination, `count`, `distinct`, and grouped statistics down to the database.
|
||||
- Summary/dashboard endpoints should prefer aggregate queries or aggregate endpoints.
|
||||
- Avoid selecting large JSON/text payload columns on hot paths unless needed.
|
||||
- List endpoints default to database-side pagination.
|
||||
- When a query is slow, first check:
|
||||
- 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
|
||||
- repeated per-request recomputation that should be cached or aggregated
|
||||
|
||||
---
|
||||
### Country Data
|
||||
|
||||
## Release Workflow - MANDATORY
|
||||
- Data sources carrying country, region, or territory fields must validate against `backend/app/core/countries.py`.
|
||||
- Use `normalize_country(value)` as the single gate.
|
||||
- If normalization returns `None`, log and reject or flag the value.
|
||||
- Do not override canonical political labels with raw source labels.
|
||||
- Add aliases to `COUNTRY_ENTRIES`; do not scatter aliases across collectors or API handlers.
|
||||
- Frontend country labels should come from the canonical dictionary after normalization.
|
||||
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
## Icon System - MANDATORY
|
||||
|
||||
All canvas-drawn marker icons for the 3D earth visualization **MUST** have a canonical SVG in:
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
git diff --name-only HEAD -- backend
|
||||
python3 -m py_compile <changed-python-files>
|
||||
rg -n "scalars\\(\\)\\.all\\(\\)|\\.all\\(\\).*\\[:|len\\(.*\\.all\\(" backend/app
|
||||
rg -n "text\\(\"SELECT \\*|execute.*SELECT \\*" backend/app
|
||||
rg -n "normalize_country|COUNTRY_ENTRIES" backend/app
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module: earth
|
||||
|
||||
### Load When
|
||||
|
||||
Editing `frontend/public/earth`, 3D Earth, canvas/Three.js rendering, BGP/vessel/satellite/cable layers, geographic boundaries, or Earth marker icons.
|
||||
|
||||
### Must
|
||||
|
||||
- Keep rendering state and UI state explicitly synchronized.
|
||||
- If a layer is hidden, clear or reconcile related hover, lock, tooltip, and selection state.
|
||||
- Avoid one-off visual patches before checking render order, coordinate ownership, and data lifecycle.
|
||||
- Use Three.js for 3D elements.
|
||||
- Verify 3D/canvas work with real rendering, not only TypeScript build.
|
||||
- Do not let loading messages, phase labels, or optimistic UI override real backend task state.
|
||||
- WebSocket data frames should include timestamps, type, and payload when streaming Earth state.
|
||||
- Default heartbeat for real-time streams is 30 seconds unless a protocol states otherwise.
|
||||
|
||||
### Icon System
|
||||
|
||||
Canvas-drawn marker icons for Earth must have canonical SVG sources in:
|
||||
|
||||
```text
|
||||
frontend/public/earth/assets/icons/
|
||||
```
|
||||
|
||||
This directory is the **single source of truth** for icon shapes. The canvas/Three.js drawing code may use inline `Path2D` strings or `<canvas>` draw calls derived from these SVGs, but the geometry must originate here.
|
||||
This directory is the single source of truth for icon geometry. Canvas or Three.js drawing code may use `Path2D` strings or draw calls derived from these SVGs, but the shape must originate here.
|
||||
|
||||
### Naming convention
|
||||
Naming:
|
||||
|
||||
`{module}-{description}.svg` in kebab-case.
|
||||
| Prefix | Context |
|
||||
|--------|---------|
|
||||
| `marker-` | Surface map markers |
|
||||
| `bgp-` | BGP/routing layer icons and event symbols |
|
||||
| `compute-` | Compute center markers |
|
||||
|
||||
| Module prefix | Context |
|
||||
|---------------|---------|
|
||||
| `marker-` | Surface map markers (landing points, etc.) |
|
||||
| `bgp-` | BGP/routing layer icons and event symbols |
|
||||
| `compute-` | Compute center markers |
|
||||
Rules:
|
||||
|
||||
Examples: `marker-landing-point.svg`, `bgp-event-triangle.svg`, `compute-gpu-cluster.svg`
|
||||
- Use `fill="currentColor"` for single-color icons.
|
||||
- Hardcode brand colors only when color is part of icon identity.
|
||||
- State variants are handled by calling code via color/opacity; do not create separate SVGs per state.
|
||||
- Use the native canvas coordinate space as `viewBox`, typically `0 0 128 128`.
|
||||
- When adding an icon, create the SVG, document it in this module, and reference its geometry from rendering code.
|
||||
|
||||
### Existing icons
|
||||
Current icons:
|
||||
|
||||
| File | Used in | Description |
|
||||
|------|---------|-------------|
|
||||
| `marker-landing-point.svg` | `cables.js` | Cable landing point pin (with circular cutout) |
|
||||
| `bgp-collector.svg` | `bgp.js` | BGP collector marker (access_point icon + outer ring) |
|
||||
| `bgp-glow-dot.svg` | `bgp.js` | Base radial glow dot under BGP collector |
|
||||
| `bgp-event-ring.svg` | `bgp.js` | Ring overlay on event markers |
|
||||
| `marker-landing-point.svg` | `cables.js` | Cable landing point pin |
|
||||
| `bgp-collector.svg` | `bgp.js` | BGP collector marker |
|
||||
| `bgp-glow-dot.svg` | `bgp.js` | Base radial glow dot |
|
||||
| `bgp-event-ring.svg` | `bgp.js` | Event ring overlay |
|
||||
| `bgp-event-triangle.svg` | `bgp.js` | Origin anomaly |
|
||||
| `bgp-event-exclamation.svg` | `bgp.js` | Withdraw event |
|
||||
| `bgp-event-wave.svg` | `bgp.js` | Flap event |
|
||||
| `bgp-event-burst.svg` | `bgp.js` | Specific/burst anomaly |
|
||||
| `bgp-event-leak.svg` | `bgp.js` | Route leak |
|
||||
| `bgp-event-dot.svg` | `bgp.js` | Generic event |
|
||||
| `compute-supercomputer.svg` | `compute-centers.js` | Supercomputer (#38bdf8) |
|
||||
| `compute-gpu-cluster.svg` | `compute-centers.js` | GPU cluster (#2dd4bf) |
|
||||
| `compute-supercomputer.svg` | `compute-centers.js` | Supercomputer |
|
||||
| `compute-gpu-cluster.svg` | `compute-centers.js` | GPU cluster |
|
||||
|
||||
### Color rules
|
||||
### Verify
|
||||
|
||||
- Use `fill=”currentColor”` for single-color icons so the caller controls the color (event symbols, landing point)
|
||||
- Hardcode brand colors only when the color is part of the icon identity (compute center types)
|
||||
- State variants (hover, locked, dimmed) are handled by the calling canvas code via color/opacity — **do not create separate SVG files per state**
|
||||
```bash
|
||||
bun run --cwd frontend build
|
||||
rg -n "hover|locked|selected|tooltip|visible|Path2D|drawImage" frontend/public/earth
|
||||
ls frontend/public/earth/assets/icons/
|
||||
```
|
||||
|
||||
### Coordinate system
|
||||
---
|
||||
|
||||
- Use the native canvas coordinate space as the `viewBox` (typically `0 0 128 128`)
|
||||
- Exception: `marker-landing-point.svg` uses a `viewBox` cropped from 1000-unit path space
|
||||
- SVG must visually match the canvas output at the same scale
|
||||
## Module: ai
|
||||
|
||||
### When adding a new icon
|
||||
### Load When
|
||||
|
||||
1. Create the SVG in `assets/icons/` following naming rules above
|
||||
2. Add a row to the table in this section
|
||||
3. Reference the SVG path/geometry in the canvas drawing code — do not invent new shapes directly in JS
|
||||
Editing AI Provider, LLM gateway, AI Playground, prompt templates, model selection, custom collector mapping generation, or LLM-assisted data transformation.
|
||||
|
||||
### Must
|
||||
|
||||
- AI provider endpoint/base URL/model/token configuration belongs in settings/integration config, not hardcoded page state.
|
||||
- The local API route used by the console is not the same as the external LLM provider base URL.
|
||||
- Common LLM provider presets should be selectable and refreshable from provider docs or catalog logic.
|
||||
- Store fallback/default provider config centrally.
|
||||
- Credential previews must reuse the existing product masking convention instead of inventing page-local display logic.
|
||||
- Never send secrets to logs or docs.
|
||||
- LLMs may assist with mapping generation or unknown API exploration, but runtime collection should use saved deterministic mapping rules.
|
||||
- If custom collectors transform into existing domain data, require an explicit target schema.
|
||||
- If custom collectors introduce entirely new data, do not pretend Earth can use it until a corresponding feature exists.
|
||||
- Prompts, mapping schemas, default examples, and provider constants must be centralized.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
rg -n "AI_PROVIDER|provider_api|base_url|api_key|service_token|prompt|mapping" backend aiprovider frontend/src
|
||||
git diff --check -- backend aiprovider frontend/src
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module: release
|
||||
|
||||
### Load When
|
||||
|
||||
The user asks to `发版`, bump version, release, commit/push release work, or update changelog/version history as part of a release.
|
||||
|
||||
### Must
|
||||
|
||||
- Treat release work as release workflow, not a plain commit.
|
||||
- Use `.codex/skills/release/SKILL.md` when Codex performs a release.
|
||||
- Version bump rules:
|
||||
- `feature` -> `+0.1.0`
|
||||
- `improvement` -> `+0.0.1`
|
||||
- `bugfix` -> `+0.0.1`
|
||||
- `docs`, `maintenance`, `refactor` do not bump unless explicitly requested
|
||||
- Mixed bugfix and small feature/UI work defaults to `improvement` unless the user explicitly chooses another release type.
|
||||
- A release bump updates all version-bearing files together:
|
||||
- `VERSION`
|
||||
- `frontend/package.json`
|
||||
- `pyproject.toml`
|
||||
- `uv.lock`
|
||||
- A release bump updates release records together:
|
||||
- `docs/CHANGELOG.md`
|
||||
- `docs/version-history.md`
|
||||
- `uv.lock` must be regenerated by `uv lock`, never edited manually.
|
||||
- Before committing a release, verify target version consistency.
|
||||
- Before pushing a release, run the smallest relevant validation for the changed scope and report what was or was not validated.
|
||||
- Do not include generated runtime output directories in release commits.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
git branch --show-current
|
||||
git status --short
|
||||
cat VERSION
|
||||
rg -n "\"version\":|^version =|version = " frontend/package.json pyproject.toml uv.lock
|
||||
git diff --stat HEAD
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user