Commit 943afda
Address PR #111 review round 3: runtime wrapper + doctor + markdown
Three more findings from PR #111 review — two P1s and one P2. The
v0.20 third-party adapter surface now has parity across all three
consumers (scan dispatcher, doctor introspection, markdown report)
and the documented lenient/strict contract holds end-to-end. 4 new
regression tests, 32 total in test_adapter_entry_point_discovery.py.
P1 #1 — adapter runtime failures bypassed loaded_adapters.runtime_errors.
The dispatcher in ``_load_sources`` called ``adapter.load(...)``
directly for ALL adapters. ``run_validated_adapter`` existed in
``inputs/adapter_validation.py`` but was never invoked during a real
scan. A third-party adapter that raised at runtime aborted
``run_scan`` with the raw exception, no report was emitted, and
``--strict-plugins`` never saw the failure. Contradicted the
documented "lenient mode records the failure, continues" contract.
Fix:
- ``_load_inputs`` now builds a ``third_party_records: dict[str,
LoadedAdapter]`` map from discovery (one entry per valid
third-party adapter, keyed by source_type) and threads it to
``_load_sources(..., third_party_records=...)``.
- ``_load_sources`` (pass 1 / pass 2) checks each adapter's
source_type against the map. Built-ins keep the direct
``adapter.load(...)`` call shape — a built-in raising is a
scanner bug and must abort loudly. Third-party adapters route
through ``run_validated_adapter`` instead, which catches every
exception, captures it into ``LoadedAdapter.runtime_errors`` (and
mirrors into the matching ``loaded_adapters[].runtime_errors``
dict), and returns ``None`` so the dispatcher skips ``_absorb``.
- ``_invoke_per_source_adapter`` learned a ``third_party_record``
kwarg that switches to the wrapper. Built-in optional-source
``InputParseError`` handling is preserved on the built-in branch.
Result: lenient-mode third-party crashes are captured, the scan
completes, the report contains the failed adapter row with
``runtime_errors`` populated, and ``--strict-plugins`` exits 4 as
documented.
P1 #2 — doctor couldn't introspect manifests using third-party
source types. ``inspect_sources`` (the doctor command's manifest-
introspection entry point) called ``_load_sources`` against the
global ``REGISTRY`` (which is intentionally builtin-only since the
PR #111 P1 #1+#2 per-scan-clone refactor) and did NOT run adapter
discovery. So a manifest with ``tool_sources[].type: demo_source``
that scanned cleanly now made ``doctor`` crash with
``ConfigError: No adapter registered for source type
'demo_source'``.
Fix:
- ``inspect_sources`` mirrors the ``_load_inputs`` pattern: build
``scan_registry = REGISTRY.clone()``, run
``discover_third_party_adapters(scan_registry, …)`` (honoring the
same ``plugins_enabled`` env / override semantics), pass the
per-scan registry and ``third_party_records`` to
``_load_sources``.
- New ``plugins_enabled: bool | None = None`` kwarg defaults to
``None`` so the existing env var governs (no doctor CLI flag
change required).
- ``loaded_adapters`` surfaced in the doctor payload alongside
``policy_packs`` so an operator can confirm what was discovered
without running a full scan.
P2 #3 — Markdown reports hid adapter validation failures.
``report/markdown.py`` rendered ``loaded_policy_packs`` and
``loaded_plugins`` but had no equivalent ``loaded_adapters``
section. A ``load_failed`` adapter appeared in ``report.json`` but
not in ``report.md`` — the default human report was blind to
skipped third-party extensions.
Fix:
- New ``_append_loaded_adapters(lines, report)`` renders a
``## Loaded Adapters`` section listing each entry as
``- distribution version: source_type — \`validation_status\``
followed by per-error indented bullets (validation_error and
runtime_error). Hidden when ``loaded_adapters`` is empty so
clean repos see no extra noise.
- Wired into the main rendering flow right after
``_append_loaded_plugins``.
Regression tests (4 new in
tests/test_adapter_entry_point_discovery.py):
- ``test_runtime_error_in_third_party_adapter_captured_not_propagated``
— a third-party adapter that raises ``RuntimeError`` at runtime
doesn't abort ``run_scan``; the failure lands in
``loaded_adapters[].runtime_errors``; the scan exits 0.
- ``test_strict_plugins_exits_on_third_party_adapter_runtime_error``
— end-to-end via CliRunner: ``--strict-plugins`` elevates exit
code 4 specifically on the new adapter-runtime-error path.
- ``test_doctor_resolves_third_party_source_types`` — a manifest
with ``type: demo_source`` and a matching third-party adapter is
introspected successfully by ``inspect_sources``; the adapter
runs; the doctor payload surfaces ``loaded_adapters[]``.
- ``test_markdown_report_renders_loaded_adapters_section`` — a
``load_failed`` adapter shows up in ``report.md`` under the
``## Loaded Adapters`` heading with its status and validation
error.
Verification: 1268 tests pass; ruff clean; schema roundtrip --check
exits 0; coverage 88.06%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 14b943a commit 943afda
3 files changed
Lines changed: 400 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
377 | 377 | | |
378 | 378 | | |
379 | 379 | | |
380 | | - | |
| 380 | + | |
381 | 381 | | |
382 | 382 | | |
383 | 383 | | |
384 | 384 | | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
385 | 398 | | |
386 | | - | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
387 | 404 | | |
388 | 405 | | |
389 | 406 | | |
| |||
1246 | 1263 | | |
1247 | 1264 | | |
1248 | 1265 | | |
1249 | | - | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
1250 | 1285 | | |
1251 | 1286 | | |
1252 | 1287 | | |
| |||
1264 | 1299 | | |
1265 | 1300 | | |
1266 | 1301 | | |
1267 | | - | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
| 1310 | + | |
| 1311 | + | |
| 1312 | + | |
| 1313 | + | |
| 1314 | + | |
| 1315 | + | |
| 1316 | + | |
| 1317 | + | |
| 1318 | + | |
| 1319 | + | |
| 1320 | + | |
| 1321 | + | |
| 1322 | + | |
| 1323 | + | |
| 1324 | + | |
| 1325 | + | |
1268 | 1326 | | |
1269 | 1327 | | |
1270 | 1328 | | |
| |||
1312 | 1370 | | |
1313 | 1371 | | |
1314 | 1372 | | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
| 1376 | + | |
| 1377 | + | |
1315 | 1378 | | |
1316 | 1379 | | |
1317 | 1380 | | |
| |||
1399 | 1462 | | |
1400 | 1463 | | |
1401 | 1464 | | |
| 1465 | + | |
1402 | 1466 | | |
1403 | 1467 | | |
1404 | 1468 | | |
| |||
1430 | 1494 | | |
1431 | 1495 | | |
1432 | 1496 | | |
| 1497 | + | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
| 1502 | + | |
| 1503 | + | |
| 1504 | + | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
1433 | 1508 | | |
1434 | 1509 | | |
1435 | 1510 | | |
| 1511 | + | |
| 1512 | + | |
1436 | 1513 | | |
1437 | 1514 | | |
1438 | 1515 | | |
| |||
1447 | 1524 | | |
1448 | 1525 | | |
1449 | 1526 | | |
| 1527 | + | |
1450 | 1528 | | |
1451 | | - | |
| 1529 | + | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + | |
| 1534 | + | |
1452 | 1535 | | |
| 1536 | + | |
| 1537 | + | |
| 1538 | + | |
| 1539 | + | |
| 1540 | + | |
1453 | 1541 | | |
1454 | 1542 | | |
1455 | 1543 | | |
| |||
1458 | 1546 | | |
1459 | 1547 | | |
1460 | 1548 | | |
1461 | | - | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
| 1561 | + | |
| 1562 | + | |
| 1563 | + | |
| 1564 | + | |
1462 | 1565 | | |
1463 | 1566 | | |
1464 | 1567 | | |
| |||
1552 | 1655 | | |
1553 | 1656 | | |
1554 | 1657 | | |
1555 | | - | |
| 1658 | + | |
| 1659 | + | |
| 1660 | + | |
| 1661 | + | |
| 1662 | + | |
| 1663 | + | |
| 1664 | + | |
| 1665 | + | |
| 1666 | + | |
| 1667 | + | |
| 1668 | + | |
| 1669 | + | |
| 1670 | + | |
| 1671 | + | |
| 1672 | + | |
| 1673 | + | |
| 1674 | + | |
| 1675 | + | |
| 1676 | + | |
| 1677 | + | |
| 1678 | + | |
| 1679 | + | |
| 1680 | + | |
| 1681 | + | |
| 1682 | + | |
| 1683 | + | |
| 1684 | + | |
| 1685 | + | |
| 1686 | + | |
1556 | 1687 | | |
1557 | 1688 | | |
1558 | 1689 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
107 | 108 | | |
108 | 109 | | |
109 | 110 | | |
| |||
461 | 462 | | |
462 | 463 | | |
463 | 464 | | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
464 | 503 | | |
465 | 504 | | |
466 | 505 | | |
| |||
0 commit comments