Files
certctl/scripts/ci-guards/T-1-frontend-page-coverage.sh
shankar0123 8908c8ff5c web, docs: IssuerHierarchyPage + sysadmin runbook + connectors row (Rank 8 commit 5)
Final commit of the 5-commit Rank 8 chain. Operator-facing surface
on top of the service + handler layers shipped in commits 1-4.

Frontend (web/src):
  - api/client.ts: 3 new functions + IntermediateCA interface
    (listIntermediateCAs, getIntermediateCA, retireIntermediateCA).
  - pages/IssuerHierarchyPage.tsx: recursive nested <ul> render of
    the hierarchy tree at /issuers/:id/hierarchy. buildHierarchyTree
    is a pure helper that walks the flat list and groups children
    on parent_ca_id; the dendrogram view is parking-lot work tracked
    in WORKSPACE-ROADMAP. Two-phase retire UX surfaces 'Retire…'
    then 'Confirm retire (terminal)' when the row is in retiring
    state. Admin gate is enforced at the API; the page renders the
    backend's 403 as ErrorState for non-admin callers.
  - main.tsx: register the new /issuers/:id/hierarchy route.

CI guard update:
  - scripts/ci-guards/T-1-frontend-page-coverage.sh: add
    IssuerHierarchyPage to the deferred-test allowlist with the
    standard 'why deferred' comment. Admin-gate + recursive build
    semantics are already pinned at the backend layer
    (intermediate_ca_test.go service tests + intermediate_ca_test.go
    handler triplet). Vitest test deferred until next feature
    change touches the page.

Docs:
  - docs/intermediate-ca-hierarchy.md: new operator runbook
    covering:
      Concepts (HierarchyMode 'single' vs 'tree', defense-in-depth
        on key bytes never persisting on rows).
      Lifecycle states + drain-first semantics
        (active → retiring → retired with active-children gate).
      Three deployment patterns: 4-level FedRAMP boundary CA,
        3-level financial-services policy CA, 2-level internal
        PKI.
      RFC 5280 enforcement (§3.2 self-signed, §4.2.1.9 path-length
        tightening, §4.2.1.10 NameConstraints subset).
      Migration from single → tree using the load-bearing
        TestLocal_HierarchyMode_SingleVsTree_ByteIdentical pin as
        the canary.
      API reference + observability (IntermediateCAMetrics
        Prometheus exposure).
      Known limitations + Rank-8 follow-on roadmap.

  - docs/connectors.md: extend the Built-in Local CA section with
    a 'Tree mode (Rank 8)' paragraph describing the new chain
    assembly path + cross-link to docs/intermediate-ca-hierarchy.md.

Roadmap:
  - WORKSPACE-ROADMAP.md: 5 follow-on items under a new
    'Intermediate CA hierarchy extensions (Rank 8 V2 follow-ons)'
    bullet block:
      HSM-backed roots (PKCS#11 / cloud KMS drivers via existing
        signer.Driver interface — no service-layer change needed).
      Automated CA rotation (parallel-validity windows ahead of
        expiry).
      Intra-hierarchy CRL chaining (per-CA CRL endpoints stitched
        at issue time).
      NameConstraints policy templates (FedRAMP / financial /
        internal PKI declarative templates instead of hand-rolled
        JSON).
      D3 dendrogram visualization (separate page so the existing
        list view stays the default + the dep stays opt-in).

Verified locally:
  gofmt: clean.
  go vet ./...: exit 0.
  tsc --noEmit (web/): exit 0 (no TypeScript errors).
  go test -short -count=1 ./internal/api/handler/... + service +
    local: ok across all three packages, 4-5s each.
  All 24 CI guards: clean
    (T-1 frontend-page-coverage with the new
     IssuerHierarchyPage allowlist entry; openapi-handler-parity,
     M-008 admin-gate, every other guard untouched).

Rank 8 chain complete:
  66d2af3  domain, migrations: IntermediateCA type + intermediate_cas
           + Issuer.HierarchyMode (commit 1)
  fb54ebc  service: IntermediateCAService + IntermediateCAMetrics
           + RFC 5280 enforcement (commit 2)
  62523fb  service: 10 IntermediateCAService tests + in-memory fake
           repo (commit 2.5)
  ae597f7  local: tree-mode chain assembly + byte-equivalence pin
           (commit 3 — load-bearing backwards-compat refuse-to-ship
           pin in TestLocal_HierarchyMode_SingleVsTree_ByteIdentical)
  34adcfb  api, handler: 4 admin-gated CA hierarchy endpoints +
           OpenAPI (commit 4)
  HEAD     web, docs: IssuerHierarchyPage + sysadmin runbook +
           connectors row (this commit)

Reference: cowork/rank-8-intermediate-ca-hierarchy-prompt.md, commit 5.
2026-05-04 02:33:48 +00:00

62 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# scripts/ci-guards/T-1-frontend-page-coverage.sh
#
# T-1 closure (cat-s2-c24a548076c6): pre-T-1 only 3 of 28 pages
# had Vitest coverage. T-1 lifted that to 11/28 by writing tests
# for the 8 highest-leverage pages (CertificatesPage filter +
# pagination state, the new B-1 Edit modals, the D-2 type-trim
# render sites, etc.). The remaining pages are deferred to per-
# page commits — when the next feature change touches them, the
# test gets added in the same commit. This script blocks new
# pages from landing without tests.
#
# Allowlist: pages that are explicitly deferred — listed below
# with a one-line "why deferred" justification. Each entry must
# be removed when the page gets its test.
# - LoginPage: static auth form, no business logic
# - AuditPage: read-only timeline; D-2 already trimmed
# - ShortLivedPage: derived view of certs already covered by CertificatesPage
# - DigestPage: server-rendered digest; minimal client logic
# - ObservabilityPage: exposes Prometheus / Grafana links only
# - HealthMonitorPage: wraps M-006 health check timeline; M-006 has its own tests
# - NetworkScanPage: wraps the network scanner UX; SSRF unit-tested in domain
# - JobsPage: covered transitively via AgentDetailPage
# - JobDetailPage: drill-down view; covered transitively via JobsPage
# - AgentFleetPage: bulk overview; covered transitively via AgentsPage
# - ProfilesPage: CRUD form; mirrors PoliciesPage shape (covered)
# - CertificateDetailPage: drill-down view; covered transitively via CertificatesPage
# - IssuerDetailPage: drill-down view; covered transitively via IssuersPage
# - IssuerHierarchyPage: Rank 8 admin-gated hierarchy render; admin gate +
# recursive build tested at the API + service layers
# (intermediate_ca_test.go + intermediate_ca_test.go
# handler triplet); defer Vitest until the next
# feature change touches the page
# - TargetDetailPage: drill-down view; covered transitively via TargetsPage
#
# See coverage-gap-audit-2026-04-24-v5/unified-audit.md
# cat-s2-c24a548076c6 for closure rationale.
set -e
ALLOW='^(LoginPage|AuditPage|ShortLivedPage|DigestPage|ObservabilityPage|HealthMonitorPage|NetworkScanPage|JobsPage|JobDetailPage|AgentFleetPage|ProfilesPage|CertificateDetailPage|IssuerDetailPage|IssuerHierarchyPage|TargetDetailPage)$'
UNTESTED=""
for f in web/src/pages/*.tsx; do
base=$(basename "$f" .tsx)
case "$f" in *.test.tsx) continue ;; esac
if [ -f "web/src/pages/${base}.test.tsx" ]; then continue; fi
if echo "$base" | grep -qE "$ALLOW"; then continue; fi
UNTESTED="${UNTESTED}${base} "
done
if [ -n "$UNTESTED" ]; then
echo "::error::T-1 regression: page(s) without sibling .test.tsx and not on the deferred allowlist:"
echo " $UNTESTED"
echo ""
echo "Either add web/src/pages/<Page>.test.tsx (mirror NotificationsPage.test.tsx),"
echo "or add the page to the ALLOW pattern in scripts/ci-guards/T-1-frontend-page-coverage.sh"
echo "with a one-line 'why deferred' comment. See"
echo "coverage-gap-audit-2026-04-24-v5/unified-audit.md cat-s2-c24a548076c6"
echo "for closure rationale."
exit 1
fi
ALLOWLIST_SIZE=$(echo "$ALLOW" | tr '|' '\n' | wc -l)
echo "T-1 frontend-page-coverage: clean (allowlist size: $ALLOWLIST_SIZE pages deferred)."