Files
KSP_project/docs/superpowers/plans/2026-07-09-guanghan-topology-repair.md
T

367 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Guanghan Temporal Topology Repair Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Repair the approved audit findings so the Wiki topology is genuinely database-driven, historically accurate, deployable, navigable, readable, and covered by automated and screenshot verification.
**Architecture:** The Flask topology service will serve both the local demo page and same-origin JSON endpoints. PostgreSQL remains the source of truth for missions, components, connections, state, and layout; the browser will create SVG nodes and edges entirely from the snapshot response. Layout-only presentation metadata stays in layout tables, while operational facts remain in temporal topology tables.
**Tech Stack:** PostgreSQL 16, psycopg 3, Flask 3, vanilla JavaScript, SVG, Python unittest, Playwright.
---
## File map
- `topology_api/config.py`: secure environment loading and service port.
- `topology_api/app.py`: Flask app, health endpoint, and local demo-page serving.
- `topology_api/routes/snapshot.py`: JSON request validation, snapshot and mission-list endpoints.
- `topology_api/queries/snapshot_queries.py`: mission list, snapshot, component provenance, and layout queries.
- `topology_api/migrations/005_layout_presentation.sql`: layout node membership and presentation metadata.
- `topology_api/seed/seed_ghc01_to_ghc05.py`: complete mission anchors, correct temporal facts, component data.
- `topology_api/seed/seed_layout.py`: persisted node groups, concise labels, and radiator decorations.
- `data/wiki/guanghan_topology_demo.html`: same-origin API client, dynamic SVG renderer, time controls, modal, navigation.
- `tests/test_topology_api.py`: real API/database contract coverage.
- `tests/test_guanghan_topology_demo.py`: current source-level page contract.
- `tests/test_guanghan_topology_e2e.py`: browser behavior against the real Flask app and database.
### Task 1: Secure and unify local serving
**Files:**
- Modify: `topology_api/config.py`
- Modify: `topology_api/app.py`
- Create: `tests/test_topology_api.py`
- Modify: `data/wiki/guanghan_topology_demo.html`
- [ ] **Step 1: Write failing tests**
Add tests asserting:
```python
def test_app_serves_demo_page_and_health(client):
assert client.get("/health").status_code == 200
page = client.get("/guanghan_topology_demo.html")
assert page.status_code == 200
assert "广寒基地拓扑结构" in page.get_data(as_text=True)
def test_page_uses_same_origin_api():
html = HTML_PATH.read_text(encoding="utf-8")
assert "http://localhost:5000" not in html
assert "const API_BASE = ''" in html
def test_config_has_no_default_password():
source = CONFIG_PATH.read_text(encoding="utf-8")
assert 'os.environ.get("PGPASSWORD",' not in source
```
- [ ] **Step 2: Run tests and verify RED**
Run:
```text
python -m unittest tests.test_topology_api -v
```
Expected failures: no demo route, hardcoded API host, and hardcoded password.
- [ ] **Step 3: Implement minimal serving and configuration**
Use `load_dotenv(PROJECT_ROOT / ".env")`, require `PGHOST`, `PGPORT`, `PGUSER`, and `PGPASSWORD`, default only the non-secret topology database name, and default the topology service port to `8080`.
Serve the existing page:
```python
@app.get("/guanghan_topology_demo.html")
def topology_demo():
return send_from_directory(WIKI_DIR, "guanghan_topology_demo.html")
```
Use `const API_BASE = ''` so browser requests remain same-origin.
- [ ] **Step 4: Run tests and verify GREEN**
Run the Task 1 tests and confirm zero failures.
- [ ] **Step 5: Commit**
```text
git commit -m "fix(topology): secure and unify demo serving"
```
### Task 2: Validate API requests and expose missions
**Files:**
- Modify: `topology_api/routes/snapshot.py`
- Modify: `topology_api/queries/snapshot_queries.py`
- Modify: `tests/test_topology_api.py`
- [ ] **Step 1: Write failing API tests**
Cover:
```python
def test_invalid_time_is_json_400(client):
response = client.get("/api/v1/bases/guanghan/snapshot?at=not-a-time")
assert response.status_code == 400
assert response.json["error"] == "invalid_time"
def test_time_and_mission_preserve_context(client):
response = client.get(
"/api/v1/bases/guanghan/snapshot"
"?mission=GHC-01&at=2025-01-02T08:00:00%2B08:00"
)
assert response.status_code == 200
assert response.json["query"]["requestedMission"] == "GHC-01"
assert response.json["query"]["anchorSource"] == "explicit_time"
def test_mission_list_is_database_backed(client):
response = client.get("/api/v1/bases/guanghan/missions")
assert response.status_code == 200
assert "GHC-05" in [row["code"] for row in response.json["missions"]]
```
- [ ] **Step 2: Run tests and verify RED**
Expected failures: invalid time is 500 and the mission endpoint is 404.
- [ ] **Step 3: Implement validation and mission query**
Parse ISO 8601 with `datetime.fromisoformat(value.replace("Z", "+00:00"))`, require a timezone, normalize to ISO output, and return JSON 400 on failure.
Add `fetch_missions(base_code)` returning code, title, occurrence status, and maximum event time ordered chronologically.
- [ ] **Step 4: Run tests and verify GREEN**
Run `python -m unittest tests.test_topology_api -v`.
- [ ] **Step 5: Commit**
```text
git commit -m "fix(topology): validate time and list missions"
```
### Task 3: Correct temporal seed facts
**Files:**
- Modify: `topology_api/seed/seed_ghc01_to_ghc05.py`
- Modify: `tests/test_topology_api.py`
- [ ] **Step 1: Write failing mission-anchor and port tests**
Assert all nine expected terminal instants:
```python
EXPECTED_LAST_EVENTS = {
"BL-01": "2024-05-17T23:01:00+00:00",
"GHC-01": "2024-06-16T07:23:00+00:00",
"JC-01": "2024-06-17T08:19:00+00:00",
"BL-02": "2024-06-23T05:23:00+00:00",
"GHC-02": "2024-07-15T03:16:00+00:00",
"GHC-03": "2024-08-30T12:45:00+00:00",
"BL-03": "2024-09-23T19:20:00+00:00",
"GHC-04": "2024-11-20T13:57:00+00:00",
"GHC-05": "2025-01-22T10:52:00+00:00",
}
```
Also assert Binglun 1/2/3 connect to west/south/east hub ports respectively.
- [ ] **Step 2: Run and verify RED**
Expected: five terminal-time mismatches and two Binglun port mismatches.
- [ ] **Step 3: Add terminal checkpoint events and correct ports**
Add a non-topology `mission_checkpoint` event at each missing terminal instant. Map Binglun connections:
```python
hub_ports = {
"binglun_1": "west",
"binglun_2": "south",
"binglun_3": "east",
}
```
- [ ] **Step 4: Re-seed and verify GREEN**
Run:
```text
python -m topology_api.seed.seed_ghc01_to_ghc05
python -m topology_api.seed.seed_layout
python -m unittest tests.test_topology_api -v
```
- [ ] **Step 5: Commit**
```text
git commit -m "fix(topology): correct mission anchors and rover ports"
```
### Task 4: Persist complete diagram semantics
**Files:**
- Create: `topology_api/migrations/005_layout_presentation.sql`
- Modify: `topology_api/seed/seed_layout.py`
- Modify: `topology_api/queries/snapshot_queries.py`
- Modify: `tests/test_topology_api.py`
- [ ] **Step 1: Write failing layout tests**
Require every layout node to expose:
```python
{
"nodeKey": "...",
"memberKeys": ["..."],
"displayMeta": "...",
"displayHint": "...",
"decorations": {"radiator": True}
}
```
Assert west and east group membership and exactly four radiator nodes.
- [ ] **Step 2: Run and verify RED**
Expected: missing layout fields.
- [ ] **Step 3: Add schema and query support**
Add `display_meta text`, `display_hint text`, and `decorations jsonb` to `diagram_nodes`, plus:
```sql
CREATE TABLE topology.diagram_node_members (
diagram_node_id uuid NOT NULL REFERENCES topology.diagram_nodes(id) ON DELETE CASCADE,
component_id uuid NOT NULL REFERENCES topology.components(id),
display_order integer NOT NULL DEFAULT 0,
PRIMARY KEY (diagram_node_id, component_id)
);
```
Seed direct and grouped members, concise approved labels, and radiator decorations.
- [ ] **Step 4: Apply migration, seed, and verify GREEN**
Execute migration transactionally with psycopg, seed layout, then run API tests.
- [ ] **Step 5: Commit**
```text
git commit -m "feat(topology): persist complete diagram semantics"
```
### Task 5: Render SVG entirely from snapshot data
**Files:**
- Replace rendering section: `data/wiki/guanghan_topology_demo.html`
- Modify: `tests/test_guanghan_topology_demo.py`
- Modify: `tests/test_guanghan_topology_e2e.py`
- [ ] **Step 1: Write failing renderer contracts**
Assert the source no longer contains hardcoded topology nodes or fixed connection inventory, uses `currentData.layout`, renders connections from `currentData.connections`, renders radiators only from visible node decorations, and retains future expansion comments.
Add E2E assertions:
- BL-01 has one node, zero connection lines, zero radiators;
- GHC-01 has two nodes and zero connection lines;
- GHC-05 has 14 visual nodes, two green pressurized lines, four radiators;
- no visible SVG text extends outside its owning node rectangle.
- [ ] **Step 2: Run and verify RED**
Expected: fixed SVG and historical visibility failures.
- [ ] **Step 3: Implement dynamic renderer**
Create SVG elements with `document.createElementNS`, map component keys to persisted layout nodes, aggregate multiple database edges that share visual endpoints and line class, calculate rectangle-edge intersections from node centers, and attach click/keyboard handlers after node creation.
Use concise persisted `displayMeta`/`displayHint`; truncate text based on node width and add a `<title>` for full content. Render a radiator only when the visible layout node declares `decorations.radiator`.
- [ ] **Step 4: Run and verify GREEN**
Run source and browser tests.
- [ ] **Step 5: Commit**
```text
git commit -m "feat(topology): render temporal graph from database"
```
### Task 6: Repair controls, navigation, errors, and modal semantics
**Files:**
- Modify: `topology_api/queries/snapshot_queries.py`
- Modify: `data/wiki/guanghan_topology_demo.html`
- Modify: `tests/test_topology_api.py`
- Modify: `tests/test_guanghan_topology_e2e.py`
- [ ] **Step 1: Write failing behavior tests**
Cover:
- mission dropdown comes from `/missions`;
- mission plus explicit time sends both query parameters and retains mission subtitle;
- invalid time shows a localized validation error without a request;
- browser back reloads the URL-selected snapshot;
- modal “任务节点” displays introduction mission and time;
- modal close restores focus to the triggering node;
- internal anchor enum values are localized;
- database strings are inserted with `textContent`, not `innerHTML`.
- [ ] **Step 2: Run and verify RED**
Expected: each approved audit defect reproduces.
- [ ] **Step 3: Implement minimal behavior**
Return `introducedMission` and `introducedAt` for components. Fetch mission options from the API. Always send both non-empty parameters. Add `popstate`, ISO validation, localized anchor labels, stable Asia/Shanghai formatting, safe line rendering helpers, and trigger-focus restoration.
- [ ] **Step 4: Run and verify GREEN**
Run API, source, and E2E tests.
- [ ] **Step 5: Commit**
```text
git commit -m "fix(topology): repair controls navigation and details"
```
### Task 7: Full service and screenshot verification
**Files:**
- Update only if a verified defect remains.
- Create screenshots under `demo/topology_repair/`.
- [ ] **Step 1: Run the complete automated suite**
```text
python -m unittest tests.test_topology_api tests.test_guanghan_topology_demo tests.test_guanghan_topology_e2e -v
```
Expected: all pass with no browser console errors.
- [ ] **Step 2: Restart the topology service**
Stop only the verified processes listening on ports 5000 and 8080, then start:
```text
python topology_api/app.py
```
with a hidden window and working directory at the repository root.
- [ ] **Step 3: Test and screenshot every mission**
Visit each of the nine tasks through the page control, verify node/edge/radiator counts, open representative modal details, test explicit time, invalid time, back navigation, and 390×844 layout.
- [ ] **Step 4: Review diffs and requirements**
Confirm no unrelated files changed, no secret remains in tracked source, all approved audit items have a test or explicit evidence, and the original GHC-05 spatial skeleton remains intact.
- [ ] **Step 5: Commit verification evidence**
```text
git commit -m "test(topology): verify repaired temporal topology"
```