Meticulous-review platform — phases 2 through 4c (review workflow, cross-MCP, schematic analyzers)#125
Merged
Conversation
Adds the binding 8-pass professional design review workflow plus the single entry-point MCP tool that drives it. * docs/CLAUDE_REVIEW_PLAYBOOK.md — P0 intake → P1 scoping interview → P2 parse + cross-reference → P3 domain analysis → P4 standards verification → P5 simulation escalation → P6 cross-domain correlation → P7 reporting → P8 self-critique. Includes per-pass MCP tool-call sequences and a worked CISPR-25 example. * docs/MARKET_INTAKE_MATRIX.md — per-market question packs (automotive, medical, wireless, commercial, industrial) and the standards/analyzers each market activates. * docs/SELF_CRITIQUE_CHECKLIST.md — explicit Pass-8 checklist (coverage, confidence audit, assumption ledger, counter-evidence, simulation gap, standards traceability, human-review flags, known blind spots). * src/mcp_pcb_emcopilot/market_packs.py — data-only QUESTION_BANK, MARKET_STANDARDS, MARKET_ANALYZERS plus merge helpers. * src/mcp_pcb_emcopilot/review_playbook.py — SERVER_INSTRUCTIONS string (surfaced to Claude on every MCP initialize), file classifier, manifest/gap builder, interview-pack assembler, standards/analyzer shortlist computers, and the start_professional_review payload builder. * src/mcp_pcb_emcopilot/server.py — Server constructed with instructions=SERVER_INSTRUCTIONS; registers pcb_start_professional_review as the canonical entry point; pcb_parse_layout now accepts an optional session_id so the playbook session survives parsing. * src/mcp_pcb_emcopilot/session.py — replace_session() for in-place session updates without poking private attrs. Plus untracked tests bundled in: test_review_playbook (40 tests covering file classification, manifest, gaps, interview pack, multi-market unions, playbook markdown loader, start_professional_review payload) and test_pcb_start_professional_review (6 dispatcher tests, including the session-reuse path through pcb_parse_layout). Tool surface now 117 (was 116); all-tools smoke green; full suite 1383 passing with the same 4 pre-existing failures unrelated to this work. Also includes the v1 production-readiness plan reference doc, the test suite (smoke + per-domain coverage) that had been sitting untracked, and the .gitignore updates to exclude generated review HTML and sim_plots/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the sibling-MCP coordination layer that lets Claude verify
high-severity / low-confidence findings against openEMS, NEC2, and
emc-regulations without breaking the request/response model FastMCP
enforces. The pcb-emcopilot Python process emits actions; Claude
executes them via mcp__<server>__<tool>; results flow back through a
new attach endpoint that re-correlates the linked findings.
* src/mcp_pcb_emcopilot/integrations/external_actions.py — new module
with ExternalAction + ExternalResult dataclasses, priority constants
(CRITICAL/HIGH/NORMAL/LOW/NICE_TO_HAVE), dedupe_actions,
sort_by_priority, filter_pending, has_pending_critical helpers. Pure
data — no I/O, hashable params signatures for safe dedupe across nested
geometries.
* src/mcp_pcb_emcopilot/session.py — DesignSessionManager gains
pending_actions queue and external_results map per session_id, with
enqueue_actions / get_pending_actions / find_action / record_result
helpers. close_session evicts the queues alongside the design data.
* src/mcp_pcb_emcopilot/orchestrator.py — ReviewFinding schema upgrade:
confidence (default 0.8), verified (False), source ("analytical"),
finding_id (auto-generated, uses the consolidated _prefix_for from
report_builder), linked_actions list. Adds escalation policy
constants (ESCALATE_SEVERITY_MAX, ESCALATE_CONFIDENCE_BELOW,
SIM_TOLERANCE_PCT, NEC2_MIN_FREQ_MHZ, OPENEMS/NEC2 domain sets).
New _emit_next_actions(design, review_result) walks findings that
meet the predicate and emits openEMS-bound ExternalActions, pairing
candidate geometries from RFSimulationExtractor with each finding.
* src/mcp_pcb_emcopilot/reports/report_builder.py — _prefix_for now
strips non-letters and falls back to GEN when the domain string has
no alpha characters at all. Fixes 3 pre-existing failing tests in
test_more_coverage.py::TestFindingIdPrefix.
* Orchestrator ethernet skip predicates — switched from exact tuple
membership to substring match so 1000BASE-T / 2.5GbE / 10GbE variants
are caught. Fixes the 4th pre-existing failing test.
* src/mcp_pcb_emcopilot/server.py — Server(... instructions=...) now
registers four new tools:
- pcb_suggest_next_actions(session_id, domains?, max_actions?,
include_completed?) — drains the queue, prioritised order.
- pcb_attach_external_result(session_id, action_id, result, error?)
— persists the sibling-MCP result; on success marks the linked
finding verified=True, bumps confidence to 0.95, sets
source=<mcp_server>. On error stamps source with "+sim_failed".
- pcb_finalize_review(session_id, require_critical_verified?) —
blocks while critical-priority actions are pending unless the
caller opts out; returns confidence_distribution and a pointer to
docs/SELF_CRITIQUE_CHECKLIST.md.
- pcb_lookup_limit_live(standard, class_or_level, frequency_mhz,
detector?, fallback?) — Phase 3a stub: emits a deferred envelope
pointing at the matching mcp__emc-regulations__* tool. Phase 3b
will add the regulations_bridge with cached + local-fallback
values via analyzers/emc/limits_provider.py.
pcb_run_design_review now invokes _emit_next_actions and enqueues
the resulting actions; the response includes a next_actions field.
pcb_generate_design_review_report gains a force flag and refuses to
run (status=deferred) when critical-priority actions remain, unless
force=True (then stamps the output preliminary=True).
Tool surface: 117 → 121 registrations, parity with dispatch branches.
Tests: +36 new (test_external_actions, test_review_finding_schema,
test_phase3_mcp_tools) AND 4 pre-existing failures now pass. Full
suite 1423 passing (was 1383 passed + 4 failed before this slice).
Phase 3b will follow with limits_provider.py + analyzer refactor to
read live limits, and the regulations / nec2 / drawio bridges that
emit their own ExternalActions through this same queue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the centralised regulatory-limit provider with live-cache override
plus three intent-emitting bridges (regulations, NEC2, drawio) and the
openEMS verification loop. The orchestrator now emits four flavours of
sibling-MCP intent per review — Claude drives them, results round-trip
through pcb_attach_external_result, and findings/limits update in place.
* src/mcp_pcb_emcopilot/analyzers/emc/limits_provider.py — new module.
get_limit(standard, class_or_level, freq_mhz, detector?) → LimitPoint
with source tracking ("local_fallback" | "live_regs"). Local tables
cover CISPR-25 radiated+conducted (per class, with radiated/conducted
disambiguation via standard alias or AVG detector), FCC Part 15 A/B
radiated+conducted, CISPR 32 / EN 55032 A/B, ISO 11452-2 field +
ISO 11452-4 BCI, IEC 60601-1-2 Ed 4.0/4.1 RF immunity. Runtime cache
populated by cache_live_result() from the regs bridge.
* src/mcp_pcb_emcopilot/analyzers/emc/clock_emi_analyzer.py —
_get_regulatory_limit now delegates to limits_provider.get_limit and
falls back to its legacy tuple list only on provider miss. Behaviour
identical for the historical call path; live regs overrides propagate
automatically.
* src/mcp_pcb_emcopilot/reports/simulation_plots.py — cispr25_compliance
plot's limit mask now reads through limits_provider, so a Claude-fed
live regs result repaints the mask without code changes.
* src/mcp_pcb_emcopilot/integrations/regulations_bridge.py — new module.
build_limit_lookup_intent / build_intents_for_standards emit
ExternalAction objects targeting mcp__emc-regulations__* tools per
standard. apply_limit_result parses sibling-MCP responses (supports
several common payload shapes) and writes them into the provider's
runtime cache. Standard→tool mapping covers CISPR/FCC/ISO/IEC families.
* src/mcp_pcb_emcopilot/integrations/nec2_bridge.py — new module.
build_antenna_intent emits mcp__nec2-antenna__nec2_create_<type>
actions for antenna findings ≥ 30 MHz with non-zero trace length;
infer_antenna_type maps title/description text to dipole / monopole /
yagi / loop / vertical / inverted-v primitives. build_simulate_followup
pairs each create with the corresponding nec2_simulate action.
is_antenna_finding gates the orchestrator scan.
* src/mcp_pcb_emcopilot/integrations/drawio_bridge.py — new module.
build_diagram_intents composes up to four report-time intents
(create_pcb_stackup, create_rf_block_diagram, create_emc_test_setup
per market, markup_schematic when schematic parsed). Market →
canonical standard mapping (automotive→CISPR-25, medical→IEC-60601,
etc.).
* src/mcp_pcb_emcopilot/orchestrator.py — _emit_next_actions now
produces all four intent flavours: openEMS escalations (passing
analytical_value through for compare_results), NEC2 antenna runs for
antenna findings ≥ 30 MHz, regulations live-lookup batches per
standard in the shortlist, drawio diagrams at the end. Dedupe handles
overlap between escalation paths.
* src/mcp_pcb_emcopilot/server.py — pcb_attach_external_result now
performs bridge-specific post-processing:
- emc-regulations results call apply_limit_result, populating the
provider cache and returning live_limit_cached=True.
- openems results call OpenEMSBridge.compare_results against the
analytical_value carried in the action's params (passed in by
orchestrator). Sim status drives the linked finding's confidence/
severity per SIM_TOLERANCE_PCT: pass→0.95+verified, warning→0.6,
fail→severity escalated to critical with an explanatory note
appended to the description.
Tests: +52 new (test_limits_provider, test_regulations_bridge,
test_nec2_bridge, test_drawio_bridge, test_openems_attach_loop) covering
local fallback for every standard family, live-cache override and
clock-emi provider integration, intent emission per market, NEC2 type
inference + min-freq gating, drawio composite emission, and the full
openEMS pass/warning/fail roundtrip + emc-regulations cache write-back.
Full suite 1475 passing / 0 failing.
Tool surface stable at 121 (no new tools — all module-level work).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gate
Wires the per-market question packs (already shipped in market_packs.py
during Phase 2) into review_context.py so pcb_get_review_questions now
returns the merged core + active-market pack. Adds a programmatic
standards-coverage map and the preflight gate the playbook references
in Pass 0/1, plus three new MCP tools that drive the gate end-to-end.
* src/mcp_pcb_emcopilot/review_context.py — get_review_questions merges
CORE legacy pack with market_packs.get_pack(m) for every active market.
New helpers: get_active_markets (explicit list > playbook declared >
RF-net heuristic), get_target_standards_for (union across markets).
Typed getters added for every market-specific answer (vehicle_class,
bus_voltage, iso26262_asil, cispr25_class, iso7637_pulses, oem_spec,
device_class, iec60601_edition, patient_contact, fcc_part,
tx_power_dbm, antenna_gain_dbi, intentional_radiator, target_regions,
cispr32_class, iec61000_4_immunity_level, hazloc_class,
surge_target_kV). Tolerates list-or-csv-string forms.
* src/mcp_pcb_emcopilot/standards/coverage.py (new package). 30+ standards
mapped to required_analyzers, limit_source (local_fallback or
emc-regulations), coverage_level (full / partial / stub / unimplemented).
StandardCoverage dataclass + get_coverage(standards, ran_analyzers) +
summarise_coverage() rollup.
* src/mcp_pcb_emcopilot/standards/preflight.py — ValidationGate dataclass
with ready/missing_required_questions/missing_standard_selection/
incomplete_standards/notes. validate_review_complete(design, ran)
enforces core required questions (operating_environment,
fab_stackup_spec) plus per-market required sets (automotive:
vehicle_class+cispr25_class+bus_voltage; medical: device_class+
iec60601_edition; wireless: intentional_radiator+fcc_part; commercial:
cispr32_class+target_regions; industrial: en61326_immunity+
surge_target_kV). Surfaces stub/unimplemented standards as
incomplete_standards but does not block on partial coverage.
* docs/STANDARDS_COVERAGE_MATRIX.md — human-readable mirror.
* src/mcp_pcb_emcopilot/server.py — three new tools:
- pcb_set_market(session_id, market_id, replace?, sub_options?) —
activates a market preset, updates the playbook's standards +
analyzer shortlists, optionally pre-fills sub_options into
interactive_answers. Multi-market via repeated calls.
- pcb_get_standards_coverage(session_id) — returns the coverage
summary derived from review_results' domain_results.
- pcb_validate_review_complete(session_id) — wraps the preflight gate.
pcb_generate_design_review_report now runs the preflight gate FIRST
(before the cross-MCP critical-action gate) and returns
status=deferred with the gate breakdown on failure. force=True
bypasses both gates and stamps the output preliminary=True. The
generated response includes preflight + standards_coverage payloads.
* tests/test_phase3_mcp_tools.py — Phase 3a report-gate tests now
satisfy preflight first via a helper, then exercise the cross-MCP
gate as before.
Tool surface 121 → 124 (parity with dispatch branches intact).
Tests: +50 new (test_review_context_multi_market 19, test_standards_coverage
9, test_preflight 7, test_phase4_mcp_tools 15). Full suite 1521 passing
/ 0 failing / 10 skipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Layers the schematic side of the meticulous review onto the layout-centric
work shipped in phases 2-4. Adds an auto-dispatch schematic parser, four
new schematic-aware analyzers, and the long-promised 3-way (schematic ↔
BOM ↔ layout) cross-reference. All run automatically when schematic data
is attached to the session.
* src/mcp_pcb_emcopilot/parsers/schematic_dispatch.py — new module.
detect_format(path) maps extension + magic bytes to
Literal["kicad","altium","pdf","netlist","unknown"].
parse_schematic_auto(path) routes to the existing PDF / KiCad /
Altium parsers and returns a uniform {source_format, components,
nets, title, warnings, raw} dict. Refuses image-only PDFs with a
clear remediation message via pdfplumber peek. Includes a minimal
ORCAD/Pads netlist stub so .net files yield at least refdes + net
lists.
* src/mcp_pcb_emcopilot/analyzers/schematic/ — new package, four
analyzers + a shared _normalise helper that handles both dict-form
(from PDF) and dataclass-form (from KiCad/Altium) inputs.
- power_topology — per-rail caps, bulk presence, regulator/input
source. Degrades cleanly when pin-net mapping is missing.
- protection_circuits — TVS coverage on external nets (USB / ETH /
ANT / GPIO_EXT / CAN / VIN / VBUS / VBAT), common-mode choke on
diff pairs, fuse on power input. Per-net flagging when pin-net data
is present; aggregate fallback otherwise.
- decoupling_per_ic — counts caps on each IC's Vdd-class pins;
falls back to global ratio when pin-net data is absent.
- component_rating — joins BOM voltage ratings against inferred rail
voltages; flags 80% derating violations.
* src/mcp_pcb_emcopilot/analyzers/validation/three_way_xref.py —
promoted from bom_cross_reference. Severity ladder per the plan:
CRITICAL = component missing from layout / footprint mismatch sch ↔
layout / value mismatch sch ↔ BOM. HIGH = DNP flag differs / MPN
differs. MEDIUM = component absent from BOM or schematic. LOW =
manufacturer differs only. Value normalisation handles unit symbol
variants (10 kΩ ≡ 10K). Missing-from-X checks fire only when source
X actually has data, so an empty BOM doesn't generate false
"missing from BOM" for every component.
* src/mcp_pcb_emcopilot/server.py — six new tools (130 total, 124 → 130):
- pcb_parse_schematic — auto-dispatch over .kicad_sch, .SchDoc,
.pdf, .net. Reuses an existing session via optional session_id.
- pcb_parse_bom — wraps BOMParser; attaches bom_items to session.
- pcb_analyze_power_topology / _protection_circuits /
_decoupling_per_ic — individual schematic analyzers.
- pcb_three_way_cross_reference — runs the new 3-way xref.
* src/mcp_pcb_emcopilot/orchestrator.py — _select_analyzers appends
the five new schematic analyzers (power_topology, protection,
decoupling_per_ic, component_rating, three_way_xref) when
design.schematic_components is non-empty. New _run_schematic_callable
+ _run_three_way_xref helpers dispatch the function-style analyzers.
Ethernet skip predicate (Phase 3a fix) and finding-id auto-generation
remain unchanged.
Tests: +43 new (test_schematic_dispatch 13, test_schematic_analyzers
24, test_phase4b_mcp_tools 7). Full suite 1564 passing / 0 failing /
11 skipped (+1 skip is the optional pdf-fixture path).
Tool surface 124 → 130, parity with dispatch branches intact.
Honest readout: deeper parser rewrites (KiCad sexpdata, Altium OLE
extension, full ORCAD/Pads netlist) remain deferred — they need real
fixture files. The auto-dispatch + degraded-mode analyzers cover the
typical-case meticulous review today.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed CI Closes the long-tail items from the meticulous-review buildout: a proper S-expression KiCad parser, real ORCAD/Pads netlist support, the deferred signal-flow analyzer, schematic test fixtures, Altium DNP/MPN parameter mapping, plus the ruff/CI pinning that lets local + GitHub Actions agree on lint output. Issues closed by this commit: #117 #118 #119 #120 #121 #122 #123. Follow-up #124 tracks the (pre-existing) mypy debt — typecheck job is advisory until that lands. Pinning + CI ------------ * pyproject.toml — ruff >= 0.9.0,<1.0 (knows UP045); sexpdata >= 1.0 added to [dev] + [all] extras. New [sexpdata] extra for installs that only need the parser dependency. * .github/workflows/ci.yml — lint job installs the pinned ruff via `pip install -e ".[dev]"` (was bare `pip install ruff`). Typecheck job marked `continue-on-error: true` until #124 closes. Schematic fixture corpus (#118) ------------------------------- * tests/fixtures/sample_2sheet.kicad_sch — hand-written hierarchical KiCad schematic, 4 components (R/C/C/U), MPN/Manufacturer/Datasheet properties, in_bom/on_board flags (one DNP cap), wires/labels/junctions. * tests/fixtures/generate_schematic_pdf.py + sample_schematic.pdf — tiny hand-rolled PDF (985 bytes, no PyMuPDF dependency to generate) with an extractable text layer naming 7 components + 5 net labels. * tests/fixtures/sample_netlist.NET — synthetic PSTXNET netlist, 7 parts + 5 nets, MPN/Manufacturer per part, per-net pin lists. * tests/fixtures/sample.SchDoc — minimal OLE2 stub (1.5 kB) the schematic_dispatch.detect_format() path recognises as Altium. * tests/conftest.py — pytest fixtures for each of the above. KiCadSchematicParser rewrite (#119) ----------------------------------- * parsers/schematic_parser.py — full sexpdata tree walker replaces the regex parser. Surfaces: - refdes, value, footprint, MPN, Manufacturer, Datasheet - in_bom / on_board → properties['dnp'], dnp_in_bom, dnp_on_board - per-component pins with x/y absolute coords - pin → net resolution via label-coordinate proximity to wires - hierarchical / global label nets - sub-sheet references in properties['lib_id'] * Falls back to the legacy regex parser when sexpdata isn't installed or the file is malformed — keeps the package usable without the optional dependency. ORCAD/Pads netlist parser (#122) -------------------------------- * parsers/netlist_parser.py — new module. Section-aware *PART* / *NET* reader, refdes + value + MPN + manufacturer per component, per-net pin lists with REFDES.PIN parsing. * detect_netlist_dialect() distinguishes PSTXNET vs Pads via header sniff; both share the same section structure so one walker handles both. * parsers/schematic_dispatch.parse_schematic_auto() now routes .net files through this proper parser, falling back to the regex stub only when the proper parser yields zero output. Altium parameter records (#121, partial) ---------------------------------------- * parsers/altium_parser.py — AltiumComponent gains a `properties` dict. _parse_records now maps Parameter rows: DNP / DONOTPLACE / NOSTUFF → properties['dnp'] = True; COMPONENTCLASS / CLASS → properties ['component_class']; COMMENT/NOTE → properties['comment']; unknown parameter names stashed lower-cased so downstream analyzers can inspect Tolerance, Voltage, etc. * Deeper sheet-symbol traversal + pin-net resolution remain deferred (need real .SchDoc fixtures — out of scope without licensed test data). #121 stays open with that scope clarified. Signal-flow analyzer (#123) --------------------------- * analyzers/schematic/signal_flow.py — three checks in one analyzer: 1. Clock distribution: Y/X crystals + oscillator-class ICs + frequency-pattern values are sources; flag clock fan-out > 4 unbuffered loads (HIGH); flag designs with ICs but no clock source (MEDIUM). 2. Reset distribution: detect *RESET*/*RST*/*nRST* nets; flag no-supervisor-IC (MEDIUM); flag multi-IC-driver contention (HIGH). 3. JTAG/SWD accessibility: detect TCK/TDI/TDO/TMS/SWCLK/SWDIO/SWO nets; flag missing debug connector / ICs without JTAG nets. * analyzers/schematic/__init__.py exports the new function. * New pcb_analyze_signal_flow MCP tool (130 → 131). * Orchestrator wires it under sch_signal_flow; _select_analyzers appends it when schematic_components are attached. Two mypy fixes from earlier phases ---------------------------------- * analyzers/schematic/_normalise.py — guard asdict() against the Type | Instance ambiguity is_dataclass() returns. * standards/preflight.py — coerce markets to list[str] before iterating so the "Optional list" branch is type-safe. Coverage / surface ------------------ * MCP tools: 130 → 131 (parity with dispatch branches confirmed). * Tests: +51 new (test_kicad_schematic_parser 12, test_netlist_parser 12, test_signal_flow 11, test_altium_parameter_records 11, + parser smoke). Suite 1564 → 1611 passing / 0 failing / 11 skipped. * Coverage 78.65 % (CI gate at 50 %). * Ruff clean against the pinned 0.9+ config. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI failed because the test imports were of the form `from src.mcp_pcb_emcopilot...` which works locally (pytest's rootdir walk picks up src/ as a package root) but breaks under `pip install -e .` where the package is at the top level. Strip the src. prefix from all imports in this file so the test collects everywhere. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous fix caught only top-of-file `from src.mcp_pcb_emcopilot...`
imports. Several tests build module paths as strings passed to
`importlib.import_module("src.mcp_pcb_emcopilot...")` — same root cause,
same fix: drop the `src.` prefix so installed-package layouts (CI) work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Job-level `continue-on-error: true` lets the workflow finish but still reports the job as FAILURE, leaving the PR in an UNSTABLE state. Moving the flag onto the mypy step converts a non-zero exit into a successful step, so the job — and the PR check — go green. mypy errors still appear in the job log for #124 cleanup tracking. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 19, 2026
Closes #124. mypy goes from 66 errors across 13 files to zero. The CI typecheck job no longer needs `continue-on-error` — it's now a hard gate alongside lint and tests. By file ------- * current_profiler.py (-41) — `profile.get("X", 0)` calls return `Any` through `Dict[str, Any]`; wrap each return / sleep_ua / quiescent_ua in `float(... or 0)`. Tighten `_COMPILED_PATTERNS` element type to `tuple["re.Pattern[str]", Dict[str, Any]]` so the matcher returns the proper profile dict. * server.py (-8) — rename clashing locals (`detector` was shadowing `InterfaceDetector` import; `parser` was shadowing `STEPParser`; `summary` was shadowing the earlier handler's dict; `ran` / `candidates` were redefined across branches). Convert `ParsedBOMItem` instances to dicts via `asdict` when storing on `data.bom_items` (typed `list[dict]`). Annotate the simulation- candidate cache narrowing explicitly. Use `setattr` for the dynamic `_accepted_findings` attribute. * review_context.py (-5) — typed getters returning `str`/`Optional[str]` were returning `Any` from `_answers.get`. Wrap with `str(val)` to narrow the type. * orchestrator.py (-2) — annotate `cand` / `ant_cand` as `SimulationCandidate | None` explicitly so mypy doesn't infer `SimulationCandidate` from the outer-loop `cand` binding. * impedance_validator.py (-2), rf_simulation_extractor.py (-2) — wrap layer-derived dielectric values in `float()` for explicit narrowing. * gnss_analyzer.py (-2) — guard the `s["distance_mm"] < 8.0` check with an `isinstance(... (int, float))` to satisfy the `dict[str, Any]` union path. * smps_loop_analyzer.py (-1) — defensive `if nearest_cap is None: continue` on the both-`None` branch so the `getattr(...).reference` access is type-safe. * copper_pour_checker.py (-1) — `(net_name or "").upper()` to handle the `None` net case. * schematic_dispatch.py (-1) — narrow `is_dataclass(obj)` to instance before calling `asdict()` (same fix as `_normalise.coerce`). CI -- * .github/workflows/ci.yml — drop `continue-on-error: true` from the mypy step. typecheck now blocks merges on type regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
End-to-end build of the meticulous-professional-engineer review platform on top of the existing layout-centric MCP server. The PR collapses six commits worth of work into one reviewable diff and closes seven tracking issues.
Closes
#124 (mypy debt) intentionally not closed — typecheck is advisory until the cleanup lands.
What landed (six phases)
63135dbCLAUDE_REVIEW_PLAYBOOK.md, market packs,pcb_start_professional_review,instructions=on the Server constructor8affb3aExternalActionintent queue,ReviewFindingschema upgrade (confidence/verified/source/finding_id/linked_actions), 4 orchestration toolsef23829limits_providerwith live-regs cache, regulations/NEC2/drawio bridges, openEMS compare-results verification loop833f6d6b2b5b91f47df45Numbers
_prefix_forletters-only/GEN fallback + Ethernet substring matching).End-to-end review flow that's now real
Honest readout
.SchDocfixtures (tracked).Test plan
docs/CLAUDE_REVIEW_PLAYBOOK.md,docs/MARKET_INTAKE_MATRIX.md,docs/SELF_CRITIQUE_CHECKLIST.md,docs/STANDARDS_COVERAGE_MATRIX.mdfor accuracy against the codebase.🤖 Generated with Claude Code