# 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 `` 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" ```