Skip to content

feat(view): executor supplies declared input roles (RS-W2-3 PR-A) - #552

Merged
dwolfson merged 2 commits into
odpi:mainfrom
dwolfson:feat/rs-w2-3-pr-a-inputs-by-role
Nov 8, 2026
Merged

dwolfson merged 2 commits into
odpi:mainfrom
dwolfson:feat/rs-w2-3-pr-a-inputs-by-role

Conversation

@dwolfson

@dwolfson dwolfson commented Nov 8, 2026

Copy link
Copy Markdown
Member

RS-W2-3 PR-A: the executor supplies declared input roles (no more parameter-name introspection)

Brief: BRIEF-RS-W2-3-inputs-by-role-2026-11-07.md (STATUS + §A, A0–A7). Base: upstream/main 8ce7f02e (pyegeria 6.2.25).

What changes

  • New pyegeria/view/role_supply.py. It holds the RoleSupply protocol (client(cls), viewer(), reader(source), subject(), refusal_words(kind)), Viewer and Subject, and the refusals RoleRefused / NoSuchReader / CallerTokenExpired (ValueError subclasses that carry .words, .role, .routine, .reason). It also has PlainSupply and supply_inputs(routine, inputs, supply).
  • The executor binds by declared inputs, in declared order, to the leading positional slots. Declared parameters then follow by name. _ANALYTIC_CLIENT_PARAM_NAMES, _count_client_params and _bind_client_args are deleted. _run_analytic_function and run_analytic_action (fetch + analytic) both go through supply_inputs. exec_report_spec and _exec_analytic_chart take a new optional supply=, which is the hook the Portal adapter will use. When it is omitted, a PlainSupply is built from today's arguments (view_server/view_url/user/user_pass/token).
  • PlainSupply keeps Wave 2 byte-identical. client(cls) returns the caller's one EgeriaTech for EgeriaTech and for every class in its _subclient_map, and never a subclient (ruling A3). The client is built lazily, at most once per run, and only when an egeria_client input is declared, so ISSUE-137 (no client means no connection and no token) still holds. Readers, viewer and subject are refused with plain pyegeria's words. subject() returns None, which supply_inputs refuses.
  • Check 5 is a refusal. spec_resolver.declared_inputs(func_decl, func=None) is the one function that both the resolver's check 5 (at load) and the executor (at run, before binding) call. That satisfies rule 1: the same words at load and at run. It refuses in three cases: an unregistered routine, an input that is not a role word or is repeated, and a binding whose leading client-argument count differs from the declaration's egeria_client count: "routine X's binding takes N leading client argument(s) and its declaration lists M egeria_client input(s)". The signature count (_binding_client_slots) lives only in the resolver module and is a consistency check, never a binding. At run time, a role the supply cannot provide raises before stage 1, with the supply's refusal_words.
  • All 28 built-ins declare inputs (table below). Each slot is declared by the class whose methods that argument actually calls (A3). I found the classes by running each function with one recording fake per slot and mapping each called method to the EgeriaTech subclient class that defines it. EgeriaTech is used only for an argument that calls across classes.
  • _resolve_action_target_client (find-method specs) is unchanged. It picks a client class from the spec's Class.method, not from parameter names. Replacing it with the supply would hand out subclients, a behaviour change that belongs to Wave 3.

The 28 inputs declarations

Function Slots Class(es) Why
count_elements mgr MetadataExpert count_metadata_elements / find_metadata_elements
counts_by_type mgr MetadataExpert same, per type
governed_coverage mgr MetadataExpert find_metadata_elements
ownership_coverage mgr MetadataExpert find_metadata_elements
business_value_signals mgr MetadataExpert count + find
certifications_summary ce ClassificationExplorer get_relationships
people_counts mgr MetadataExpert count + find
feedback_summary ce ClassificationExplorer get_relationships (the expert kwarg is not a slot; unchanged)
usage_context_counts mgr MetadataExpert count + find
semantic_grounding mgr, ce MetadataExpert, ClassificationExplorer mgr counts/finds; ce get_relationships
context_readiness_funnel mgr, ce MetadataExpert, ClassificationExplorer as above
ai_ready_assets mgr, ce MetadataExpert, ClassificationExplorer mgr finds; ce get_relationships
drl_readiness_gates mgr, ce MetadataExpert, ClassificationExplorer mgr finds; ce get_relationships
growth_series mgr MetadataExpert count + find per snapshot
term_definition_completeness mgr MetadataExpert find_metadata_elements
active_contributors ce ClassificationExplorer get_relationships
sum_type_counts mgr MetadataExpert fetch = counts_by_type (via run_analytic_action)
count_elements_by_property mgr MetadataExpert count + find
count_relationships ce ClassificationExplorer get_relationships (the expert kwarg is not a slot)
contextualised_coverage mgr, ce MetadataExpert, ClassificationExplorer mgr counts/finds; ce get_relationships
solution_component_realisation mgr, ce MetadataExpert, ClassificationExplorer as above
karma_leaderboard mgr MetadataExpert find_metadata_elements
engagement_series ce ClassificationExplorer get_relationships per week
orphan_glossary_terms mgr, ce MetadataExpert, ClassificationExplorer mgr counts/finds terms; ce get_relationships
stale_assets mgr MetadataExpert count + find
metric_trend mgr, ce EgeriaTech, ClassificationExplorer mgr is forwarded to any target routine's mgr (incl. asset_composition, which also calls ValidMetadata); ce only to targets' ce slots, all ClassificationExplorer
asset_composition mgr EgeriaTech MetadataExpert count/find and get_sub_types (ValidMetadata*) through vm=mgr; vm is not an executor slot (A1)
control_implementation_summary client SecurityOfficer get_control_implementation_summary

Gates (§A7)

  • Micro-tests (uv run -p 3.13 pytest tests/micro-tests/, Python 3.13, counts from JUnit XML): without the library, 2257 tests: 2254 passed, 3 skipped, 0 failed (on 872dd83; the second commit changes only a test that is skipped without the library). With egeria-trellis-analytics 0.4.0 (throwaway venv, --no-deps over this tree): 2380 tests: 2379 passed, 1 skipped, 0 failed on 81642a4.
  • Library mirror test (second commit). test_routine_declarations_match_library compares trellis_analytics.routine.routine_declaration(spec) with spec.routine(). Library 0.4.0's routine_declaration(manifest, **extra) does not read inputs from a manifest; its docstring lists inputs among what extra adds. With inputs now declared on all 28 built-ins, the test passes them that documented way (65/0 with the library). Library follow-up (0.4.x): read manifest.inputs directly.
  • Byte-identical: 23 demo specs × DICT/JSON = 46 exec_report_spec results, plus the client-construction/auth call log, are identical under PlainSupply and a clean 6.2.25 (8ce7f02) install. The harness fakes only the Egeria client. The baseline was run twice first to prove it is deterministic.
  • Census: 438 specs with library 0.4.0 (437 without). 2 unrunnable (Asset-Graph, Data-Classes; check 1), 61 format-only, the same 2 warnings, and 0 check-5 refusals. The library's curation_coverage_tile takes no client and declares no inputs, so it passes check 5 (A5).
  • Refusal tests, each asserting its words (tests/micro-tests/test_rs_w2_3_inputs_by_role.py):
    • A missing role: viewer under PlainSupply refuses with "Routine 'who_is_looking' cannot run: its declared input 'viewer' is not supplied (Plain pyegeria does not know who is viewing; …)". No stage runs.
    • An undeclared client slot: check 5 makes the spec Unrunnable at load, and the run refuses with the same sentence, before connecting.
    • A declared input the supply lacks: the environment's CallerTokenExpired refuses before stage 1, with the environment's words.
  • End to end through the real executor, faking only the Egeria client: the semantic_grounding demo spec gets the caller's single client in both slots (one client, the token used, nothing minted). A custom supply is asked by declared class, in declared order, and no client is built from user/password. A spy test shows that the run and the resolver both read inputs through declared_inputs.

Notes

  • PR-B (binder removal) is small (A2). No spec in base_report_formats.py, analytic_demo_specs.py or library 0.4.0 sets analytic_binders. Only the executor, _output_format_models.py, spec_resolver.py and three test files touch it. That PR is the deprecation shim plus tests; no spec rewrite is needed. Binders still run unchanged in this PR.
  • overview_metrics.metric_trend still binds its target routine's mgr/ce by name inside the routine (its own _CLIENT_PARAM_NAMES). That code is outside the executor and this PR's files, and since both values are the caller's one EgeriaTech, the behaviour is identical. Follow-up: let it bind by the target's declared inputs.
  • tests/micro-tests/test_issue_169_analytic_resolver.py::test_allowed_dotted_path_resolves_and_logs_at_debug_only fails when run after certain other test files in a partial selection. This is pre-existing: the same selection fails on 8ce7f02. It passes alone and in the full suite order.

Compatibility (R1, Architect read-back): put this in the next release notes. A config-tier, entry-point or runtime-registered routine that takes mgr/ce/expert/client but declares no inputs is now refused by check 5, at load and at run, with words. The fix is to declare its inputs. Checked: the Portal's re_analytics registration and the egeria-trellis-analytics 0.4.0 manifests take no client argument, so neither is affected.

Read-back: PASS (Architect, ~/.re-gate/READBACK-RS-W2-3-PR-A-2026-11-08.md). Coordinator gate: full micro-suite 3.13, 2257 tests / 0 failed / 3 skipped.

🤖 Generated with Claude Code

An analytic routine's leading arguments are now its declared `inputs`,
supplied in declared order by the environment's RoleSupply; the executor
no longer reads parameter names (mgr/ce/expert/client) to decide what
to pass.

- role_supply.py: RoleSupply protocol, Viewer/Subject, RoleRefused /
  NoSuchReader / CallerTokenExpired, PlainSupply, supply_inputs().
  PlainSupply hands back the caller's one EgeriaTech for EgeriaTech and
  every class it proxies (no subclients: Wave 2 is byte-identical),
  built lazily so a routine with no client never connects (ISSUE-137).
- exec_report_spec / _exec_analytic_chart / _run_analytic_function /
  run_analytic_action take an optional `supply=`; _count_client_params
  and _bind_client_args are removed.
- Check 5 refuses: spec_resolver.declared_inputs() is the one function
  the resolver (at load) and the executor (at run) both call; a binding
  whose leading client arguments differ from the declared egeria_client
  inputs is refused with the same words in both places. A role the
  supply cannot provide refuses before the first stage.
- The 28 built-ins declare inputs by the class each argument calls
  (MetadataExpert, ClassificationExplorer, SecurityOfficer; EgeriaTech
  for asset_composition and metric_trend's first slot).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
trellis-analytics 0.4's routine_declaration(manifest, **extra) does not
read `inputs` from a manifest; its docstring lists inputs among what
`extra` adds. Now that every built-in declares inputs (RS-W2-3), the
mirror test passes them the documented way. Library follow-up: read
manifest.inputs directly.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
@dwolfson
dwolfson merged commit ff78b4f into odpi:main Nov 8, 2026
6 checks passed
dwolfson added a commit that referenced this pull request Nov 8, 2026
Ships #552 (RS-W2-3 PR-A: executor supplies declared input roles) and
#553 (RS-W2-3 PR-B: analytic binder removed; analytic_binders ignored
with a DeprecationWarning for one release).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
mandy-chessell pushed a commit to odpi/egeria-workspaces that referenced this pull request Nov 8, 2026
PortalSupply(request) in egeria_auth.py is the Portal's pyegeria RoleSupply.
Clients are built by class with the request token through use_token (no
password, so no identity switch on a 401); an expired token is refused with
CallerTokenExpired before any Egeria call; every client is built inside the
request's forLineage setting; the viewer is read from the existing
report_viewer contextvar. execute_spec passes the supply to exec_report_spec
and maps CallerTokenExpired to HTTP 401 with the existing 401 words.

Needs a pyegeria release carrying odpi/egeria-python#552 (6.2.26); the
pyegeria-web image must be rebuilt on it.

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