Skip to content

pyegeria HTTP-endpoint audit: SDK fixes, Dr.Egeria features, overview metrics, test coverage - #274

Open
dwolfson wants to merge 47 commits into
odpi:mainfrom
dwolfson:fix/pyegeria-http-endpoint-audit
Open

pyegeria HTTP-endpoint audit: SDK fixes, Dr.Egeria features, overview metrics, test coverage#274
dwolfson wants to merge 47 commits into
odpi:mainfrom
dwolfson:fix/pyegeria-http-endpoint-audit

Conversation

@dwolfson

@dwolfson dwolfson commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

Accumulated fixes and features from an extended pyegeria HTTP-endpoint audit and Dr.Egeria work, spanning 42 commits. Grouped by theme below (see individual commit messages for full detail — most document live verification against a real Egeria server).

pyegeria SDK bug fixes (found via systematic HTTP-endpoint/ISSUE audit)

  • Corrected several REST endpoints/URLs that didn't match Egeria's actual routes (2603117), including a genuinely broken AutomatedCuration.initiate_engine_action missing its {governanceEngineName} path segment (b17f71e)
  • fetch_element timeout retry + a delete-body crash (a6fdbd4)
  • Relationship-list parsing (ISSUE-39/49) and a wrong body validator in reference_data.py (ISSUE-56) (880cc44)
  • AssetCatalog lineage graph used two nonexistent field names, no depth override (9277b23)
  • Governance Status property_name/wire-key mapping (ISSUE-22) (58c23ee)
  • max_mermaid_node_count default raised, dead params on solution blueprint retrieval fixed (ISSUE-23) (dd33b95)
  • 4 real positional-arg/wrong-method bugs (ISSUE-27) (fe2357b)
  • Spurious query-string pagination removed from server-templates endpoint (ISSUE-43) (7114e24)
  • PascalCase → SCREAMING_SNAKE_CASE auto-conversion for specification-properties/by-type (ISSUE-17) (afa5285)
  • fetch_as_is now honors an explicit GUID, warns on silent-duplicate risk (ISSUE-59) (3ea695c)
  • overview_metrics page_size=5000 exceeded Egeria's new 1000-record max (c631f0c)

Dr.Egeria features

  • New WorkingSet/ExternalStandard/AccountingCodes commands, DataHub rename, Reference Data family (f1c5ed1)
  • Domain Identifier migrated from a hardcoded Java enum to a live Valid Metadata Value lookup (2afa6d3)
  • New Action Author commands: Create Embedded Process, Initiate/Cancel Engine Action, plus a new Lineage Linker family (b17f71e)
  • Various Dr.E command/template cleanup (4aff48e)
  • New Link/Unlink Governance Results command (Governance Officer family) — the OMVS wrapper (GovernanceOfficer.link_governance_results/detach_governance_results) already existed but had no compact-spec/dispatcher wiring; now routes through the existing GovernanceLinkProcessor (80526c3)

Overview dashboard metrics

  • count_elements_by_property helper (d2143e7)
  • max_paging_size made env-configurable (EGERIA_MAX_PAGE_SIZE), replacing a hardcoded module constant (18486a9)
  • contextualised_coverage (Usage % contextualised) (8010b1f)
  • karma_leaderboard and engagement_series (57a345c)
  • governed_coverage now returns a Fully/Partial governance split (4c86092)

Test coverage

  • test_create_my_todo teardown fix — was leaking live ToDos (097558c)
  • FileDirectory:CreateAndSurvey governance-action-process test, live-verified (be89676)
  • Coverage-gap audit against recent commits: engine-action URL regression test, Action Author dispatcher-registration coverage, overview_metrics unit tests, max_paging_size config tests (51be451)
  • Regression test for the cross-family attribute-name-pooling hazard that silently corrupted rendered Dr.E templates (29eebc1)

Dependencies

  • uv.lock refreshed to close all 90 open Dependabot alerts (1 critical, ~24 high, ~30 medium, ~9 low) — all resolved within existing pyproject.toml constraints, no manual pins needed (be89676)

Docs / issue tracking

  • Ongoing PYEGERIA_ISSUES.md maintenance: re-verification passes confirming prior fixes still hold, status restructuring by who-can-fix-it (Egeria vs. pyegeria vs. closed), and new/updated issue write-ups (ISSUE-52, -59, -60, and re-checks of -14, -25, -28, -30, -38, -48, -53, -54, -55, -56)
  • New BACKLOG.md entries flagging the live-test risk for Initiate Engine Action and a stale pointer correction in an earlier commit message

Testing

pytest tests/micro-tests passes in full. Most individual fixes/features are documented as live-verified against a real Egeria server in their own commit messages. The FileDirectory:CreateAndSurvey test in be89676 was run against a live qs-view-server this session and confirmed passing.

🤖 Generated with Claude Code

Cross-checked pyegeria's OMVS client methods against two Egeria-team
audits of the Egeria-api-*.http example collections (endpoint-coverage
audit + error-only audit) and against the real 6.1 distribution's
http-client-collections as ground truth.

Fixed URL/body bugs:
- action_author: remove_first_action_process_step missing GUID segment
- asset_maker: _async_unassign_action reassign/unassign copy-paste bug;
  catalog-target methods pointed at automated-curation instead of
  asset-maker
- runtime_manager: publish_open_lineage_event hit the wrong endpoint;
  cohort connect/disconnect/unregister used a stale omag-servers/...
  URL shape instead of cohort-members/...  (disconnect was also DELETE
  instead of POST)
- collection_manager: detach_agreement_actor took two element GUIDs
  against a URL that doesn't exist server-side; real endpoint takes the
  relationship GUID directly (signature change, live caller in
  md_processing/v2/collection_manager_processor.py updated to resolve
  the relationship GUID via ClassificationExplorer.get_relationships
  instead of tracking a GUID that link_agreement_actor never returns)
- classification_explorer: unlink_elements_as_peer_duplicates had a
  related-elements/ vs elements/ path bug and called the classification
  delete helper instead of the relationship delete helper
- governance_officer: addGovernanceDefinitionToElement-style methods
  already correct; removed the license/certification update/decertify
  methods that collided by name with classification_explorer's
  equivalent (functionally identical) implementations under EgeriaTech's
  generic attribute delegation - classification_explorer's now wins
  unambiguously instead of relying on dict insertion order
- reference_data: fixed ValidValuesImplementationProperties naming,
  dropped a fabricated ConsistentValidValuesProperties class (that
  relationship has no properties in the real API)
- project_manager: set_project_dependency/set_project_hierarchy sent a
  fabricated properties class the server doesn't recognize
- server_operations: integration-daemon and engine-host calls used a
  legacy /users/{userId}/... path shape (and integration_daemon
  underscore instead of hyphen) that 404s against a real server
- full_omag_server_config: removed set_server_user_password, which
  called a server-user-password endpoint that has never existed
  (confirmed against ConfigPropertiesResource.java; no callers)

New methods added (endpoint existed server-side, pyegeria had no
client method): connection_maker get_connector_types_by_connector_
provider_class_name; asset_maker detach_catalog_target; lineage_linker
update_lineage/detach_lineage; metadata_expert detach_related_elements_
in_store; project_manager get_project_hierarchy; collection_manager
set/clear_editing/scoping/staging_collection; governance_officer
license_element/certify_element; classification_explorer get_resource_
list/get_supported_by_resource; reference_data valid-value-
implementation/associated/consistent/mapped relationship pairs.

Added pyegeria/http_client_notes.md to record the intentional
divergences above (license/cert routing, removed dead method) outside
the pyegeria/http clients/ directory, since that directory gets
periodically replaced wholesale from an Egeria distribution copy.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…aHub rename, and Reference Data family

Wires up the new Egeria PR (DataHub -> DataSharingHub rename + Liskov
connector rename; ExternalStandard, WorkingSet, AccountingCodes types) plus
the ValidValuesAssignment/ReferenceValueAssignment/SpecificationProperty
Assignment relationships, through the full Dr.Egeria compact-spec ->
processor -> report-spec pipeline.

New/changed Dr.Egeria commands:
- Create Working Set (Collections) - and 5 more real Egeria Collection
  subtypes with no prior command: Event Set, Context Event Collection,
  IT Subsystem, Naming Standard Rule Set, Software Archive
- Create External Standard (External Reference)
- Classify/Reclassify/Declassify Accounting Codes (Curation)
- DataHub -> DataSharingHub: clean rename (delete + recreate, matching
  Egeria's own no-backward-compat-shim choice), COLLECTION_SUBTYPES updated
- New "Reference Data" family (commands_reference_data_compact.json):
  - Link/Detach Element to Valid Values (generic ValidValuesAssignment)
  - Link/Detach Reference Value Assignment
  - Link/Detach Specification Property Assignment
  - Setup/Clear Valid Metadata Value, Map Name, Map Value
  - Set Consistent Metadata Values
  plus two contextual ValidValuesAssignment variants in their natural
  families: Link/Detach Question to Valid Values (Glossary), Link/Detach
  Data Field to Valid Values (Data Designer) - all three share one new
  OM_TYPE-dispatched processor, ReferenceDataLinkProcessor.

pyegeria SDK additions backing the above:
- classification_explorer.py: set/clear_accounting_codes_classification
- valid_metadata.py: link/detach_specification_property (plus a missing
  `logger` import in that module)
- external_links.py: EXTERNAL_REFERENCE_PROPS/TYPES were missing
  ExternalSourceCode entirely (pre-existing gap, fixed alongside adding
  ExternalStandard)

Processing-code verification (per the compact-spec-sync lifecycle - a new
custom_attribute isn't proven wired just because it validates and
templates): caught and fixed a real gap where Working Set's new
`Disposition` attribute wasn't read anywhere in
set_collection_manager_body(), and caught a bug in my own first pass at the
DataHub rename (COLLECTION_SUBTYPES needs the display name for the
`Create {X}` dispatch lookup to resolve, not the bare type name).

Regenerated via refresh_specs --merge-reports; validate_compact_specs: 0
errors; pytest tests/ -m unit and test_gen_report_specs.py both pass; a
--validate smoke test against hand-written markdown confirmed
Create Working Set / Create External Standard resolve end-to-end with
correct qualified names.

Also fixes 4 stale sample-data/egeria-inbox/dr-egeria-help-*.md files that
were accidentally deleted then restored during this session's cleanup.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…e Valid Metadata Value lookup

Egeria changed GovernanceDomain from a Java enum to a domainIdentifier
Valid Metadata Value set, so deployments can extend the list of governance
domains without rebuilding Egeria. Dr.Egeria's Domain Identifier attribute
was still typed as a hardcoded "Enum" (GovernanceDomains) tied to the old
fixed set of 9 values.

- md_processing/v2/parsing.py: new `legacy_enum_type` hint for "Valid
  Value"-style attributes. Old ALL_CAPS enum names (and new-style display
  names for the original 9 - the normalization already treats underscores
  and spaces the same) resolve locally via the existing GovernanceDomains
  enum with no server call; anything the old enum doesn't recognize (a new
  display name like "All Domains", or a deployment-added custom domain)
  falls through to the existing live valid_metadata_values lookup/cache
  mechanism (already implemented, just not wired to this attribute before).
- Domain Identifier (Governance Officer - shared via bundle inheritance by
  all 28 governance-definition subtype commands, e.g. Create Governance
  Policy/Risk/Threat, plus Action Author's 3 governance-process commands
  which reference the same globally-shared attribute name) and Role Domain
  Identifier (Solution Architect's Create Solution Role) migrated from
  style "Enum" to "Valid Value", property_name "domainIdentifier",
  legacy_enum_type "GovernanceDomains".
- Verified live against the real domainIdentifier valid value set: the
  original 9 domains kept their exact ordinals (Data=1, Privacy=2, ...,
  Other=99) except ALL is now officially labelled "All Domains"; confirmed
  old-style, new-style, and two already-deployed custom domains (Drug
  Development=20, Sustainability=9) all resolve to the correct int via
  direct unit-level calls against _process_attribute_value.

Processing-code verification caught a real, pre-existing bug unrelated to
the style migration itself: set_solution_architect_body() never read Role
Domain Identifier into the outgoing body at all (old style or new) - fixed
alongside the migration.

Regenerated via refresh_specs --merge-reports; validate_compact_specs: 0
errors; pytest tests/ -m unit and test_gen_report_specs.py both pass.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…RIA_ISSUES.md triage

Two real bugs found and fixed while investigating why a bulk dr_egeria
--process run against the live server was crashing/timing out, plus a
pass through the whole open-issues tracker.

- md_processing/v2/processors.py: AsyncBaseCommandProcessor.fetch_element()
  now retries the same ClassificationExplorer call (up to 2 attempts) on a
  PyegeriaTimeoutException instead of immediately falling through to the
  MetadataExpert fallback. A timeout means either the request was
  transient (retrying the same call is exactly as likely to succeed as
  switching endpoints) or the server is under sustained load (a different
  endpoint is no more likely to help, and its response shape isn't
  guaranteed compatible with what downstream code expects -- switching on
  a timeout was trading a clean failure for a KeyError crash).
  MetadataExpert stays the fallback for genuine non-timeout failures, and
  as a last resort after retries are exhausted. (ISSUE-51)

- pyegeria/core/_server_client.py: validate_open_metadata_delete_request
  now builds a default {"class": "OpenMetadataDeleteRequestBody"} when no
  body is given, instead of returning None straight into a .model_dump()
  call three lines later in every caller of the shared
  _async_open_metadata_delete_body_request helper. Was an unconditional
  AttributeError crash for the common "just delete it" case (e.g.
  MetadataExpert.delete_metadata_element(guid) with no body). (ISSUE-31)

- PYEGERIA_ISSUES.md: fixed a numbering bug in the tracker itself (three
  ISSUE-# values were each independently reused for two unrelated
  entries -- renumbered the later duplicates 45/46/47 -> 53/54/55, noted
  in the file header). Triaged all 21 open entries; closed the two above
  plus ISSUE-19 (found already fixed in code, never marked closed).
  Updated ISSUE-52 (Egeria-server integration-connector write load) with
  a post-restart observation.

Verified: pytest tests/ -m unit passes throughout. Re-ran the same
100+-command dr-egeria help file that originally crashed on its first
command (ISSUE-51) and separately timed out repeatedly (ISSUE-52) --
against a freshly-restarted server it now completes cleanly end-to-end,
every command SUCCESS, 0 errors, ~6 minutes total (vs. 70+ minutes and
repeated timeouts before).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…body validator in reference_data.py

- pyegeria/omvs/metadata_expert.py: process_related_element_list()'s
  relationship_list=True branch looked for the relationship list under
  "elementList" instead of the real key, "relationships", nested inside
  the server's OpenMetadataRelationshipListResponse envelope
  ({"relationshipList": {"relationships": [...]}}). Also, two call sites
  (get_metadata_element_relationships, get_all_metadata_element_
  relationships) weren't passing relationship_list=True at all, so they
  looked for the wrong top-level key too. Both bugs fixed; confirmed live
  with a real ResultsSet/SavedQuery/SmartQuery relationship that all three
  affected methods (those two plus find_relationships_between_elements,
  which already had relationship_list=True set and so was only hit by the
  inner-key bug) now return the relationship instead of "No elements
  found". (ISSUE-39, ISSUE-49)

- pyegeria/omvs/reference_data.py: 7 relationship-link methods
  (link_valid_value_member, link_valid_values_assignment,
  link_reference_value_assignment, link_valid_value_implementation,
  link_associated_valid_values, link_consistent_valid_values,
  link_mapped_valid_values) called _async_create_element_body_request,
  which validates the body against NewElementRequestBody's Pydantic model
  (class: Literal["NewElementRequestBody"]) -- but every one of these
  methods' own docstring documents a NewRelationshipRequestBody sample
  body, which always fails that Literal check. Swapped to the matching
  helper, _async_new_relationship_request (validates against
  NewRelationshipRequestBody, identical call signature, no other code
  changes needed). Verified live: linked a real ValidValueMember to a
  ValidValueSet using exactly the documented sample body -- previously
  failed on every call, now succeeds. (ISSUE-56, reported by another
  session working on resource-explorer)

Verified: pytest tests/ -m unit passes throughout both fixes.
Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…names, no depth override

pyegeria/omvs/asset_catalog.py: _async_get_asset_lineage_graph_by_guid
hardcoded "queryGraphDepth": 5 with no way to override it. Checked the
real AssetLineageGraphRequestBody Java class (-> QueryOptions ->
GetOptions): that field name doesn't exist on the body at all -- the real
field is "graphQueryDepth", inherited from GetOptions (which also defines
"maxMermaidNodeCount" and "includeOnlyRelationships"). So the hardcoded 5
was always silently dropped server-side; the server's own default
(coincidentally also 5) was what actually applied the whole time. Found
the same problem on "relationshipTypes", which isn't real either -- the
actual field is "includeOnlyRelationships", so the caller-supplied
relationship-type filter has never worked either.

Fixed both field names, and added graph_query_depth/max_mermaid_node_count
parameters (the real maxMermaidNodeCount field was never sent before) to
all three call layers: _async_get_asset_lineage_graph_by_guid,
get_asset_lineage_graph_by_guid, get_asset_lineage_mermaid_graph.

Verified live: created a throwaway asset, spied on the outgoing request
body, confirmed graph_query_depth=7/max_mermaid_node_count=42 arrive as
graphQueryDepth/maxMermaidNodeCount in the POST body and the server
accepts the call. pytest tests/ -m unit passes. (ISSUE-24)

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ve ToDos

tests/functional-tests/test_my_profile.py::test_create_my_todo created a
real ToDo against the live server on every run and never deleted it,
silently accumulating orphaned ToDo entities across repeated test runs.
Added a finally block that deletes the created GUID via a MetadataExpert
client, matching the delete pattern used elsewhere in this file's own
verification steps.

Verified live: ran the test, confirmed the ToDo was created, then
confirmed the same GUID is soft-deleted immediately after the run.
pytest tests/ -m unit passes. (ISSUE-45)

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…apping (ISSUE-22)

Two stacked bugs in the Impact/Confidence/Confidentiality/Criticality/
Retention classification path:

1. commands_curation_compact.json: the 'Governance Status' attribute had
   been migrated to style 'Valid Value' but with no explicit property_name,
   so it auto-derived to the nonexistent 'governanceStatus' property,
   silently falling through to a stale ALL-CAPS fallback list. Fixed via
   the Spec Editor API: property_name is now 'statusIdentifier', and
   valid_values matches the real GovernanceClassificationStatus set
   (Discovered/Proposed/Imported/Validated/Deprecated/Obsolete/Other).

2. md_processing/v2/curation.py: _GOVERNANCE_SHARED_FIELDS mapped
   'Governance Status' to the outgoing JSON key 'status', but the real
   field on ImpactProperties/ConfidenceProperties/ConfidentialityProperties/
   CriticalityProperties/RetentionClassificationProperties is
   statusIdentifier -- 'status' is silently ignored by the server. Fixed
   the mapping to 'statusIdentifier'.

Ownership was not affected (its ClassificationSpec uses its own explicit
field map, not _GOVERNANCE_SHARED_FIELDS); Retention was affected and is
fixed by the same change.

Verified end-to-end via a live Classify Impact run through the actual
Dr.Egeria pipeline: Governance Status: Validated -> statusIdentifier: 3
persisted correctly. Test element cleaned up afterward.

Templates regenerated via refresh_specs --merge-reports.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…arams on solution blueprint retrieval (ISSUE-23)

Raised the remaining max_mermaid_node_count=5 defaults to 10 across
_server_client.py's shared find/get helpers (_async_get_name_request,
_async_get_guid_request, _async_get_request_body_request,
_async_activity_status_search_request, content-status/deployment-status
search variants) and asset_catalog.py's lineage-graph methods, matching
_async_find_request's already-raised default.

Also fixed get_solution_blueprint_by_guid/_async_get_solution_blueprint_by_guid
(solution_architect.py) -- when body was None, no body was ever sent, so
graph_query_depth/max_mermaid_node_count were dead parameters regardless of
the default. Same 'dead parameter' shape ISSUE-26 already fixed for
get_info_supply_chain_by_guid/get_solution_role_by_guid; fixed the same way
by building an AnyTimeRequestBody when body is None, and added
max_mermaid_node_count as an explicit parameter (previously not accepted at
all).

get_solution_component_by_guid was checked and found NOT to share this bug
-- it already routes through _async_get_guid_request and forwards both
parameters correctly.

Verified live via request-body spies for all three: the shared-default
raise, the blueprint fix, and the component non-fix. pytest tests/ -m unit
passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…SSUE-27 audit

Scripted audit of ~350 self._async_*(...) delegation call sites across
pyegeria/omvs/*.py (all multi-line all-positional calls, plus single-line
calls with 3+ bare-identifier positional args), each cross-checked against
its target async method's actual (last-defined, matching Python's real
name resolution) parameter order. Found and fixed 4 real bugs, all in
governance_officer.py/solution_architect.py; everything else was either an
exact match or a same-value/different-local-name false positive.

1. solution_architect.py: delete_solution_role's sync wrapper transposed
   cascade_delete and body when calling _async_delete_solution_role --
   sent the bool into the body slot and the body dict into the cascade
   slot. Fixed the call order.

2. governance_officer.py: add_regulator_to_regulation called the wrong
   async method entirely (_async_link_governance_results, a copy-paste
   leftover hitting a completely different relationship/URL) instead of
   _async_add_regulator_to_regulation. Fixed the call target.

3. governance_officer.py: _async_add_regulator_to_regulation/
   _async_detach_regulator_from_regulation both built the wrong URL
   (self.url_marker is already governance-officer, so the hardcoded
   literal governance-officer/ after it duplicated the segment) and the
   attach method used the wrong properties class (GovernanceResultsProperties
   instead of RegulatorProperties). Fixed both against
   Egeria-api-governance-officer.http's ground truth.

4. governance_officer.py: detach_governance_results passed data_asset_guid
   twice, pushing the real body argument out of the target's 3-parameter
   signature entirely. Fixed by removing the duplicate.

Verified live against qs-view-server: delete_solution_role via a
request-body spy; add_regulator_to_regulation/detach_regulator_from_regulation
end-to-end with throwaway Regulation+Organization elements, cleaned up
afterward; detach_governance_results via a request-body spy.

Also logged ISSUE-57 (Egeria server, not pyegeria-fixable): live-verifying
the untouched, unrelated _async_link_governance_results surfaced a genuine
server-side relationship end1/end2 ordering bug, out of scope here.

pytest tests/ -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ver (Egeria server, not pyegeria)

Re-ran the updateNote 404 repro end-to-end via pyegeria (create DataStructure
+ NoteLog + Note all succeed, update 404s) against the current qs-view-server
post the environment restart noted in ISSUE-52, to rule out this being a
stale finding from the original redeploy. Still reproduces identically.
Nothing to fix client-side -- no code change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…lates endpoint (ISSUE-43)

_async_get_server_templates_by_dep_impl_type built a URL with
?startFrom={..}&pageSize={..}&getTemplates=true appended, but
Egeria-api-runtime-manager.http's getServerTemplatesByDeployedImplementationType
worked example shows a plain POST with no query string at all -- pagination
and the Template filter both belong in the FilterRequestBody. The sibling
non-templates method in the same file already builds the URL correctly with
no query string. Removed the spurious suffix; the shared
_async_get_name_request helper already builds startFrom/pageSize into the
body correctly regardless (confirmed via a before/after request-body spy).

Verified live against qs-view-server: the call now succeeds cleanly. A real
2+-page pagination comparison still isn't possible with this demo dataset
(no Template-classified software servers of any tested deployed
implementation type exist), same limitation the issue already noted --
resolved via ground-truth comparison instead, per the issue's own suggested
next step.

pytest tests/ -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…side count discrepancy

Numbers have shifted since 2026-07-24 (demo data changed): count is now 58,
get_relationships now 57, not 276/55. Cross-checked against a second,
independent pyegeria list-based method (MetadataExpert.find_relationships_between_elements,
a different OMVS client/endpoint) with the same filter -- also returns 57,
identical GUID set to get_relationships. Two independent pyegeria code
paths agree exactly with each other; only the server's native COUNT(*)
endpoint disagrees, by 1. Re-confirmed the 'not the type filter' and 'not
status/effectivity' findings still hold with current data. Nothing to fix
client-side -- genuinely server-side, narrower but still real. No code
change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…pyegeria since 2026-08-03

Re-checked live and found _async_update_comment (_server_client.py)
already auto-fetches qualifiedName via _async_get_comment_by_guid when the
caller's body doesn't supply one -- the exact workaround this entry
described as living in 'application code', actually shipped directly in
pyegeria itself since 92cde6a (2026-08-03), just never reflected in the
tracker. Verified live: created a throwaway DataStructure + comment,
called update_comment with no qualifiedName in the body -- succeeds
cleanly. No pyegeria code change needed, tracker correction only.

Also includes a concurrent edit from another session correcting ISSUE-58
(renumbered from a same-day ISSUE-57 collision) -- self-diagnosed as a
caller error, not a resolver bug, landed on this shared tracker file while
this session was working.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ecification-properties/by-type (ISSUE-17)

Original diagnosis (server-side enum-binding drift, unfixable) was wrong.
Egeria-api-valid-metadata.http's getSpecificationPropertyByType worked
example uses specificationPropertyType=PRODUCED_GUARD -- SCREAMING_SNAKE_CASE
-- which succeeds immediately when tried directly. The real problem:
get_specification_property_types() returns its stringMap verbatim from a
different server endpoint (specification-properties/type-names) that uses
PascalCase keys (e.g. ProducedGuard) for the same enum -- a genuine
cross-endpoint casing inconsistency on the server, but one pyegeria can
paper over: _async_get_specification_property_by_type did zero conversion,
so feeding it the single most natural, discoverable input (a key from
get_specification_property_types()) always 400'd.

Fixed by auto-converting PascalCase/camelCase input to SCREAMING_SNAKE_CASE
via a plain regex (no hardcoded type list); an already-correct
SCREAMING_SNAKE_CASE value passes through untouched.

Verified live: all 12 real specification property types, fed straight from
get_specification_property_types() into get_specification_property_by_type()
unmodified, now resolve successfully (10 return results, 2 correctly return
'No elements found' -- no data of that type). Already-correct form
(PRODUCED_GUARD) still works. pytest tests/ -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…finition REST endpoint

User's decision: no pyegeria wrapper or Dr.Egeria command work until the
server-side endpoint actually exists. No code change.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ing cold-start crawl

quickstart-egeria-main had been restarted again (uptime ~1h vs. sibling
containers' 8h+), giving a fresh post-restart observation window rather
than a stale one. OpenAPICataloguer's creation rate is clearly decelerating
(134->86->48->26->8->6 across shrinking trailing windows), not a steady
continuous rate -- better characterized as a one-time cold-start crawl that
converges than unbounded re-crawling. JacquardDigitalProductLoom logged
zero activity in this window (too early to rule out recurrence).
Server responsiveness confirmed fast right now (0.02-0.19s/call, no
timeouts). Net: acute symptom not currently reproducing; still genuinely
Egeria-server/deployment-config territory, lower urgency than originally
assessed. No pyegeria code change -- infra investigation only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ver (Egeria server, not pyegeria)

Re-ran with a raw body passed straight through MetadataExpert.find_metadata_elements
(bypassing pyegeria body-construction, equivalent to the original raw-curl
repro) against the current, restarted qs-view-server -- metadataElementSubtypeNames
still has zero effect (byte-identical type mix with/without it). Nothing to
fix client-side. No code change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…e-GUID artifact in the broad scan

Re-ran both independent cross-checks (direct exhaustive GlossaryTerm scan
vs. type-mix within an exhaustive Referenceable scan; SemanticAssignment
relationship participants vs. Referenceable scan membership) against the
current, restarted qs-view-server. Magnitude improved (61%/73% coverage
this time vs. the original ~54%/5.6%) but the core defect clearly persists
-- still a real, substantial undercount, not a rounding gap.

New this pass: the exhaustive Referenceable scan itself returned 39
duplicate GUIDs across different pages (19166 total vs 19127 distinct) --
not previously noted, suggests unstable server-side result ordering for
this specific broad-type scan as a plausible (unconfirmed) mechanism behind
the omissions too. Nothing to fix client-side. No code change, tracker
note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…onfirm ISSUE-55 still accurate

ISSUE-55's original entry had a second, unrelated issue (glossary-term
sort-scope/pagination) merged in underneath it with no --- divider and an
orphaned second Status: line, from the original egeria-workspaces-fs
consolidation. Split it out as its own numbered entry (ISSUE-60), no
content changed.

Re-checked ISSUE-55 itself against the current server/pydantic models --
FindRequestBody still has no exclude-type/negation field anywhere (checked
both Egeria-api-metadata-expert.http and the live FindRequestBody model).
Per user's decision, left as a documented, blocked enhancement -- nothing
actionable in pyegeria until Egeria adds real server-side negation support.
No code change.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…plicate risk (ISSUE-59)

Two-part fix for Create <X> upsert commands silently duplicating an
element when the caller changes ### Qualified Name to something the as-is
lookup has never seen before:

1. fetch_as_is() now tries an explicit ### GUID attribute directly via
   fetch_element() before any qualified-name-derivation or name-based
   lookup. Previously this attribute was accepted and parsed but never
   consulted for the as-is lookup at all -- the one case where a caller
   most needs the check to target a specific element (renaming its QN)
   was exactly the case it was silently ignored.

2. The existing 'same Display Name, different QN' guard (added since this
   issue was filed) now calls self._add_warning() instead of only
   logger.info() -- the risk was already detected server-side, it just
   never reached the caller in --process/--validate output. The warning
   names the existing element's GUID and how to target it directly.

Verified live with the exact original repro: Create Perspective with a
new Qualified Name and no GUID now creates a second element AND prints an
explicit warning (previously fully silent); a follow-up re-run with the
same QN plus the original GUID correctly rewrites to Update, returns the
same GUID, and the fetched element shows the QN actually changed with
version incremented 1->2 -- no duplicate. Test elements cleaned up.
pytest tests/ -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…irmed unreliable, not a design ambiguity

Checked pyegeria's request shape against Egeria-api-glossary-manager.http's
findGlossaryTerms worked example -- byte-identical, not a client-side
body-construction bug. Tested live: sequencing_property=displayName/
qualifiedName both still return server-internal order, not alphabetical.
Went further than the original repro -- compared None/ASCENDING/DESCENDING/
CREATION_DATE_RECENT: None/DESCENDING/CREATION_DATE_RECENT return identical
first-page sets while ASCENDING differs, so the parameter isn't fully
inert (influences scan strategy) but never produces an actual sorted
order. Confirms fetch-all-then-sort client-side is necessary, not just
cautious. Product-strategy questions (fetch-all vs paged UI, maxPageSize,
shared helper) remain genuine team decisions, not attempted here -- nothing
client-side to fix regardless. No code change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Spot-checked the exact original repro plus the three previously-crashing
scramble sites and ClassificationExplorer.find_root_elements's field-scramble
fix, live against qs-view-server -- all still correct, no regression.
pytest tests/ -m unit passes. No code change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ence

Re-confirmed live: bogus GUID raises PyegeriaNotFoundException (not
NameError), and a real find-then-fetch-by-guid round trip succeeds with
the correct identifier. Also fixed a stale 'currently broken outright'
note in the Quick-reference table that hadn't been updated when this was
fixed 2026-08-05. No code change.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Re-ran test_create_my_todo directly against the live server -- passes,
confirms its own cleanup, and a direct get_metadata_element_by_guid
immediately after correctly raises PyegeriaNotFoundException. No code
change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Recreated the exact repro fresh (new ResultsSet + SavedQuery pair, real
SmartQuery link) -- get_metadata_element_relationships correctly returns
the relationship instead of 'No elements found'. Test elements cleaned up.
pytest tests/ -m unit passes. No code change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Created a fresh ValidValueDefinition pair, linked via
_async_link_valid_value_member() -- succeeds. Test elements cleaned up.
No code change, tracker note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ia first, pyegeria second, closed last

Split the flat 'Open Issues' section (previously several loosely-related
## subsections mixing pyegeria bugs, Egeria server bugs, design
discussions, and docs gaps) into two clearly-scoped sections ordered by
what's actionable:

1. 'Open Egeria Server issues (not fixable in pyegeria)' -- ISSUE-30, 57,
   52, 53, 54, 38, 41, 60. Pure Egeria-side defects; nothing here can be
   fixed by editing this repo.
2. 'Open pyegeria items (including follow-ons blocked on an Egeria fix)'
   -- ISSUE-48, 55. Both are pyegeria/Dr.Egeria SDK gaps that are
   currently blocked on an Egeria Server capability that doesn't exist
   yet; each entry already documents what pyegeria needs to build once
   that capability ships, so the follow-on work isn't lost.

Everything else (all entries whose current Status is fixed/n/a/not-a-bug)
moved to the existing Appendix, unchanged in content -- this consolidates
several since-reclassified entries (ISSUE-14, 17, 39, 43 were fixed
pyegeria-side but still sitting under the old 'Egeria Server' heading;
ISSUE-19 was fixed but sitting under 'Docs' gaps) into the Fixed appendix
where they now belong.

No issue content was altered -- verified every '### ISSUE-N' title line is
byte-identical to HEAD before/after (diff on sorted title lists is empty).
Reordering only. Also fixed a real extraction bug caught before writing
the file: an initial mechanical pass mistook '#'-prefixed Python comment
lines inside fenced code examples for markdown headers, which would have
silently truncated ~18 entries (ISSUE-34 lost 724 words, ISSUE-60 lost
352, several others 100-250 words each) -- redone fence-aware, verified
word-for-word against the original before replacing the file.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…biguity warning (ISSUE-59)

The 'Automatic Command Rewriting (Upsert)' section only described matching
by Qualified Name -- stale since ISSUE-59's fix: an explicit ### GUID now
takes priority for the as-is lookup, and re-running Create <X> with a new
Qualified Name under the same Display Name now surfaces an explicit
warning instead of silently duplicating. Added a paragraph documenting
both, including the exact warning text and how to correctly rename an
element's Qualified Name via ### GUID.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…just CPU load

Recurred while retrying the help-Glossary --process step. docker stats
showed Postgres at up to 650% CPU; connecting directly found the real
mechanism -- 38 connections stuck 'idle in transaction' against the egeria
DB, all holding the same entity-table query shape open. CPU eased over 15
minutes without by-name-search latency improving at all, showing this is a
connection-holding problem, not simple compute starvation. No code change,
diagnostic note only.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Three new command surfaces in Dr.Egeria v2, added via the Spec Editor
REST API (compact-spec edits) plus bespoke processors where the
generic family walkers don't apply:

Action Author family:
- Create Embedded Process (EmbeddedProcess -> Action -> Process ->
  Asset; upsert-by-qualified-name via AssetMaker's generic asset
  endpoints, same pattern as report.py's ReportProcessor since no
  dedicated OMVS wrapper exists for this type).
- Initiate Engine Action (ad-hoc runtime request against a governance
  engine, distinct from the family's existing design-time Governance
  Action Process/Type/Step definitions). Fixed a real pyegeria bug
  along the way: AutomatedCuration._async_initiate_engine_action's URL
  was missing the required {governanceEngineName} path segment
  entirely (confirmed against AutomatedCurationResource.java's actual
  @PostMapping route) -- the method was unusable before this. Verified
  live: the fixed URL now reaches Egeria's real business logic
  (governance-engine-name resolution, request-type validation), not
  the malformed-URL client error it hit before.
- Cancel Engine Action. New "Cancel"/"Initiate" verbs added to
  STANDARD_VERBS (extraction.py) since neither existed -- both
  intentional new verbs, not overloading Delete/Remove/Create (Cancel
  doesn't erase the audit record; Initiate is never an idempotent
  upsert the way every other Create command is).

Lineage Linker family (new):
- One generic Link/Update/Unlink command triple covering all seven
  Lineage Linker OMVS relationship types (DataFlow, ControlFlow,
  ProcessCall, LineageMapping, DataMapping, UltimateSource,
  UltimateDestination) via a Relationship Type selector attribute --
  mirrors the OMVS client's own generic link_lineage() design rather
  than 14 thin per-type command wrappers. Link and Unlink share one
  processor class (LineageLinkProcessor) because compact-spec tooling
  treats every LINK_VERBS member as synonyms for variant-registration
  purposes, so two different classes would silently clobber each
  other's dispatcher slot -- same constraint CurationLinkProcessor
  already resolves the same way.
- Found and fixed a second pyegeria/Egeria-platform bug along the way:
  deleteRelationshipInStore rejects its own server-side default
  deleteMethod (LookForLineage) for relationship deletes
  (OMAG-COMMON-400-032) -- confirmed live against a real DataFlow
  relationship, and confirmed independently reproducing in unrelated
  integration connectors (JacquardDigitalProductLoom,
  OMAGServerPlatformCataloguer) in the live server's own logs, so this
  isn't specific to the new command. Added the missing delete_method
  field to DeleteRelationshipRequestBody (models.py) so callers can
  override it; LineageLinker.detach_lineage now passes SOFT_DELETE
  explicitly.

Verified against a live server (quickstart, qs-view-server): created
and inspected a real EmbeddedProcess element directly, created and
inspected a real DataFlow relationship directly (all properties
landed correctly), unlinked it and confirmed the relationship was
actually gone, confirmed Initiate Engine Action's fixed URL reaches
real Egeria business logic. compact-spec validation (0 errors),
pytest -m unit, and test_gen_report_specs.py all pass.

Also fixes a same-name attribute-pooling hazard the new Lineage Linker
family's original attribute defs accidentally tripped: attributes with
the same NAME across different family JSON files get treated as one
shared pool by generate_md_cmd_templates, so a same-named-but-
different-definition "Relationship Type"/"Guard"/"Label"/"Description"
briefly clobbered unrelated commands' rendered template output in
Glossary/Curation/Action Author (caught via git diff on the synced
egeria-workspaces-fs templates, fixed by renaming the Lineage-Linker-
specific one and aligning the rest to the existing canonical text).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
New count_elements_by_property(mgr, type_name, property_name,
property_value, as_of) — counts ACTIVE elements of a given type whose
named string property equals a value, e.g. counting DigitalProduct
elements by deploymentStatus for the Egeria Overview dashboard's Data
Products tile (egeria-workspaces-fs OVERVIEW_NEXT_STEPS.md "Remaining
app wiring"). Same native-count-with-fallback seam as the existing
count_elements (one cheap native count_metadata_elements call, falls
back to len(find_metadata_elements(...)) on older servers), just with
a searchProperties EQ condition instead of a bare type filter.

Verified live against quickstart's Egeria server (via a live-patched
copy in the quickstart-pyegeria-web container's site-packages, since
that container runs the published pyegeria package, not this dev
checkout): correctly counted 2 ACTIVE of 6 total DigitalProduct
elements. pytest -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…bump deps

- test_automated_curation.py: point at the actual local server names
  (good_server_1 -> qs-metadata-store, good_view_server_2 for the UC
  server-element test) instead of stale names from an older
  environment; update the UC-from-template test's target
  (laz3/port 8080 -> uc-unity-coco/port 8087, matching the actual
  running container); add test_initiate_file_directory_create_and_survey
  exercising initiate_gov_action_process against
  FileDirectory:CreateAndSurveyGovernanceActionProcess.
- uv.lock: routine dependency version bumps.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Confirmed live 2026-08-17 (after the quickstart Egeria redeploy):
ClassificationExplorer.get_relationships(page_size=5000) now fails
with OMAG-COMMON-400-010 "The number of records to return, 5000... is
greater than the allowable maximum of 1000" on
findRelationshipsBetweenMetadataElements. count_relationships()
catches this as a total failure and returns None, so every caller
silently degraded instead of erroring -- context_readiness_funnel's
`lineage` stage, ai_ready_assets' composite (which also depends on
DataFlow relationships), and any AttachedRating/Comment/Like/Tag/
NoteLog count via feedback_summary() were all quietly returning
None/0-that-looked-intentional rather than their real values.

Caught while re-verifying the Overview dashboard's Data Products tile
(this session's prior commit) after the redeploy -- its
dataProductsRatings=None looked like "confirmed zero ratings" but was
actually this same silent failure; the true value (also 0, re-verified
after this fix) happened to match by coincidence, not because the
earlier check was actually sound.

Replaced the three hardcoded page_size=5000 call sites with the
existing DEFAULT_CAP constant (500, already the established cap used
throughout this file, safely under the new 1000 server limit) --
count_relationships (line 304), and the two DataFlow relationship
fetches inside ai_ready_assets (lines 727, 848).

Verified live: funnel.lineage 372→null became 9 (real count);
funnel.aiReady 0→null-masked became 4; Data Products' dataProductsRatings
now computes a real (still coincidentally 0) result instead of
silently failing. pytest -m unit passes.

Note for whoever picks this up: `grep -rn "page_size=5000"` also hits
egeria-workspaces-fs's insights_handler.py (one call site) and this
repo's test_overview_asof.py -- not fixed here, out of scope for the
Overview dashboard work this was found during, but likely the same
live bug wherever it's hit.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Previously a bare hardcoded module constant in pyegeria/core/_globals.py,
completely disconnected from the .env/config.json settings system every
other pyegeria default already goes through. Follow-up to c631f0c (the
page_size=5000 fix): that fixed the three call sites that had drifted
to their own oversized literal, but max_paging_size itself was still
not something a deployment could tune without a code change if
Egeria's server-side page-size limit changes again.

New settings field egeria_max_page_size (EGERIA_MAX_PAGE_SIZE env var
/ "Egeria Max Page Size" in config.json), same Field(default=...,
alias=...) pattern as egeria_width, merged in load_app_config() the
same way. _globals.py now reads it from settings at import time
(wrapped in try/except, falling back to the bare 500 default if
settings can't load yet -- e.g. mid-bootstrap, no .env present -- so
this can't break the import chain).

overview_metrics.py's DEFAULT_CAP now derives from max_paging_size
instead of an independent hardcoded 500 -- one source of truth instead
of two constants that happened to match today by coincidence.

Verified live: EGERIA_MAX_PAGE_SIZE=750 override confirmed working
both standalone and inside the quickstart-pyegeria-web container
(docker exec -e EGERIA_MAX_PAGE_SIZE=750 ... -> max_paging_size == 750).
Default (no override) still correctly resolves to 500, safely under
Egeria's 1000 max. pytest -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
OVERVIEW_NEXT_STEPS.md's "Usage % contextualised" looked blocked on a
genuine per-asset participation traversal (no native "count of
elements reachable from a relationship" API exists in Egeria/pyegeria
today). It isn't, once routed through the right relationship instead
of trying to walk every asset: `ImplementedBy` (model 0737, Solution
Implementation) links a SolutionComponent to its concrete
implementation -- end1 is always SolutionComponent, end2 is whatever
actually implements it.

Confirmed live 2026-08-17: on this dataset end2 is a real mix (109
GovernanceActionType, 31 genuine Asset-subtype elements --
IntegrationConnector/SoftwareServer/Topic/SoftwareServerPlatform), so
filtering end2 to Asset-subtype elements and counting distinct GUIDs
is a single bounded relationship fetch, not a traversal. Also checked
the sibling relationship ImplementationResource -- connects only to
GovernanceActionType in this dataset, not useful here, not included.

New contextualised_coverage(mgr, ce, as_of) returns
{contextualisedCount, assetTotal, contextualisedPct}. Documented as a
proxy in its own docstring, not the literal metric: confirms an asset
was given *some* solution-design context via ImplementedBy, not that
its specific SolutionComponent is itself wired into an
InformationSupplyChain/SolutionBlueprint (would need a second
composition-relationship hop) -- same single-hop tradeoff every other
proxy metric in this module already makes (e.g. `lineage` counting
DataFlow relationships without confirming strict-sense lineage
semantics per edge).

Verified live: contextualisedCount=31, assetTotal=384,
contextualisedPct=8.1. pytest -m unit passes.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…0b1f)

Four gaps identified by comparing recent production changes against existing
test coverage, all closeable without a live server:

- test_automated_curation_engine_action.py: regression test for
  AutomatedCuration._async_initiate_engine_action's URL shape. b17f71e's
  commit message documents this method was unusable before that commit --
  the URL was missing the required {governanceEngineName} path segment --
  and nothing protected against it regressing. Mocks _async_make_request,
  asserts the URL, body shape, and sync-wrapper delegation.

- test_action_author_processor_coverage.py: dispatcher-registration coverage
  for the "Action Author" family, mirroring test_governance_processor_coverage
  .py's existing check for "Governance Officer". register_governance_processors
  is family-name-gated (CLAUDE.md's documented gotcha) and "Action Author" was
  added as a second gated family in b17f71e with no equivalent test -- this
  walks the real compact specs and asserts every Action Author command variant
  reaches a processor, the OM_TYPE-routed Link commands reach their dedicated
  processors, and Create Embedded Process / Initiate Engine Action / Cancel
  Engine Action reach their bespoke override registrations.

- test_overview_metrics.py: unit tests for contextualised_coverage (8010b1f)
  and count_elements_by_property (d2143e7), the two newest overview_metrics
  functions -- both were only "verified live," not added to the suite that
  covers every sibling metric.

- test_globals_max_paging_size.py: unit tests for the env-configurable
  max_paging_size added in 18486a9 -- default value, EGERIA_MAX_PAGE_SIZE
  override, and the fallback-to-500 path when settings can't load yet, plus
  confirming overview_metrics.DEFAULT_CAP tracks it as one source of truth.

pytest tests/micro-tests/ passes (including the full suite together, to rule
out cross-test pollution from the reload-based max_paging_size tests).

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Initiate Engine Action has a real side effect (kicks off whatever the
target governance engine is configured to run), unlike every other
Dr.Egeria command's metadata-only create/update/delete -- flagged during
today's test-coverage audit before adding any live functional/scenario
test for it. Current coverage stays at the mocked/unit level
(dispatcher-registration + client URL-shape tests added this session);
live coverage needs a decision on an isolated test target first.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
karma_leaderboard: top-N people by karma. One bounded find over
ContributionRecord elements (karmaPoints is a scalar property on the
record itself, not derived from counting related things), filtered to
Person-anchored records via the standard Anchors classification each
element already carries, sorted desc.

engagement_series: weekly-bucketed feedback trend. Reuses the same 5
feedback-relationship-type queries feedback_summary() already makes,
keeping each relationship's createTime instead of only the count,
bucketed into ISO weeks and zero-filled across the trailing window.

Both feed the Overview dashboard's People panel leaderboard/
engagementSeries fields, previously left None as "deferred".

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Extends governed_coverage() to bucket the same hits list it already
fetches for byClassification/topZones into fullyGoverned (carries >=1
substantive governance classification: Confidentiality/Criticality/
Impact/Retention) and partialZoneOnly (carries ZoneMembership and
nothing else from the governance set) -- no extra query, just
additional aggregation over data already in hand.

Feeds the Overview dashboard's "Fully/Partial/Ungoverned breakdown",
previously a hardcoded sample split with a comment noting the shape
governed_coverage returned couldn't support it.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
load_compact_specs_from_dir() pools attribute_definitions from every
compact_commands/*.json file by name alone -- b17f71e's commit message
documents this silently corrupting Glossary/Curation/Action Author's
rendered templates when the new Lineage Linker family's original attribute
defs reused "Relationship Type"/"Guard"/"Label"/"Description" with
different definitions. It was only caught by a human diffing synced
templates afterward, not by a test.

test_compact_attribute_name_collisions.py reads every real compact JSON
file directly and compares only the fields generate_md_cmd_templates.py's
_write_attr_block() actually renders (input_required, data_type/style,
description, attr_labels, valid_values, default_value) -- ignoring
bookkeeping-only fields like "Journal Entry" that don't affect template
output. A blanket zero-collisions assertion turned out too strict: several
common attribute names (Identifier, Element Id, Effective Time, ...) are
already, legitimately duplicated with family-specific wording across many
files, predating this test. So the test instead baselines today's known
collisions and fails only on a NEW colliding name -- verified the detection
logic actually works by injecting a synthetic collision into a temp
directory and confirming it fails before adding the baseline exemption
mechanism.

Also corrects a stale pointer in c631f0c's commit message (claimed
test_overview_asof.py needed the same page_size=5000 fix; that file doesn't
exist in this repo and the grep now returns nothing) -- noted in BACKLOG.md
rather than rewriting already-pushed history.

pytest tests/micro-tests/ passes in full.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ng it

PR odpi#252 (docs/backlog-template-scan-findings, 2026-07-06) documented two
findings. Its Create Project Parent ID/Parent Relationship Type Name item
is superseded -- already fixed and documented in more depth directly on
main (2026-08-02, "Parent ID/Parent Relationship Type Name silently dropped
on Update (fixed)"). Its cross-family Link/Attach template-defect scan
(missing reference fields on Link_Term-Term_Relationship/Attach_Comment,
copy-pasted field text on a few Governance Officer/Data Designer commands)
is NOT captured anywhere else, so closing odpi#252 outright would have silently
lost it. Ported here with the fix path updated from Tinderbox (retired
2026-08-05) to the Dr.Egeria Spec Editor.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
orphan_glossary_terms: approved-but-unassigned glossary terms. One
bounded SemanticAssignment relationship fetch, filtered to the
GlossaryTerm-typed end, distinct-GUID count is the "referenced" set;
orphan = term total - referenced. Same single-bounded-fetch shape as
contextualised_coverage's ImplementedBy proxy.

stale_assets: assets with no update in N days (default 180). One
bounded Asset element fetch, each element's own version metadata
(_update_time, already used elsewhere in this module) compared
against a cutoff -- no relationship traversal. as_of anchors both the
query and the cutoff clock, matching certifications_summary's own
as_of handling.

Both feed the Overview dashboard's Attention Queue panel, previously
two of five rows with no backend computation at all.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…ions

analytic_registry.py: adds count_elements_by_property,
contextualised_coverage, karma_leaderboard, engagement_series,
orphan_glossary_terms, stale_assets to _BUILTINS -- the registry's own
docstring claimed to cover "every analytic function that already
exists," which had gone stale relative to overview_metrics.py's real
content. 23/23 parity restored against analytic_demo_specs.py, which
gets one matching demo FormatSet per new function.

Verified live: /api/analytics returns all 23 functions, /api/report-specs
returns 348 specs including all 6 new demos, and executing "Analytic
Demo - Orphan Glossary Terms" returns real data
({"termTotal": 407, "referencedCount": 9, "orphanCount": 398}).

Also logs PYEGERIA_ISSUES.md ISSUE-61: no Dr.Egeria command exists to
attach a GovernanceMetric to its implementation via the GovernanceResults
relationship, even though the OMVS wrapper
(GovernanceOfficer.link_governance_results) already exists -- found
while piloting GovernanceMetric->Report as the "describe a dashboard
metric in Egeria itself" design (see egeria-workspaces-fs's
OVERVIEW_NEXT_STEPS.md for the full pilot writeup). Purely a missing
compact-spec command + processor wiring, not an SDK gap.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
No Dr.Egeria command existed for GovernanceResults (linking a
GovernanceMetric to the data asset where its measurements are kept), even
though the OMVS wrapper (GovernanceOfficer.link_governance_results/
_async_link_governance_results, plus detach_governance_results/
_async_detach_governance_results) already existed -- purely missing
compact-spec + processor wiring, not an SDK gap.

Added via the Dr.Egeria Spec Editor's REST API (compact-spec edits):
- Two new attributes, "Governance Metric" (existing_element:
  GovernanceMetric) and "Data Asset" (existing_element: Asset), both
  Reference Name style, matching the sibling "Governance Definition"/
  "Referenceable" pair on "Link Governed By".
- New command "Link Governance Results" (OM_TYPE GovernanceResults, bundle
  "Link Command Base" -- same bundle every other simple Governance Officer
  Link command uses).

GovernanceLinkProcessor (md_processing/v2/governance.py) already handles
both Link and its Detach/Unlink/Remove variants from one spec entry via
build_command_variants -- no separate registration needed. Added a
"Governance Results" entry to its endpoint_map and one apply_changes branch
per direction, calling _async_link_governance_results/
_async_detach_governance_results with a body built via the existing generic
set_rel_prop_body() (produces class "GovernanceResultsProperties" from the
object_type name, matching the real properties class exactly -- no bespoke
body builder needed, same as Notification Subscriber/Zone Hierarchy/
Monitored Resource).

Verified: validate_compact_specs reports 0 errors (same pre-existing 26
warnings as baseline, none new); refresh_specs regenerated basic+advanced
markdown templates correctly; test_governance_processor_coverage.py
confirms the new command variants reach GovernanceLinkProcessor via the
existing generic family walk with no dispatcher changes required. Added
3 new unit tests to test_governance_link_processor.py (link, unlink,
missing-reference-guid) following the file's existing per-object-type
pattern -- all pass, along with the full pytest tests/micro-tests/ suite.

Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Dan added the "Link Governance Results" Dr.Egeria command (commit
80526c3) and cut pyegeria 6.0.18.2 with it plus this session's other
Overview dashboard work. Verified live against the pilot GovernanceMetric/
Report pair: validate/process both succeed, the relationship resolves
correctly via mgr.get_all_related_elements() after re-linking through
the new command. Moved the entry from "Open pyegeria items" to the
Appendix's "Fixed / Resolved" section per this file's own convention,
keeping its ISSUE-61 number.

Also verified 6.0.18.2 itself: installed into quickstart-pyegeria-web,
all 23 analytic_registry.py functions and 348 report specs present,
"Link Governance Results" resolves and executes correctly end to end.

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