@@ -629,6 +629,153 @@ on an Egeria Server capability that doesn't exist yet — but the pyegeria/
629629Dr.Egeria-side work each will need once that capability ships is written
630630into 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