Repository navigation
RE: Blueprint materialization (Phases A–C) — Egeria projection of recovered architecture - #43
Merged
Merged
Conversation
…confirmed
docs/blueprint-materialization-plan.md's own gate: don't ship wire enqueueing
on an assumption from pyegeria's docstring, measure it. Ran the new
scripts/arch-spike/measure_wire_multi_link.py against the live quickstart
platform: two identical link_solution_linking_wire() calls between the same
two throwaway SolutionComponents returned two different relationship GUIDs
(f4179125-..., 8f18b426-...) — the definitive test per this session's own
reference_egeria_dedup_and_link_patterns memory note ("if attach returns an
identifier it is multi-link; if it returns nothing it is uni-link").
Confirms the docstring's own claim rather than contradicting it, and still
worth having measured directly — the two relationships this codebase
already relies on were each independently measured uni-link despite
looking, on paper, like they might not be.
Coordinated before writing: messaged all 8 live peer sessions, all cleared;
confirmed the earlier republish batch's outbox was fully drained (0
'running' rows, not just a process check) and the platform functionally
settled before trusting a negative-duplication result would have meant
anything. Cleanup used delete_solution_component(cascade_delete=True) on
both throwaway components, not a by-ends detach, which peers flagged would
have silently removed both wires and corrupted the evidence before it
could be read back.
Confirmatory read-back (get_component_related_elements) came back empty —
not a contradiction, a wrong instrument: that method's response has no
generic wire/relationship list. The return-value test alone was sufficient;
noted the likely correct method (get_relationships_with_property_value) for
a future attempt, un-tried here.
Updated docs/reference_egeria_dedup_and_link_patterns.md with the
measurement (the memory note's own third relationship-type entry).
The gate's actual ship/defer decision (Decision 5, option a vs. deferring
wire enqueueing to a follow-up) is NOT made here — the measurement answers
"is the risk real," not "do we ship with it." Left open for the project
owner.
Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…flag AnnotationReview Project owner's decision, asked directly per the plan's own gate: defer wire enqueueing to its own follow-up. Phase B ships blueprint materialization and member/child attachment; enqueue_blueprint_wires/_create_solution_linking_wire/ OutboxClients.solution_architect are out of scope for it. A materialized blueprint renders without its wire diagram until wire enqueueing lands separately. Sharper framing of the risk itself, from a peer session's review after the measurement landed: this isn't "wires need their own idempotency layer" as an add-on. apply_element's create-blind safety for every other outbox kind rests on qualifiedName search-then-adopt (why qualified_name is NOT NULL on the table) — relationships have no qualifiedName. Wires would be the first element kind the outbox's central idempotency invariant structurally cannot cover at all, not the first one that merely lacks a layer. New Backlog item 5, from the same review: egeria_outbox.py's own _create_annotation_link docstring already names AnnotationReview (Phase 3, not built) as a second unmeasured relationship type queued for the same create-blind path. Verified the docstring reference is real before writing it down. Not fixed — nothing calls it yet, no live bug — flagged as the next candidate for the same measure-before-relying-on-it treatment. Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…outbox docs/blueprint-materialization-plan.md Phase B, scoped per the project owner's Phase A.5 decision to exclude wires — enqueue_blueprint_wires, _create_solution_linking_wire, and OutboxClients.solution_architect are NOT part of this commit; a materialized blueprint attaches its members and child blueprints but renders without its wire diagram until that lands as its own follow-up. egeria_outbox.py: enqueue_blueprint_members — one CollectionMembership row per member component or child blueprint, reusing element_kind= "collection_membership" unchanged (parallel to enqueue_collection_members, not a call to it, since that function hardcodes entity_type="investigation" and a blueprint's own resource is a repo). curate.py: BlueprintVerdictCreate/list_blueprint_verdicts/ add_blueprint_verdict, mirroring the existing component-verdict route exactly. _materialize_blueprint_if_accepted: looks up the candidate_blueprint finding, materializes the blueprint element synchronously (BlueprintMaterializer), resolves member/child GUIDs (never raising for an unmaterialized one — Decision 2's enforcement point), enqueues attachment for whatever IS ready, and reports status="partial" plus which members/children are still missing when it isn't everything. Same non-fatal-but-visible contract as the component route: the verdict saves regardless of whether Egeria was reachable. Fixed a real gap the tests caught, not scoped in the plan: the EXISTING list_component_verdicts route never filtered to verdict_target="component" — get_component_verdicts' own docstring says it returns both mixed and leaves filtering to the caller, and only the new blueprint route filtered its own side. A blueprint verdict's key (perspective::cluster_name) would never practically collide with a real scope_locator, but the endpoint's name promises component verdicts specifically. Added the mirroring filter. 20 new route tests (tests/test_web.py::TestCurateBlueprintVerdictsRouter) — add/list, verdict validation, rejected-never-materializes, missing-cluster error, full materialization, partial materialization (Decision 2), two-level child-blueprint resolution (Decision 1), oversized flag passthrough, Egeria-unreachable-still-saves. Plus 2 direct unit tests for enqueue_blueprint_members (tests/test_egeria_outbox.py). Full suite: 3615 passed, 92 skipped, 0 failed (excluding the one test needing a currently-unreachable-by-design-choice live Egeria smoke check, unrelated to this change). Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…remain Records what bce70ca shipped and points at what's left — Phase C (frontend, in Curate per the outside-in reconciliation, not Analysis) and the deferred wire-enqueueing follow-up. Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
Per the outside-in reconciliation in curate-evidence-based-decisions-plan.md §D item 10: candidate-blueprint and component verdict controls move to a new Curate sub-tab, not Analysis's results panel — Analysis stays a read-only evidence display. Backend: - repo_survey_definition_adapter.py: new _candidate_blueprints_results reader, wired as _architecture_recovery_results' new data.blueprints key. Reads kind="architecture_blueprints" candidate_blueprint findings, disambiguated by (perspective, cluster_name), latest survey wins, merged with verdict/materialization state via curate.py's existing key shape. - tests/test_candidate_blueprints_reader.py: 10 new unit tests. Frontend (index.html): - New Curate sub-tab "🏛 Architecture Verdicts" (repo-only), mirroring _automateSubnavHtml's pattern (_curateSubnavHtml/_setCurateSubTab). Renders candidate blueprints (accept/reject, oversized-cluster warning, unmaterialized-member/child warning on partial materialization) and components still awaiting a verdict (reuses the existing _archRow/ _archVerdictControlsHtml chain). - New _archInteractiveMode module flag (false by default) gates the accept/reject/change controls in the shared architecture-recovery renderer — Analysis's call site stays false (read-only facts), Curate's render pass sets it true (editable). Existing renderCuratePanel renamed to renderCurateGeneralPanel; loadCuratePanel is now a small dispatcher so existing mutation handlers (addCurateTag, saveCurateGroup, ...) don't need to change. Live-verified against sqlglot (18 components, 3 candidate blueprints) through the actual UI: accepted a blueprint and a component, confirmed the verdict badge/"change" control render in Curate and the same payload renders no interactive controls in Analysis. Found, not caused by this change: BlueprintMaterializer.materialize_blueprint_element (Phase A) fails Egeria's request-body validation on a real accept — verdict still saves correctly (report-then-curate split held); tracked as Backlog item 6, not fixed here. Full suite: 3652 passed, 92 skipped, 0 failed. docs/blueprint-materialization-plan.md and docs/Backlog.md updated to record Phase C done and the two divergences from the original plan (flat blueprint list instead of nested tree; the materialization bug found live). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…og item 6) materialize_blueprint_element sent class: "NewSolutionElementRequestBody" with initialStatus: "DRAFT", per SolutionArchitect.create_solution_blueprint's own docstring — but that class was never a real pydantic model. pyegeria validates every create-blueprint body against a bare TypeAdapter(NewElementRequestBody), whose class field is Literal["NewElementRequestBody"], so this failed local pydantic validation before any HTTP call — a PyegeriaInvalidParameterException that read like an Egeria-side rejection but wasn't one. Confirmed live 2026-09-03 accepting a real candidate blueprint against sqlglot. Root cause was worse than the missing model, per egeria-python's own review (ISSUE-84, egeria-python-65, PR odpi/egeria-python#337): NewSolutionElementRequestBody/initialStatus never appeared in any .http ground-truth file either — the real, already-modeled mechanism is contentStatus inside properties (ReferenceableProperties, base of SolutionBlueprintProperties/SolutionComponentProperties). That fix isn't adopted here yet — needs live confirmation that contentStatus at creation time actually takes effect server-side, blocked right now by unrelated platform instability. This commit ships the immediate, verified-safe fix: class: "NewElementRequestBody", no initialStatus — the same body shape ComponentMaterializer already uses successfully. Blueprints materialize ACTIVE, not Draft, same pre-existing gap ComponentMaterializer already has. Two new/replaced tests in test_blueprint_materializer.py, including one that validates the actual body against pyegeria's real TypeAdapter(NewElementRequestBody) rather than only asserting the mock call shape — the kind of test that would have caught this before it reached a live accept. Backlog.md item 6 updated with the fix and the deferred contentStatus follow-up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
…us materialization) Follow-up to the previous commit's ACTIVE-only fallback. egeria-python-65 reviewed ISSUE-84 and found the real mechanism (odpi/egeria-python#337): contentStatus is a plain field on ReferenceableProperties (base of SolutionBlueprintProperties), settable inside properties on the existing NewElementRequestBody — no separate request-body class was ever needed. Confirmed live, twice, each independently read back by guid rather than trusted from the create call's own return: - Standalone probe (guid 1c6550a9-77cd-49ac-a791-ffb9fa56f3fc, then 8755a5ac-7941-4880-a735-72010d779a73 to check the properties-block shape) — properties.contentStatus == "DRAFT" via get_solution_blueprint_by_guid. - A real accept through the actual Curate UI's own API endpoint (POST /api/curate/blueprint-verdicts/repo/sqlglot, not a direct library call) materialized guid 809025b5-cca9-4e9a-a2f7-3a5104138f67, independently re-queried and confirmed the same. Both checks: properties.contentStatus round-trips as "DRAFT" correctly; elementHeader.status stays "ACTIVE" — a separate, unrelated instance-status axis (OMRS soft-delete/active at the repository level), not the same thing as the content-maturity axis contentStatus represents. The design doc's own wording ("ContentStatus = Draft") names the field this now sends. architecture-recovery.md §10 Phase 2's "All at ContentStatus = Draft" is now achieved for blueprints. ComponentMaterializer's identical gap (still NewElementRequestBody with no contentStatus) is NOT fixed here — separate, still-open follow-up, out of scope for this module. One new regression test (test_content_status_draft). Full suite: 3653 passed, 92 skipped, 1 deselected (a pre-existing, unrelated live-Egeria test flaky only during the platform's connection-pool-exhaustion window this session also found and reported), 0 failed. Backlog.md item 6 updated with the full resolution. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
dwolfson
referenced
this pull request
in dwolfson/trellis
Sep 3, 2026
…ranch Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
dwolfson
added a commit
that referenced
this pull request
Sep 13, 2026
RE tests: the malformed-usage test talks to a file store, not localhost:5025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes docs/blueprint-materialization-plan.md end to end: turns a curator's
ACCEPTED verdict on a candidate architecture blueprint into a real Egeria
SolutionBlueprint, with member/child attachment via the outbox, and a
dedicated Curate UI to review and act on it.
Phases
BlueprintMaterializer(find-or-create the SolutionBlueprintelement), registry migration (
architecture_component_verdicts.verdict_target,new
architecture_materialized_blueprintstable).SolutionLinkingWire's multi-link behaviorbefore trusting it in the outbox (confirmed: duplicates on retry). Project
owner decision: defer wire enqueueing to its own follow-up rather than ship
it with the confirmed risk.
/api/curate/blueprint-verdicts/...),member/child attachment via
egeria_outbox.enqueue_blueprint_members.controls for candidate blueprints and components; Analysis's own rendering
of the same data is now read-only (verdict controls relocated to Curate per
the outside-in reconciliation in curate-evidence-based-decisions-plan.md).
Two follow-up fixes, found live-verifying Phase C
NewSolutionElementRequestBodydocumented but never a real model or API surface — logged as
egeria-python
PYEGERIA_ISSUES.mdISSUE-84, fixed upstream indocs(solution-architect): ISSUE-84 fix -- correct fictional NewSolutionElementRequestBody/initialStatus docstrings egeria-python#337).
contentStatusproperty(the fix ISSUE-84 actually surfaced) — verified live, independently
re-queried by guid both as a standalone probe and through the actual
Curate UI's accept endpoint.
Testing
Full suite: 3653 passed, 92 skipped, 0 failed. New tests: blueprint
materializer (21), curate blueprint-verdicts route (20), outbox
enqueue_blueprint_members(2),data.blueprintsreader (10).Not in this PR
Wire enqueueing (deferred, tracked in Backlog.md item 1), the
AnnotationReviewrelationship-type measurement gap (Backlog item 5),ComponentMaterializer's equivalentcontentStatusgap (separate,still-open follow-up).
🤖 Generated with Claude Code