feat(inventory): enforce high-risk contextual help
This commit is contained in:
@@ -31,6 +31,9 @@ ENDPOINT_SURFACE_CATEGORIES = {
|
||||
DEFAULT_ENDPOINT_DECLARATIONS = (
|
||||
META_ROOT / "tools" / "inventory" / "endpoint-surface-declarations.json"
|
||||
)
|
||||
DEFAULT_HIGH_RISK_HELP_BASELINE = (
|
||||
META_ROOT / "tools" / "inventory" / "high-risk-help-baseline.json"
|
||||
)
|
||||
REQUIRED_LOCALES = ("de", "en")
|
||||
REFERENCE_LOCALE = "de"
|
||||
|
||||
@@ -75,6 +78,12 @@ def main() -> int:
|
||||
default=DEFAULT_ENDPOINT_DECLARATIONS,
|
||||
help="Versioned endpoint-surface declaration registry.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--high-risk-help-baseline",
|
||||
type=Path,
|
||||
default=DEFAULT_HIGH_RISK_HELP_BASELINE,
|
||||
help="Versioned upper bound for high-risk controls without exact F1 help.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
catalog = json.loads((META_ROOT / "repositories.json").read_text(encoding="utf-8"))
|
||||
@@ -85,11 +94,15 @@ def main() -> int:
|
||||
endpoint_declarations = _load_endpoint_declarations(
|
||||
args.endpoint_declarations.resolve()
|
||||
)
|
||||
high_risk_help_baseline = _load_high_risk_help_baseline(
|
||||
args.high_risk_help_baseline.resolve()
|
||||
)
|
||||
inventory = _assemble_inventory(
|
||||
webui=webui,
|
||||
backend_endpoints=backend_endpoints,
|
||||
manifests=manifests,
|
||||
endpoint_declarations=endpoint_declarations,
|
||||
high_risk_help_baseline=high_risk_help_baseline,
|
||||
runtime_snapshot=(
|
||||
_load_runtime_snapshot(args.runtime_snapshot.resolve())
|
||||
if args.runtime_snapshot is not None
|
||||
@@ -164,6 +177,28 @@ def _strict_failures(
|
||||
f"{len(declaration_health['stale_runtime_routes'])} runtime route "
|
||||
"declarations have no WebUI implementation"
|
||||
)
|
||||
help_health = inventory.get("help_health", {})
|
||||
if check_declarations and help_health.get("invalid_risk_annotations"):
|
||||
failures.append(
|
||||
f"{len(help_health['invalid_risk_annotations'])} controls use an "
|
||||
"unsupported contextual-help risk class"
|
||||
)
|
||||
if check_declarations and help_health.get("baseline_regression"):
|
||||
failures.append(
|
||||
f"{len(help_health['missing_exact_high_risk_help'])} high-risk "
|
||||
"controls lack exact F1 help; baseline permits at most "
|
||||
f"{help_health['baseline_maximum_missing']}"
|
||||
)
|
||||
if check_declarations and help_health.get("unresolved_exact_high_risk_help"):
|
||||
failures.append(
|
||||
f"{len(help_health['unresolved_exact_high_risk_help'])} high-risk "
|
||||
"controls reference no manifest DocumentationTopic help context"
|
||||
)
|
||||
if check_declarations and help_health.get("high_risk_help_without_german"):
|
||||
failures.append(
|
||||
f"{len(help_health['high_risk_help_without_german'])} high-risk "
|
||||
"controls resolve to documentation without complete German content"
|
||||
)
|
||||
runtime_comparison = inventory.get("runtime_comparison")
|
||||
if (
|
||||
check_declarations
|
||||
@@ -355,6 +390,29 @@ def _extract_manifests(
|
||||
}
|
||||
for permission in manifest.permissions
|
||||
],
|
||||
"documentation": [
|
||||
{
|
||||
"id": topic.id,
|
||||
"help_contexts": sorted(
|
||||
{
|
||||
str(context)
|
||||
for context in topic.metadata.get(
|
||||
"help_contexts", ()
|
||||
)
|
||||
if isinstance(context, str) and context.strip()
|
||||
}
|
||||
),
|
||||
"german_complete": (
|
||||
isinstance(topic.translations.get("de"), dict)
|
||||
and all(
|
||||
isinstance(topic.translations["de"].get(field), str)
|
||||
and topic.translations["de"][field].strip()
|
||||
for field in ("title", "summary", "body")
|
||||
)
|
||||
),
|
||||
}
|
||||
for topic in manifest.documentation
|
||||
],
|
||||
"architecture": (
|
||||
manifest.architecture.to_dict()
|
||||
if manifest.architecture is not None
|
||||
@@ -400,6 +458,7 @@ def _assemble_inventory(
|
||||
backend_endpoints: list[dict[str, Any]],
|
||||
manifests: list[dict[str, Any]],
|
||||
endpoint_declarations: dict[tuple[str, str, str], dict[str, Any]],
|
||||
high_risk_help_baseline: dict[str, Any] | None = None,
|
||||
runtime_snapshot: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
frontend_refs = webui["frontendApiReferences"]
|
||||
@@ -471,8 +530,47 @@ def _assemble_inventory(
|
||||
if any(key not in catalog_keys.get(locale, set()) for locale in expected_locales)
|
||||
]
|
||||
fields = webui["fields"]
|
||||
actions = webui.get("actions", [])
|
||||
help_candidates = [field for field in fields if field["helpCandidate"]]
|
||||
dynamic_help = [field for field in fields if field.get("helpDynamic")]
|
||||
controls = [*fields, *actions]
|
||||
high_risk_controls = [item for item in controls if item.get("helpRisk")]
|
||||
missing_exact_high_risk_help = [
|
||||
item for item in controls if item.get("highRiskHelpMissing")
|
||||
]
|
||||
invalid_risk_annotations = [
|
||||
item
|
||||
for item in controls
|
||||
if item.get("helpRiskSource") == "invalid_explicit"
|
||||
]
|
||||
documentation_contexts = {
|
||||
context: {
|
||||
"module_id": manifest["id"],
|
||||
"topic_id": topic["id"],
|
||||
"german_complete": topic["german_complete"],
|
||||
}
|
||||
for manifest in manifests
|
||||
for topic in manifest.get("documentation", [])
|
||||
for context in topic.get("help_contexts", [])
|
||||
}
|
||||
unresolved_exact_high_risk_help = [
|
||||
item
|
||||
for item in high_risk_controls
|
||||
if item.get("helpExact")
|
||||
and not item.get("helpContextDynamic")
|
||||
and item.get("helpContextId") not in documentation_contexts
|
||||
]
|
||||
high_risk_help_without_german = [
|
||||
item
|
||||
for item in high_risk_controls
|
||||
if item.get("helpContextId") in documentation_contexts
|
||||
and not documentation_contexts[item["helpContextId"]]["german_complete"]
|
||||
]
|
||||
baseline_maximum_missing = (
|
||||
high_risk_help_baseline["maximum_missing_exact_help"]
|
||||
if high_risk_help_baseline is not None
|
||||
else None
|
||||
)
|
||||
governance_adoption = Counter(
|
||||
dimension["adoption"]
|
||||
for manifest in manifests
|
||||
@@ -499,10 +597,34 @@ def _assemble_inventory(
|
||||
"modules": manifests,
|
||||
"interface_declarations": source_declarations,
|
||||
"declaration_health": declaration_health,
|
||||
"help_health": {
|
||||
"supported_risk_classes": sorted(
|
||||
{
|
||||
str(item["helpRisk"])
|
||||
for item in high_risk_controls
|
||||
if item.get("helpRisk")
|
||||
}
|
||||
),
|
||||
"high_risk_controls": high_risk_controls,
|
||||
"missing_exact_high_risk_help": missing_exact_high_risk_help,
|
||||
"invalid_risk_annotations": invalid_risk_annotations,
|
||||
"unresolved_exact_high_risk_help": unresolved_exact_high_risk_help,
|
||||
"high_risk_help_without_german": high_risk_help_without_german,
|
||||
"dynamic_owner_context_controls": [
|
||||
item
|
||||
for item in high_risk_controls
|
||||
if item.get("helpContextDynamic")
|
||||
],
|
||||
"baseline_maximum_missing": baseline_maximum_missing,
|
||||
"baseline_regression": (
|
||||
baseline_maximum_missing is not None
|
||||
and len(missing_exact_high_risk_help) > baseline_maximum_missing
|
||||
),
|
||||
},
|
||||
"runtime_comparison": runtime_comparison,
|
||||
"ui": {
|
||||
"fields": fields,
|
||||
"actions": webui.get("actions", []),
|
||||
"actions": actions,
|
||||
"labels": webui["labels"],
|
||||
"visible_text": webui["visibleText"],
|
||||
"routes": webui["routes"],
|
||||
@@ -554,7 +676,19 @@ def _assemble_inventory(
|
||||
"ui_fields_with_resolvable_f1_context": len(fields),
|
||||
"help_review_candidates": len(help_candidates),
|
||||
"dynamic_help_references": len(dynamic_help),
|
||||
"ui_actions": len(webui.get("actions", [])),
|
||||
"ui_actions": len(actions),
|
||||
"high_risk_controls": len(high_risk_controls),
|
||||
"high_risk_controls_with_exact_help": (
|
||||
len(high_risk_controls) - len(missing_exact_high_risk_help)
|
||||
),
|
||||
"high_risk_controls_missing_exact_help": len(
|
||||
missing_exact_high_risk_help
|
||||
),
|
||||
"invalid_help_risk_annotations": len(invalid_risk_annotations),
|
||||
"unresolved_exact_high_risk_help": len(
|
||||
unresolved_exact_high_risk_help
|
||||
),
|
||||
"high_risk_help_without_german": len(high_risk_help_without_german),
|
||||
"interface_declarations": len(source_declarations),
|
||||
"duplicate_interface_ids": len(declaration_health["duplicate_ids"]),
|
||||
"undeclared_source_surfaces": len(
|
||||
@@ -885,6 +1019,10 @@ def _render_markdown(inventory: dict[str, Any]) -> str:
|
||||
for item in inventory["api"]["unreferenced_by_static_webui_scan"]
|
||||
)
|
||||
classification_counts = inventory["api"]["classification_counts"]
|
||||
high_risk_by_repository = Counter(
|
||||
item["repository"]
|
||||
for item in inventory["help_health"]["missing_exact_high_risk_help"]
|
||||
)
|
||||
lines = [
|
||||
"# GovOPlaN Platform Interface Inventory",
|
||||
"",
|
||||
@@ -900,6 +1038,12 @@ def _render_markdown(inventory: dict[str, Any]) -> str:
|
||||
f"- Fields with a resolvable F1 context: {summary['ui_fields_with_resolvable_f1_context']}",
|
||||
f"- Fields with dynamic help references: {summary['dynamic_help_references']}",
|
||||
f"- Help review candidates: {summary['help_review_candidates']}",
|
||||
f"- High-risk controls: {summary['high_risk_controls']}",
|
||||
f"- High-risk controls with exact F1 help: {summary['high_risk_controls_with_exact_help']}",
|
||||
f"- High-risk controls missing exact F1 help: {summary['high_risk_controls_missing_exact_help']}",
|
||||
f"- Invalid help-risk annotations: {summary['invalid_help_risk_annotations']}",
|
||||
f"- High-risk exact contexts missing a manifest topic: {summary['unresolved_exact_high_risk_help']}",
|
||||
f"- High-risk contexts without complete German topic content: {summary['high_risk_help_without_german']}",
|
||||
f"- Stable interface declarations: {summary['interface_declarations']}",
|
||||
f"- Duplicate interface IDs: {summary['duplicate_interface_ids']}",
|
||||
f"- WebUI surfaces missing runtime declarations: {summary['undeclared_source_surfaces']}",
|
||||
@@ -930,6 +1074,23 @@ def _render_markdown(inventory: dict[str, Any]) -> str:
|
||||
f"| `{repository}` | {count} |"
|
||||
for repository, count in sorted(help_by_repository.items())
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
"## High-risk Contextual-help Debt",
|
||||
"",
|
||||
"Inferred or explicitly classified high-risk controls require an exact",
|
||||
"F1 context. `data-help-risk-reviewed=\"standard\"` records a reviewed",
|
||||
"false positive. The versioned baseline makes this queue non-regressing.",
|
||||
"",
|
||||
"| Repository | Missing exact contexts |",
|
||||
"| --- | ---: |",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
f"| `{repository}` | {count} |"
|
||||
for repository, count in sorted(high_risk_by_repository.items())
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
@@ -1005,6 +1166,29 @@ def endpoint_key(endpoint: dict[str, Any]) -> tuple[str, str, str]:
|
||||
)
|
||||
|
||||
|
||||
def _load_high_risk_help_baseline(path: Path) -> dict[str, Any]:
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
except FileNotFoundError as exc:
|
||||
raise ValueError(
|
||||
f"High-risk contextual-help baseline does not exist: {path}"
|
||||
) from exc
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError(
|
||||
f"High-risk contextual-help baseline is invalid JSON: {exc}"
|
||||
) from exc
|
||||
if not isinstance(payload, dict) or payload.get("schema_version") != 1:
|
||||
raise ValueError(
|
||||
"High-risk contextual-help baseline must use schema_version 1."
|
||||
)
|
||||
maximum = payload.get("maximum_missing_exact_help")
|
||||
if not isinstance(maximum, int) or isinstance(maximum, bool) or maximum < 0:
|
||||
raise ValueError(
|
||||
"High-risk contextual-help baseline maximum must be a non-negative integer."
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
def _load_endpoint_declarations(
|
||||
path: Path,
|
||||
) -> dict[tuple[str, str, str], dict[str, Any]]:
|
||||
|
||||
Reference in New Issue
Block a user