Skip to content

refactor(phase8 #50 G4a): split governance routes + /api/v2/audit-logs hard-cut#1684

Merged
earayu merged 2 commits into
mainfrom
bryce/phase8-task50-g4a-audit-logs-v2
Apr 25, 2026
Merged

refactor(phase8 #50 G4a): split governance routes + /api/v2/audit-logs hard-cut#1684
earayu merged 2 commits into
mainfrom
bryce/phase8-task50-g4a-audit-logs-v2

Conversation

@earayu
Copy link
Copy Markdown
Collaborator

@earayu earayu commented Apr 25, 2026

Summary

Phase 8 second batch G4a — per architect canonical D7-2 (msg=94f663f2 §3.2.2) + Option A locked (msg=25a30445), the legacy combined governance_router (which mounted both API keys and audit logs at /api/v1) is split into two single-purpose routers so #50 G4a (audit-logs) and #51 G4b (apikeys) can migrate to /api/v2 independently.

What this PR does

  • Hard-cut /api/v1/audit-logs*/api/v2/audit-logs* (no v1 alias). aperag/openapi_spec.py:23 hidden filter is preserved so audit-logs stay outside the public OpenAPI spec.
  • Apikeys router stays at /api/v1/apikeys* for now — [Features] integrate graph database NebulaGraph as a data source #51 G4b will flip its mount to /api/v2 in a 1-line follow-up after this PR lands.

File structure choice — Option A (β) per architect spec

Per-router file convention, matching #36 / G1 / G2 / G3 / G4d carved routers:

  • aperag/domains/governance/api/routes.py (combined) → renamed to audit_routes.py with audit handlers only
  • New aperag/domains/governance/api/apikeys_routes.py with apikey handlers
  • Both files have identical handler signatures to the original — no behavior change

Deliberately chose (β) over (α) "single file, two router objects" because the per-router file pattern aligns with all existing carved routers and makes future boundary gates (e.g. domain ownership scans) easier.

Changes

File Change
aperag/domains/governance/api/audit_routes.py Rename from routes.py; trim to audit-log handlers + needed imports
aperag/domains/governance/api/apikeys_routes.py New file; apikey handlers + needed imports
aperag/app.py Replace single import+mount with two: audit_router → /api/v2, apikeys_router → /api/v1 (G4b will flip)
tests/unit_test/test_modularization_boundaries.py New gate test_no_module_imports_legacy_governance_routes — analogous to #48's test_no_module_imports_legacy_views_settings. Total 23 boundary tests.
tests/unit_test/test_v1_ghost_guard.py Shrink TRANSITIONAL_V1_PREFIXES removing /api/v1/audit-logs. 11 → 10 prefixes — clean-as-you-go pattern from #1675 H3.

Net: 5 files / +114 / -59 LOC.

FE follow-up

web/src/features/audit/{client,server}-api.ts v1→v2 caller sync will be a sibling PR by @dongdong (per architect spec) once the backend OpenAPI diff is available.

Coordination notes

Test plan

  • uv run python -m pytest tests/unit_test/test_modularization_boundaries.py tests/unit_test/test_v1_ghost_guard.py -x -q25 passed
  • uv run python -m pytest tests/unit_test -q --deselect 'tests/unit_test/test_web_typed_api_contract.py::test_phase1_fe_complete_identity_auth_admin_audit_adapter_boundary'698 passed / 29 skipped / 1 deselected / 0 failed
  • uv run ruff check aperag/ tests/ config/ → clean
  • uv run ruff format --check aperag/ tests/ → clean (408 files already formatted)
  • e2e-http-smoke / hurl — runs in CI; no hurl currently hits /api/v1/audit-logs* (per [BUG] value too long for type character varying(4096) #42 inventory)

CR ask

@chenyexuan replacement blocker-level minimal CR (per Weston-replacement routing) — pure router split + prefix flip + 2 new tests; should be tight.
@符炫炜 canonical drift quick check — Option A (β) alignment.
@dongdong audit-logs FE caller sync sibling PR coordination.

🤖 Generated with Claude Code

earayu added 2 commits April 25, 2026 17:44
…s hard-cut

Phase 8 second batch G4a — per architect canonical D7-2 (msg=94f663f2 §3.2.2)
+ Option A locked (msg=25a30445), the legacy combined ``governance_router``
(which mounted both API keys and audit logs at ``/api/v1``) is split into two
single-purpose routers so #50 G4a (audit-logs) and #51 G4b (apikeys) can
migrate to ``/api/v2`` independently.

This PR lands the audit-logs side: ``/api/v1/audit-logs*`` is hard-cut to
``/api/v2/audit-logs*`` (no v1 alias). The ``aperag/openapi_spec.py:23``
hidden filter is preserved so audit-logs stay outside the public OpenAPI
spec. The apikeys router stays at ``/api/v1/apikeys*`` for now — #51 will
flip its mount to ``/api/v2`` in a 1-line follow-up.

File structure follows architect-recommended (β) pattern: per-router file,
matching the convention of #36 / G1 / G2 / G3 / G4d carved routers.

Changes:

* Rename ``aperag/domains/governance/api/routes.py`` →
  ``aperag/domains/governance/api/audit_routes.py``; trim it to only audit
  routes + needed imports.
* New ``aperag/domains/governance/api/apikeys_routes.py`` — apikey routes +
  needed imports; identical handler signatures (no behavior change).
* ``aperag/app.py`` — replace single ``governance_router`` import + mount
  with two imports and split mounts:
  ``audit_router → /api/v2`` (this PR's hard-cut),
  ``apikeys_router → /api/v1`` (preserved; #51 will flip).
* New boundary test
  ``test_no_module_imports_legacy_governance_routes`` — analogous to
  ``test_no_module_imports_legacy_views_settings`` from #48, forbids re-import
  of the deleted combined ``aperag.domains.governance.api.routes`` module.
  Total boundary tests now 23.
* ``tests/unit_test/test_v1_ghost_guard.py`` — TRANSITIONAL_V1_PREFIXES
  shrinks ``/api/v1/audit-logs`` (G4a removed). 11 → 10 prefixes remaining,
  matching the "clean as you go" pattern established in #1675 H3.

FE follow-up: ``web/src/features/audit/{client,server}-api.ts`` v1→v2
caller sync will be a sibling PR by @dongdong (per architect spec) once the
backend OpenAPI diff is available.

Gates: 25/25 boundary + ghost guard, 698 pass / 29 skip / 1 deselect / 0
fail unit suite, ruff check + format clean.
… to /api/v2 + add regression guard

Per @chenyexuan replacement CR (msg=1b0fe11c) — the original G4a backend
correctly mounted the audit router at ``/api/v2`` but missed updating the
``HIDDEN_FROM_PUBLIC_PATH_PREFIXES`` filter in ``aperag/openapi_spec.py``,
which was still keyed on ``/api/v1/audit-logs``. After the mount switch
the filter no longer matched any path, leaking ``/api/v2/audit-logs*``
into the public OpenAPI spec — a violation of D7-2 ``hidden 保持`` and
architect canonical msg=25a30445.

Fix:

- ``aperag/openapi_spec.py:23`` — flip prefix from
  ``/api/v1/audit-logs`` → ``/api/v2/audit-logs``. One-line.
- ``tests/unit_test/test_openapi_spec.py``:
  - Update the existing synthetic-spec filter test to assert v2 paths
    (was v1; the assertion would otherwise pass vacuously after the
    real prefix changed).
  - Add new ``test_audit_logs_hidden_from_public_openapi_for_live_app``
    — runs the real ``aperag.app`` through ``build_full_openapi_spec``
    + ``filter_public_openapi`` and asserts no audit-logs path leaks
    into the public spec. This is the regression guard the architect
    + reviewer recommended so a future refactor cannot silently re-leak.

No other write set in scope. Pure 1-line canonical fix + 1 new test +
1 small test rewrite. The Option A router split, mount changes,
boundary gate, ghost guard shrink, and #51 hand-off shape from the
original PR head are unchanged.
@earayu earayu force-pushed the bryce/phase8-task50-g4a-audit-logs-v2 branch from cd41ebd to 94bfb51 Compare April 25, 2026 09:47
@earayu earayu merged commit b5c2509 into main Apr 25, 2026
2 of 3 checks passed
@earayu earayu deleted the bryce/phase8-task50-g4a-audit-logs-v2 branch April 25, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant