Skip to content

Commit e14b93d

Browse files
committed
docs(solution-architect): ISSUE-84 fix -- correct fictional NewSolutionElementRequestBody/initialStatus docstrings
create_solution_blueprint/create_solution_component (async+sync, 4 docstrings total) documented a NewSolutionElementRequestBody body class with an initialStatus field as the way to set Draft status at creation. That class and field appear in zero .http ground-truth files anywhere in this repo -- never a real Egeria API surface, not merely unmodeled -- so following the documented shape either hard-failed client-side validation (create_solution_blueprint, class literal mismatch) or silently dropped the field and created the element ACTIVE regardless of intent (create_solution_component, NewElementRequestBody's extra='ignore'). Also found and fixed while walking these: create_solution_blueprint's first example body carried leftover userDefinedStatus/lifecycleStatus fields with a missing comma, invalid JSON even as a copy-paste example. Fix: all four docstrings now document contentStatus inside properties instead -- confirmed against Egeria-api-solution-architect.http's own updateSolutionBlueprintStatus example, and content_status is already a real field on ReferenceableProperties, the base class both SolutionBlueprintProperties and SolutionComponentProperties derive from. No model change needed; NewElementRequestBody.properties is a bare dict so contentStatus already passes through untouched. Verified locally: the old shape still fails validation as expected, the corrected shape validates cleanly with contentStatus preserved through to serialized JSON. Not live-verified against a real Egeria server (no access from this environment) -- reported by dwolfson-1b/trellis via ISSUE-84, who has live access to confirm contentStatus=DRAFT actually takes effect server-side at creation time. Full detail in PYEGERIA_ISSUES.md ISSUE-84. Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
1 parent 87bd63b commit e14b93d

2 files changed

Lines changed: 238 additions & 34 deletions

File tree

‎PYEGERIA_ISSUES.md‎

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,153 @@ on an Egeria Server capability that doesn't exist yet — but the pyegeria/
629629
Dr.Egeria-side work each will need once that capability ships is written
630630
into the entry now, so it isn't rediscovered from scratch later.
631631

632+
### ISSUE-84: `SolutionArchitect.create_solution_blueprint`'s own docstring documents a `NewSolutionElementRequestBody` body (with `initialStatus`) for Draft-status creation — that class does not exist as a pydantic model, so the documented shape fails client-side validation before any HTTP call
633+
634+
**Status:** fixed 2026-09-03 — docstring-only fix (see "Fix landed" below).
635+
Reported by a consumer (trellis/Resource Explorer's `BlueprintMaterializer`,
636+
`docs/blueprint-materialization-plan.md` Phase A).
637+
638+
**Confirmed in both**: this checkout's installed pyegeria (5.3.4.23, via
639+
the trellis venv) and this repo's own working tree (6.1.9-dev) —
640+
`grep -rn "class NewSolutionElementRequestBody" pyegeria/models/*.py`
641+
returns nothing in either; the class is documented in
642+
`SolutionArchitect.create_solution_blueprint`'s (and its `_async_*`
643+
sibling's) docstring — twice, once per method, `omvs/solution_architect.py`
644+
lines ~2325 and ~2440 in this tree — as the way to set `initialStatus`
645+
(DRAFT/PREPARED/PROPOSED/APPROVED/REJECTED/ACTIVE/DISABLED/DEPRECATED/OTHER)
646+
on a newly-created blueprint, distinct from `NewElementRequestBody` which
647+
"sets the status to ACTIVE." No such model backs that documented shape.
648+
649+
**What actually happens**: `create_solution_blueprint`'s real validation
650+
path (`ServerClient._async_create_element_body_request` →
651+
`validate_new_element_request` → `self._validate_body(self.
652+
_new_element_request_adapter.validate_python, body)`) uses a bare
653+
`TypeAdapter(NewElementRequestBody)` (set in `ServerClient.__init__`) —
654+
`NewElementRequestBody.class_` is `Annotated[Literal["NewElementRequestBody"],
655+
Field(alias="class")]`, a strict literal. Any other `class` value —
656+
including the documented `"NewSolutionElementRequestBody"` — fails pydantic
657+
validation locally, before any network call, raising
658+
`PyegeriaInvalidParameterException` with `additional_info={"reason":
659+
"Request body failed validation", "validation_errors": [...]}`. The
660+
resulting exception message reads exactly like a server-side rejection
661+
("Egeria rejected the new SolutionBlueprint: ...VALIDATION_ERROR_1...
662+
Invalid parameters were provided...") and cost real debugging time on the
663+
consumer side tracing it back to "not actually an Egeria call at all."
664+
665+
Reproduced directly:
666+
```python
667+
from pyegeria.models import NewElementRequestBody
668+
from pydantic import TypeAdapter
669+
TypeAdapter(NewElementRequestBody).validate_python({
670+
"class": "NewSolutionElementRequestBody", "isOwnAnchor": True,
671+
"initialStatus": "DRAFT",
672+
"properties": {"class": "SolutionBlueprintProperties",
673+
"qualifiedName": "test", "displayName": "test"},
674+
})
675+
# ValidationError: Input should be 'NewElementRequestBody'
676+
# [type=literal_error, input_value='NewSolutionElementRequestBody', ...]
677+
```
678+
679+
**Scope beyond `create_solution_blueprint`**: `create_solution_component` (and
680+
its `_async_*` sibling) carries the identical docstring text and the
681+
identical gap — confirmed by walking each `NewSolutionElementRequestBody`
682+
docstring reference in `omvs/solution_architect.py` (lines 2254/2372/3542/3629
683+
in this tree) back to its enclosing method: `_async_create_solution_blueprint`/
684+
`create_solution_blueprint` (the two checked above) and
685+
`_async_create_solution_component`/`create_solution_component`. Both public
686+
create-element methods in this file document the same unbacked Draft-status
687+
path. Not checked beyond `solution_architect.py`: worth a full grep across
688+
`omvs/*.py` for any other method documenting `NewSolutionElementRequestBody`,
689+
`NewGovernanceElementRequestBody`, or any other `New*ElementRequestBody`
690+
variant that isn't `NewElementRequestBody` itself, since the same
691+
"documented in the docstring, no model behind it" shape could recur
692+
per-family.
693+
694+
**Candidate fix** (not applied — for whoever picks this up): either (a)
695+
define a real `NewSolutionElementRequestBody` pydantic model (subclassing
696+
or extending `NewElementRequestBody` with an `initial_status` field and its
697+
own `class_: Literal["NewSolutionElementRequestBody"]`) and register it
698+
alongside `NewElementRequestBody` wherever `_new_element_request_adapter`
699+
is built (likely needs a `Union`/discriminated-union `TypeAdapter`, not a
700+
single-model one, so both class names validate), or (b) if
701+
`initialStatus` is actually accepted by `NewElementRequestBody` itself on
702+
the Egeria server side despite not being a declared pydantic field, add
703+
`initial_status: str | None = None` directly to `NewElementRequestBody`
704+
and drop the docstring's separate-class framing — cheaper, but needs
705+
confirming server-side that `NewElementRequestBody` + `initialStatus` is
706+
accepted (not verified here; the consumer's own fallback, tracked in their
707+
own Backlog, is to use `NewElementRequestBody` with no `initialStatus` at
708+
all, which creates the element ACTIVE, not Draft, until this is resolved).
709+
710+
**Consumer-side workaround, now supersedable**: `BlueprintMaterializer.
711+
materialize_blueprint_element` (trellis/resource_explorer, `surveyors/
712+
arch_recovery/blueprint_materializer.py`) sends `class: "NewElementRequestBody"`
713+
with no status field at all, same as their pre-existing `ComponentMaterializer`
714+
— blueprints materialize ACTIVE, not Draft. With this fix landed, that
715+
workaround can be upgraded to set `contentStatus: "DRAFT"` inside
716+
`properties` on the same `NewElementRequestBody` to get the originally
717+
intended Draft-status behavior — not applied here since this is a
718+
consumer-side change, tracked for `dwolfson-1b`/trellis to pick up.
719+
720+
**Fix landed 2026-09-03 — docstring correction, no model change needed.**
721+
Checked ground truth first: `NewSolutionElementRequestBody` and
722+
`initialStatus` appear in **zero** `.http` files anywhere in this repo
723+
(`grep -rln "NewSolutionElementRequestBody\|initialStatus" "pyegeria/http
724+
clients/"` returns nothing) — the documented shape was never a real Egeria
725+
API surface, not merely an unmodeled one, which rules out candidate fix (a)
726+
(defining a real model) and (b) (adding `initial_status` to
727+
`NewElementRequestBody`) from the original write-up. The actual, real
728+
mechanism for setting status on these elements already exists:
729+
`Egeria-api-solution-architect.http`'s own `updateSolutionBlueprintStatus`
730+
example sets a blueprint's status via `"contentStatus": "ACTIVE"` inside an
731+
`AuthoredReferenceableProperties`-shaped `properties` object — and
732+
`content_status: str | None = None` is already a real field on
733+
`ReferenceableProperties` (`pyegeria/models/models.py`), the base class
734+
both `SolutionBlueprintProperties` and (transitively)
735+
`SolutionComponentProperties` derive from. Since `NewElementRequestBody.
736+
properties` is a bare `dict` (no nested pydantic validation), passing
737+
`contentStatus` inside it at *creation* time was always possible — the
738+
docstrings just never said so, and instead pointed at a body shape that
739+
doesn't exist anywhere.
740+
741+
Rewrote all four docstrings (`_async_create_solution_blueprint`/
742+
`create_solution_blueprint`, `_async_create_solution_component`/
743+
`create_solution_component`, `omvs/solution_architect.py`) to drop every
744+
reference to `NewSolutionElementRequestBody`/`initialStatus` and document
745+
`contentStatus` inside `properties` instead, with a `NewElementRequestBody`
746+
example that now sets `"contentStatus": "DRAFT"`. Two extra defects found
747+
and fixed while doing this walk, both worse than the one originally
748+
reported:
749+
- `_async_create_solution_blueprint`'s *first* ("no lifecycle") example
750+
body already carried stray `"userDefinedStatus"`/`"lifecycleStatus"`
751+
fields left over from the fictional shape — with a **missing comma**
752+
after `"lifecycleStatus": "DRAFT"`, making it invalid JSON even as a
753+
copy-paste example, independent of the pydantic-validation bug.
754+
- `_async_create_solution_component`/`create_solution_component`'s only
755+
example already used the real `"class": "NewElementRequestBody"` (so it
756+
passed validation) but paired it with the fictional top-level
757+
`"initialStatus"` field anyway. This is a *different, silent* failure
758+
mode from the reported one: `NewElementRequestBody` inherits
759+
`PyegeriaModel`'s `extra='ignore'`, so `initialStatus` validated
760+
successfully and was silently dropped before serialization — no
761+
exception at all, just an element that always came out ACTIVE regardless
762+
of the caller's intent to set DRAFT. Exactly the class of bug flagged
763+
generally in this file's own `pyegeria/core/` gotcha note about
764+
request-body models silently dropping undeclared fields.
765+
766+
Verified locally (no live server access from this environment): the
767+
original repro's `TypeAdapter(NewElementRequestBody).validate_python(...)`
768+
call with the old `NewSolutionElementRequestBody`/`initialStatus` shape
769+
still fails as expected (proving the understanding of the original bug is
770+
correct), and the corrected shape
771+
(`{"class": "NewElementRequestBody", "properties": {...,
772+
"contentStatus": "DRAFT"}}`) validates cleanly with `contentStatus`
773+
preserved intact through to the serialized JSON. **Not live-verified
774+
against a real Egeria server** — `dwolfson-1b`/trellis has live access;
775+
recommend they confirm `contentStatus: "DRAFT"` at creation actually lands
776+
as the blueprint/component's status server-side before switching
777+
`BlueprintMaterializer` off its ACTIVE-only workaround.
778+
632779
### ISSUE-82: `pyegeria/omvs/valid_metadata.py` sends the literal query string `typeName=None` whenever `type_name` is Python `None` — breaks every Type-Name-omitted (global) Valid Metadata Value, in 12 of 14 methods across `ValidMetadataManager`
633780

634781
**Status:** fixed and live-verified 2026-08-28 — all 12 affected methods

0 commit comments

Comments
 (0)