Skip to content

Latest commit

 

History

History
174 lines (132 loc) · 9.36 KB

File metadata and controls

174 lines (132 loc) · 9.36 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

See also: AGENTS.md (deep architecture map + high-signal rules) and INSTRUCTIONS.md (contributor scope per sub-project).


Development setup

uv sync
source .venv/bin/activate

Running tests

# All tests (unit/fake, no live server required)
pytest tests/

# Single test file or test function
pytest tests/micro-tests/test_v2.py
pytest tests/micro-tests/test_v2.py::test_name

# Filter by marker (unit | integration | slow | auth | format_sets | mcp)
pytest -m unit
pytest -m integration

# Live Egeria server required
pytest tests/ --live-egeria
# or
PYEG_LIVE_EGERIA=1 pytest tests/

asyncio_mode = auto — async test functions need no event-loop boilerplate.

Tests live in three folders under tests/:

  • micro-tests/ — unit/formatter/MCP tests, no live server
  • functional-tests/ — per-OMVS tests
  • scenario-tests/ — full lifecycle (Create → Link → Delete) tests

Each OMVS module has a corresponding test file in its matching folder.

Key CLI tools (installed via uv / pip install -e .)

# Dr. Egeria — process a markdown file
dr_egeria <file>                   # default: validate
dr_egeria <file> --validate
dr_egeria <file> --process
dr_egeria <file> --process --debug         # prints every Egeria API call + body
dr_egeria <file> --process --summary-only  # suppress per-command analysis

# Regenerate report specs after adding/changing compact command JSON
refresh_specs                           # both Basic + Advanced (default)
refresh_specs --usage-level Basic       # restrict to a single usage level

# Validate compact command JSON files
validate_compact_specs

# Generate Markdown command template files (for user authoring)
gen_md_cmd_templates                # Basic attributes only
gen_md_cmd_templates --advanced     # All attributes

Commits

  • Always use git commit -s to sign off commits. This appends Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com> — DCO is enforced on this repo and unsigned commits will be rejected.
  • Do not add Co-Authored-By: lines to commit messages.

Architecture

Sub-project dependency order

pyegeria/  →  commands/  (hey_egeria CLI)
pyegeria/  →  md_processing/  (Dr. Egeria)

Changes to pyegeria/ (SDK API, config, report formatting) can break both.


pyegeria SDK

pyegeria/core/ — transport, auth, config

  • _base_platform_client.py_base_server_client.py_server_client.py: layered HTTP stack
  • config.py: Pydantic-settings config; precedence = explicit args > OS env > .env > config.json > defaults
  • utils.py: shared helpers (body_slimmer, make_format_set_name_from_type, etc.)

pyegeria/omvs/ — 40+ service-specific clients (one file per OMVS)

  • Every public method has an _async_* implementation and a sync wrapper calling asyncio.get_event_loop().run_until_complete(...).
  • All public methods decorated with @dynamic_catch.
  • Ground truth for API URLs and request bodies is in pyegeria/http clients/Egeria-api-*.http — check these files before constructing URLs.

pyegeria/egeria_tech_client.pyEgeriaTech facade

  • Uses __getattr__ to lazily proxy attribute access across all OMVS subclients; do not eagerly instantiate.
  • create_egeria_bearer_token() / set_bearer_token() propagate tokens to every subclient.

pyegeria/view/ — output formatting and report spec registry

  • base_report_formats.py: generated_format_sets dict (auto-generated by refresh_specs); do not hand-edit.
  • output_formatter.py: generate_output() — materializes elements into MD/LIST/DICT/REPORT formats.
  • _output_format_models.py: Pydantic models Column/Format/FormatSet — define new specs with these, not raw dicts.
  • Generated report specs follow {Type}-DrE-{Basic|Advanced} naming; carry an optional family string for discovery.

Dr. Egeria v2 pipeline (md_processing/)

Full pipeline for one process_md_file_v2() call:

Markdown file
  ↓ UniversalExtractor (extraction.py)
      splits on ## headers / horizontal rules → DrECommand objects
  ↓ setup_dispatcher() (dr_egeria.py)
      loads COMMAND_DEFINITIONS from compact JSON specs
      registers {command_key → ProcessorClass} in V2Dispatcher
  ↓ V2Dispatcher.dispatch_batch() (dispatcher.py)
      sequential; shared context["planned_elements"] for inter-command GUID resolution
      alias resolution → fuzzy verb-stripping → subtype fallbacks
  ↓ AsyncBaseCommandProcessor.execute() (processors.py)
      1. AttributeFirstParser.parse()
      2. derive qualified name
      3. fetch_as_is (cache → Egeria lookup)
      4. CommandRewriter: Create↔Update upsert transitions
      5. resolve reference GUIDs for all attributes
      6. validate_only() → markdown analysis table
      7. apply_changes()  ← abstract; implemented per processor
      8. render_result_markdown(guid)
  ↓ dr_egeria.py: assemble final_output, write processed-*.md

Key files:

File Role
dr_egeria.py Entry point; setup_dispatcher(); process_md_file_v2()
v2/extraction.py UniversalExtractorDrECommand dataclass
v2/dispatcher.py V2Dispatcher — routing, alias resolution, fallbacks
v2/processors.py AsyncBaseCommandProcessor — base parse/validate/apply logic
v2/rewriters.py CommandRewriter — Create↔Update auto-transitions
md_processing_utils/md_processing_constants.py COLLECTION_SUBTYPES, PROJECT_SUBTYPES, COMMAND_DEFINITIONS, verb groups
md_processing_utils/common_md_utils.py Body builders (set_element_prop_body, domain helpers)
data/compact_commands/*.json Tinderbox-exported command specs — ground truth for attributes

Compact command JSON format — three sections per file:

  • attribute_definitions — full metadata per attribute (style, labels, valid values, etc.)
  • bundles — reusable attribute groups; single inheritance via "inherits"
  • commands — references a bundle + adds custom attributes; expands to a full command spec at load time

These files are Tinderbox exports — never hand-edit. Describe desired changes so the user can make them in Tinderbox and re-export. After any change, run refresh_specs to regenerate base_report_formats.py.

Dispatcher registration — how new commands get wired in:

  • COLLECTION_SUBTYPES and PROJECT_SUBTYPES drive automatic Create/Update routing to CollectionManagerProcessor / ProjectProcessor — adding a type to these lists is all that's required.
  • Families driven entirely by compact spec (Actor Manager, Governance, Solution Architect) use a register_*_processors() helper loop in dr_egeria.py.
  • register_governance_processors() is family-name-gated, not automatic — it only registers commands whose compact-spec family is literally "Governance Officer" (or, as of the Action Author family, also "Action Author"). Adding a brand-new family whose commands should reuse GovernanceProcessor/GovernanceLinkProcessor means adding its name to that check explicitly — nothing is wired up just because the compact JSON exists and validates. Within Action Author, the four Link commands are routed by OM_TYPE, not by verb alone, across two dedicated processors in md_processing/v2/action_author.py (neither uses the generic peer-link mechanism, since both need relationship properties PeerDefinitionProperties has no room for):
    • Link First/Next Process Step (OM_TYPE GovernanceActionProcessFlow/NextGovernanceActionProcessStep) → ActionProcessStepLinkProcessor, calling action_author.setup_first/next_action_process_step with GovernanceActionProcessFlowProperties (guard/requestParameters) / NextGovernanceActionProcessStepProperties (guard/mandatoryGuard). Verified end-to-end against a live server — relationship properties persist correctly.
    • Link Action to Action Executor/Target (OM_TYPE GovernanceActionExecutor/TargetForGovernanceAction) → ActionExecutorTargetLinkProcessor, calling action_author.link_governance_action_executor/link_target_for_governance_action with GovernanceActionExecutorProperties (requestType/requestParameters/requestParameterFilter/requestParameterMap/actionTargetFilter/actionTargetMap) / TargetForGovernanceActionProperties (actionTargetName). Added 2026-07-15 — not yet verified against a live server.
  • Everything else is an explicit reg("Verb Object", ProcessorClass) call in setup_dispatcher().

Body builders — all flow through one function:

  • set_element_prop_body() in common_md_utils.py is the base inner-properties builder for all element types.
  • Every domain helper (set_collection_manager_body, set_actor_manager_prop_body, set_gov_prop_body, set_data_field_body) calls it and adds type-specific fields on top.
  • To add a new Referenceable-level property, add it once to set_element_prop_body().

Adding a new processor — minimum steps:

  1. Add the command spec to the relevant compact JSON (user does this in Tinderbox).
  2. Add the type to COLLECTION_SUBTYPES / PROJECT_SUBTYPES if it's a collection/project subtype (auto-routes).
  3. Otherwise implement apply_changes() in a new AsyncBaseCommandProcessor subclass and register it in setup_dispatcher().
  4. Run refresh_specs to generate the result-table format spec.