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

12 KiB
Raw Blame History

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:

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:

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:

@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
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:

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
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:

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:

hub_ports = {
    "binglun_1": "west",
    "binglun_2": "south",
    "binglun_3": "east",
}
  • Step 4: Re-seed and verify GREEN

Run:

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
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:

{
    "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:

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
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
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
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

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:

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
git commit -m "test(topology): verify repaired temporal topology"