Skip to content

feat(report-specs): resolve every report spec at load; list unrunnable specs with words (RS-W2-2 PR-B) - #548

Merged
dwolfson merged 1 commit into
odpi:mainfrom
dwolfson:feat/rs-w2-2-pr-b-spec-resolver
Nov 8, 2026
Merged

dwolfson merged 1 commit into
odpi:mainfrom
dwolfson:feat/rs-w2-2-pr-b-spec-resolver

Conversation

@dwolfson

@dwolfson dwolfson commented Nov 8, 2026

Copy link
Copy Markdown
Member

RS-W2-2 PR-B (Wave 2 delivery step (a″), second of three): every report spec is resolved when it is loaded, and a spec that can't run is listed with the words saying why, instead of running blank. It builds on PR-A (#546). The executor is unchanged and no spec is deleted. Parameters (PR-C) are not in this PR.

The resolver (pyegeria/view/spec_resolver.py)

resolve(spec) returns a ResolvedSpec, or an Unrunnable with one sentence per failing check, each naming its check. Every spec today is single-stage: one find method or one analytic routine. For those specs the note's §2c checks work out as below.

§ 2c check What PR-B checks Fails the spec when
1 names The find method resolves on the client class the executor would use (its _CLIENT_CLASS_MAP, else EgeriaTech and every subclient it proxies to). The analytic routine is a registered name with a python binding that imports. analytic_binders and metric_path name what they must. Any name doesn't resolve. A routine with no python binding: "no binding this executor can honour".
2 parameters Find: the executor always sends required_params, spec_params, output_format and report_spec, so each must be accepted by the method. Analytic: every analytic_spec_params key is a parameter the routine declares. A key the method rejects (it would be a TypeError on every run). An undeclared key, with the declared list.
3 stage shapes Single stage, so recorded only. A composite routine (sum_type_counts) says its fetch→analytic hand-off isn't checked. —
4 formatter vs out Every output format is one the executor renders. SERIES, BAR and PIE need an analytic routine. Every column an analytic spec lists is a field the routine declares in out. Every detail_spec is a registered spec. Any of these is false.
5 role inputs Listed from the routine's declaration. Which environment supplies each is W2-3's job. — (declared, not checked)
6 admission, tile_safe Recorded from the routine's declaration. —
7 seeding The find method a spec may be seeded by is recorded. —

warnings holds defects that only bite when a caller uses them. Example: an optional parameter the method doesn't accept, which raises a TypeError only if a caller passes it. notes names every check that didn't run, and why.

Cache: results are cached per spec. A spec is re-resolved when its FormatSet object, the set of registered names, or the analytic registry changes. Changes are detected by object identity, so no invalidation hook is needed. Resolving all 437 specs takes 0.03 s after import.

Hooks

  • Startup: the first get_report_registry() logs Report specs resolved at startup: N of M cannot run (K more are format-only ...), then one WARNING per unrunnable spec with its words. pyegeria disables its own logger by default, as before, so an app sees this once it enables pyegeria logging.
  • refresh_report_specs() after startup, load_egeria_report_specs() (the Egeria archive load) and the refresh_specs CLI (new step 4) each re-resolve and log the summary again.
  • list_report_specs(with_status=True) returns {name, runnable, unrunnable_reason} for each spec. With no argument it still returns the list of names. spec_status(label) and unrunnable_specs() are also available.
  • FormatSet accepts target_type_name (Egeria's targetTypeName, the canonical key from ws #794) as input and exposes it as a property. If it disagrees with target_type, that's refused. The resolver reads target_type_name first, with target_type as its alias. No DeprecationWarning on target_type yet: every generated spec still writes it, so the warning would fire about 370 times per load. It belongs with the generator change (§9.6).
  • Not hooked: Dr.Egeria Create Report Spec. It lives in md_processing/, and another builder is editing md_processing/v2/processors.py tonight. The coordinator asked me to stay out of md_processing. The hook is one call, spec_resolver.resolve(spec, name), after the spec is built. It's listed as a follow-up.

The read-back: every spec in base_report_formats.py + analytic_demo_specs.py

437 specs in pyegeria alone; 438 with egeria-trellis-analytics 0.4.0 installed.

Unrunnable: 2. Both are real defects in the hand-maintained base_report_specs:

  1. Asset-Graph (check 1): its find method 'ServerClient.get_asset_graph' does not exist: EgeriaTech or any client it proxies to has no '_async_get_asset_graph'. The method that exists is AssetCatalog.get_asset_graph_by_guid, which takes asset_guid. Running the spec raises TypeError ... not found in client class.
  2. Data-Classes (check 1): its find method 'DataDesigner.find_data_classes' does not exist: client class DataDesigner has no '_async_find_data_classes'. DataDesigner has no data-class find at all, only get_data_class_by_guid.

Format-only: 61. These are specs with no action, e.g. Referenceable, detail specs, and commands' output formats. They format elements another call fetched and are never run on their own. They are listed with runnable: false and the sentence it has no action, so it formats elements another call fetched, and kept out of the unrunnable count.

Warnings: 1, or 2 with the library installed.

  • Valid-Values: optional parameters sequencing_order, sequencing_property, as_of_time, effective_time are not accepted by ValidMetadataManager.get_valid_metadata_values, so a caller who passes one gets a TypeError.
  • With library 0.4.0: Trellis - Curation Coverage passes analytic_spec_params.resource_type, but the library's curation_coverage_tile manifest declares no params, even though curation_coverage_kpi(slug, ods_url, resource_type) takes them. A routine that declares no parameters at all is treated as silent about them, so this is a warning, not a refusal. The spec runs today, and refusing it would make W2-2d's Portal 400 the live Trellis tile. Library follow-up: its three manifests should declare their params.

What the checks could not see, said plainly:

  • 349 of the 352 find methods take **kwargs, so check 2 can't tell whether their parameter names are read or silently dropped. Those specs carry the note takes **kwargs, so its parameter names are not checked. This is the silent-drop that PR-C's rule 4 replaces.
  • Find methods declare no out, so a find spec's columns (element properties) aren't checked against anything (note on each). A column that is never produced shows up only on an analytic spec. All 24 analytic specs pass check 4, but none of them lists a column today (every one has empty attributes), so check 4's column-vs-out rule is exercised only by the tests. It starts to bite when a spec names columns.

For the Architect's live read-back, the three to try are Asset-Graph and Data-Classes, which should both fail at run with the error above, and Valid-Values with as_of_time set, which should raise a TypeError.

Tests

  • Without the library, uv run -p 3.13 pytest tests/micro-tests/: 2237 passed, 3 skipped. The skips are the two library cross-validation modules and one pre-existing skip.
  • With egeria-trellis-analytics 0.4.0 installed (scratch venv, Python 3.13, editable pyegeria from this branch): 2362 passed, 1 skipped.
  • New: test_rs_w2_2_spec_resolver.py, 33 tests.
    • The census pins exactly the two known defects, and the format-only set is exactly the specs with no action. Every analytic demo spec is runnable.
    • Each refusal is tested with its words: check 1 (seven cases, plus no-python-binding and unimportable binding), check 2 (spec/required/always-sent/optional/analytic-undeclared/declares-none), and check 4 (unknown format, chart without a routine, unregistered detail_spec, undeclared column).
    • What a resolved spec records (checks 3, 5, 6, 7), and the target_type_name alias and its conflict.
    • list_report_specs with and without status, cache identity and invalidation on spec and routine registration, the resolver never raising, and the summary logged at refresh, once unless forced.
  • exec_report_spec is still byte-identical on the 23 analytic demo specs in DICT and JSON (46 runs) against the PR-A baseline.
  • ruff is clean on new code. refresh_specs.py:28's unused result is already on upstream/main.

No version bump, no tag. No Egeria writes; load_egeria_report_specs only gained a log line.

🤖 Generated with Claude Code

…e specs with words (RS-W2-2 PR-B)

pyegeria/view/spec_resolver.py runs the report-spec pipeline model's §2c checks
over each spec: 1 names (find method resolves on the executor's client class;
analytic routine registered, python binding imports; binders and metric_path),
2 parameters (what the executor always sends is accepted by the find method;
analytic_spec_params are declared by the routine), 3 single stage recorded,
4 formatter vs out (known output formats, charts need a routine, analytic
columns are declared out fields, detail_spec registered), 5 role inputs listed
(supply is W2-3), 6 admission/tile_safe recorded, 7 seeding method recorded.

A failing spec is an Unrunnable carrying each failing check's sentence; it is
still listed and never deleted, and the executor is unchanged. Results are
cached per spec and invalidated when the spec object, the set of spec names or
the analytic registry changes.

Hooked into startup (first get_report_registry), refresh_report_specs,
load_egeria_report_specs and the refresh_specs CLI: each logs the count of
unrunnable specs and their words. list_report_specs(with_status=True) gives
{name, runnable, unrunnable_reason}; the default return is unchanged.
FormatSet accepts target_type_name (Egeria's property) as target_type's
canonical key.

First resolution over all 437 specs: 2 unrunnable (Asset-Graph,
Data-Classes: find methods that do not exist), 61 format-only (no action).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
@dwolfson
dwolfson merged commit cf3ed31 into odpi:main Nov 8, 2026
6 checks passed
dwolfson added a commit to dwolfson/egeria-python that referenced this pull request Nov 8, 2026
Release 6.2.25 carries odpi#546 (RS-W2-2 PR-A: routine declaration +
bindings, AnalyticFunctionSpec as its view), odpi#548 (RS-W2-2 PR-B: resolve
every report spec at load; list unrunnable specs with words) and odpi#549
(Dr.Egeria: cache Egeria 404-002 ambiguous lookups; skip the display-name
pre-check for action commands).

Micro-tests on 3.13: 2240 collected, 0 failed. Wheel checked in a clean
venv (my_egeria top-level, serve_my_profile installed).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
dwolfson added a commit that referenced this pull request Nov 8, 2026
Release 6.2.25 carries #546 (RS-W2-2 PR-A: routine declaration +
bindings, AnalyticFunctionSpec as its view), #548 (RS-W2-2 PR-B: resolve
every report spec at load; list unrunnable specs with words) and #549
(Dr.Egeria: cache Egeria 404-002 ambiguous lookups; skip the display-name
pre-check for action commands).

Micro-tests on 3.13: 2240 collected, 0 failed. Wheel checked in a clean
venv (my_egeria top-level, serve_my_profile installed).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
dwolfson added a commit that referenced this pull request Nov 8, 2026
Release 6.2.25 carries #546 (RS-W2-2 PR-A: routine declaration +
bindings, AnalyticFunctionSpec as its view), #548 (RS-W2-2 PR-B: resolve
every report spec at load; list unrunnable specs with words) and #549
(Dr.Egeria: cache Egeria 404-002 ambiguous lookups; skip the display-name
pre-check for action commands).

Micro-tests on 3.13: 2240 collected, 0 failed. Wheel checked in a clean
venv (my_egeria top-level, serve_my_profile installed).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
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