Orchestrata Ultimate › Plans › diagram-to-code-parity

Diagram-to-Code Parity Report

Source: plans/diagram-to-code-parity.md at 8a2cb42ad · last changed 2026-07-27 · first committed 2026-07-13 · 160 lines.

Prose pending.

Plan text

Diagram-to-Code Parity Report

Context

orchestrata_tools.diagram.diagram_to_code.DiagramToCode (src/orchestrata_tools/diagram/diagram_to_code.py) was the original Mermaid classDiagram → Python skeleton generator. A newer generator, gen-skeleton, has been built inside scripts/serialize_codebase_ast.py (lines ~3000–5300 and CLI setup at ~9429–9460) as the "build-factory linchpin" for contract-driven, @traces'd skeletons. This report compares the two feature-by-feature, notes what is still unique to each, lists the tests that would migrate if DiagramToCode were retired, and gives a recommendation.

No source files are changed by this report.


1. Summary of both tools

DiagramToCode (src/orchestrata_tools/diagram/diagram_to_code.py)

A general-purpose reverse parser. It takes a Mermaid classDiagram, builds a list of ParsedClass objects (ParsedClass dataclass at diagram_to_code.py:28), and can emit either a single Python module or a per-namespace/per-class file tree. It is the backend used by the round-trip verifier and the spec-compliance auditor (generate_comprehensive_audit.py).

Key entry points:

gen-skeleton (scripts/serialize_codebase_ast.py)

A contract-first generator tuned for the orchestrata-ultimate build factory. It reads analyst-authored Mermaid contracts (e.g. plans/contracts/SPEC-072.mmd) and emits typed Python skeletons with @traces decorators, # IMPLEMENT: leaf markers, a companion trace-map JSON, and built-in failure-detection gates.

Key entry points:


2. Feature parity table

Feature gen-skeleton (scripts/serialize_codebase_ast.py) DiagramToCode (src/orchestrata_tools/diagram/diagram_to_code.py) Notes
Parse classDiagram blocks _parse_skeleton_diagram :3376 parse :167 Both preserve declaration order.
Parse namespace … { … } blocks _SKEL_NAMESPACE_OPEN_RE :3174, namespace stored on _SkelClass :3121 parse namespace branch :183–187, ParsedClass.namespace :43 Both group output by namespace.
Class header stereotypes (class X <<stereo>> {) _SKEL_CLASS_OPEN_RE :3132, _SkelClass.stereotype :3114 parse class match :188–194, ParsedClass.stereotypes :33 DiagramToCode supports multiple comma-separated stereotypes; gen-skeleton uses the first one as the emitter key.
In-body stereotype/decorator lines (<<abstract>>) Parsed as the class stereotype if none on header :3434–3437 _parse_class_member decorator branch :223–225, ParsedClass.decorators :34 Gen-skeleton merges body <<…>> into the class stereotype; DiagramToCode keeps them as separate decorators.
Parse fields with type/default _SkelField :3067, _parse_skeleton_field_line :3305 ParsedClass.variables :35, _parse_class_member var branch :280–296 Both handle name: type = default.
Parse methods with params/return/async _SkelMethod :3085, _parse_skeleton_method_line :3284 FunctionInfo via _parse_class_member method branch :228–277 Both handle async, bracket decorators, defaults, return types.
Bracket decorators ({classmethod}, {staticmethod}, {property}, {abstractmethod}) _skeleton_method_decorator_lines :3759 _parse_class_member :244–276 and _generate_method :593–623 Gen-skeleton emits real decorators and resolves @property/@abstractmethod order; DiagramToCode stores flags in fi._extra.
Mermaid tilde generics (List~str~) _convert_skeleton_tildes :3137 _mermaid_type_to_python :94–108 Gen-skeleton handles nested tildes deterministically; DiagramToCode uses an alternating [/] walk that can mis-pair nested spans.
Relationship: inheritance `A < -- B` Parsed but not used (gen-skeleton reads `.. >` only)
Relationship: realization `A .. > B` _SKEL_REALIZATION_RE :3181, A.bases.append(B) :3418–3421 _parse_relationship implementation :370–373, B.implements.append(A) :392–393
Relationship: composition/aggregation/dependency/association/link Not parsed _parse_relationship :367–402 tracks all five; composition/aggregation/dependency become docstring markers :559–570 DiagramToCode round-trip tests exercise all seven arrows (tests/test_diagram_to_code_roundtrip.py).
Per-module / namespace output generate_skeleton_modules :4693 writes one .py per namespace generate_code(..., per_module=True) :406–417 and write_modules :677 DiagramToCode can also split per class and emit __init__.py files; gen-skeleton emits one file per namespace.
Cross-module imports Local types resolved against the full class table (_SkelEmitContext.classes :3609) _extract_type_references :143–160, _generate_file_code :625–657 generates sibling imports Gen-skeleton never fabricates an import path; DiagramToCode auto-generates from <ns>.<file> import <Name>.
__init__.py generation Not part of gen-skeleton (separate gen-facade subcommand exists at :9468) _generate_init_code :659–675, write_modules :677–735 DiagramToCode writes package __init__.py with __all__.
Enum emission _EnumEmitter :4165 → StrEnum _generate_class enum branch :573–576 → Enum Gen-skeleton lower-cases values unless an explicit = value is given; DiagramToCode always emits Name = "Name".
Dataclass emission _DataclassEmitter :4101 → @dataclass(slots=True) Detected via dataclass stereotype/decorator :464–465, @dataclass emitted :534–537 Gen-skeleton converts mutable defaults to field(default_factory=…); DiagramToCode emits them raw.
Pydantic BaseModel emission Not supported _generate_imports :466–467, _generate_class :545–548 No <<pydantic>> emitter in gen-skeleton.
Abstract base class emission (ABC) Not supported as a class-level stereotype ({abstractmethod} bracket only) _generate_imports :468–469, _generate_class :550–553 DiagramToCode can emit class X(ABC):; gen-skeleton emits ABC only via individual @abstractmethod decorators.
Protocol emission _ProtocolEmitter :4052 → typing.Protocol + @runtime_checkable Detected via implements :470–471 Both emit class X(Protocol):.
<<Tool>> stereotype / command boilerplate _ToolEmitter :4327, full execute wrapper with ToolResult, tracer span, try/except :3901–3983 Not supported Gen-skeleton's signature feature.
Constructor DI generation _emit_composed_init_lines :4185 Not supported Gen-skeleton wires private fields to self._x and public fields to self.x; handles _tracer default.
Accessor/factory/leaf patternRole dispatch _skeleton_method_role :3728, _emit_accessor_method_lines :3837, _emit_leaf_method_lines :3792 Not supported Gen-skeleton generates return self._field for accessors and marked # IMPLEMENT: stubs for leaves.
Self-returning factory heuristic _skeleton_is_self_returning_factory_name :3350 Not supported Bare create/default/from_* returning the class type become @classmethod in gen-skeleton.
Base read-only property collision handling _introspect_base_readonly_property :3659, _skeleton_field_is_readonly_property :3687 Not supported Gen-skeleton introspects real bases (e.g. BaseTool.metadata) to avoid AttributeError.
@traces emission _traces_call :3708, class/method pragma parsing :3175–3176 Not supported Gen-skeleton's core requirement-traceability feature.
Trace-map JSON _build_skeleton_trace_map :4453 Not supported Maps REQ-* → {file, class, method}.
Leaf-map JSON + # IMPLEMENT: markers _build_skeleton_leafmap :4495, _emit_leaf_method_lines :3826–3833 Not supported Used by skeleton-diff Gate 3.
Failure-detection gates Gate 0 gate0_construction_smoke :4890, Gate 2 gate2_leaf_fraction_table :4964, Gate 3 skeleton_diff :5157, self-verify _gen_skeleton_self_verify_traces :5027 Not supported Gen-skeleton runs gates automatically in the CLI (run_gen_skeleton_gates :5069).
%% extend <path> merge mode _SKEL_EXTEND_RE :3178, _SkelClass.mode :3125 Not supported Allows a contract to declare that a class extends an existing implementation.
%% REUSE … cross-package import resolution _parse_skeleton_reuse_imports :3539, _build_skeleton_known_types :3573 Not supported Gen-skeleton resolves declared external types to real imports; unresolved types are flagged, never guessed.
PEP 695 type parameters (class X~T~) _skel_generic_suffix :3589, _SkelClass.type_params :3124 Not supported Gen-skeleton emits class X[T]:.
Partial success / no-stereotype fallback _select_skeleton_emitter :4445, warning/fallback at :4555–4587 All-or-nothing for a given input Gen-skeleton skips one bad stereotype and falls back a bare class to <<class>>; DiagramToCode raises if it cannot handle a decorator in write_modules.
include_imports=False toggle Not available generate_code(..., include_imports=False) :406, :481 DiagramToCode can emit code without imports.
import asyncio for async methods Not emitted _generate_imports :473–474 DiagramToCode adds import asyncio when any method is async.

3. Features still ONLY in DiagramToCode

These capabilities have no equivalent in gen-skeleton today:

  1. Pydantic BaseModel emission (diagram_to_code.py:345–353, :545–548). A <<pydantic>> stereotype or decorator produces from pydantic import BaseModel and class X(BaseModel):.
  2. Abstract base class (ABC) emission (diagram_to_code.py:355–357, :550–553). A class-level <<abstract>> stereotype/decorator produces from abc import ABC, abstractmethod and class X(ABC):.
  3. Relationship docstrings (diagram_to_code.py:559–570). Composition, aggregation, and dependency arrows are rendered into the class docstring as Composes: …, Aggregates: …, Depends on: ….
  4. Full relationship tracking (diagram_to_code.py:367–402). All seven Mermaid arrows (inheritance, realization, composition, aggregation, dependency, association, link) are parsed and stored; the round-trip test suite (tests/test_diagram_to_code_roundtrip.py) depends on this.
  5. Per-class file output and __init__.py generation (diagram_to_code.py:625–735). write_modules splits enums into types.py, non-enums into snake_case.py, and writes package __init__.py files with __all__.
  6. Auto-generated cross-module imports (diagram_to_code.py:143–160, :625–657). Type references to sibling modules are turned into real from <pkg>.<module> import <Name> lines.
  7. include_imports=False option (diagram_to_code.py:406, :481). Useful for embedding generated code into a larger file that already has imports.
  8. import asyncio generation (diagram_to_code.py:473–474). Any async method causes an import asyncio line.
  9. Return-type arrow-prefix stripping (diagram_to_code.py:239–242). Handles Mermaid like +run() ->int.
  10. Parameter kind metadata (positional/varargs/kwargs) (diagram_to_code.py:348–355). DiagramToCode explicitly classifies *args / **kwargs.

4. Features only in gen-skeleton

These capabilities are unique to the build-factory generator:

  1. @traces decorator emission (scripts/serialize_codebase_ast.py:3708, :4052, :4101, etc.) driven by %% traces spec=… story=… reqs=… pragmas (:3175–3176).
  2. Method-level trace overrides (scripts/serialize_codebase_ast.py:3176, :3328–3332).
  3. Trace-map JSON (scripts/serialize_codebase_ast.py:4453).
  4. Leaf-map JSON and # IMPLEMENT: stubs (scripts/serialize_codebase_ast.py:4495, :3826–3833).
  5. Failure-detection gates:
    • Gate 0 construction smoke (:4890),
    • Gate 2 leaf-fraction table (:4964),
    • Gate 3 skeleton-diff (:5157),
    • check-traces self-verify (:5027).
  6. <<Tool>> stereotype with full command boilerplate (scripts/serialize_codebase_ast.py:4327, :3901–3983) — execute gets arg validation, tracer span, try/except, ToolResult wrapping, and delegation to a matching leaf.
  7. Constructor DI synthesis (scripts/serialize_codebase_ast.py:4185) with auto _tracer injection for Tools (:4344).
  8. patternRole dispatch (scripts/serialize_codebase_ast.py:3728) — command, accessor, factory, leaf.
  9. <<class>> plain composed service emitter (scripts/serialize_codebase_ast.py:4386).
  10. Self-returning factory heuristic (scripts/serialize_codebase_ast.py:3350) — bare create/default/from_* returning the class type become @classmethod.
  11. Base read-only property introspection (scripts/serialize_codebase_ast.py:3659, :3687) to avoid collisions with real base descriptors.
  12. %% REUSE … import resolution (scripts/serialize_codebase_ast.py:3539, :3573) plus honest "unresolved type" notes.
  13. %% extend <path> merge mode (scripts/serialize_codebase_ast.py:3178, :3125).
  14. PEP 695 type parameters (scripts/serialize_codebase_ast.py:3589, :3124).
  15. Multi-module namespace routing to real files (scripts/serialize_codebase_ast.py:4693, CLI handler :10195–10246).
  16. Partial success / no-stereotype fallback (scripts/serialize_codebase_ast.py:4555–4587).
  17. Nested-tilde generic conversion (scripts/serialize_codebase_ast.py:3137).

5. Tests that would migrate if DiagramToCode were retired

The existing test files that directly exercise DiagramToCode are:

If DiagramToCode were retired, the tests would split as follows:

Current test What it covers Would migrate to / become
test_orchestrata_tools_diagram_to_code.py — parser tests for stereotypes, fields, methods, namespaces, decorators, params, return types Shared Mermaid parsing behaviour tests/scripts/test_ast_gen_skeleton.py (or a new test_ast_gen_skeleton_parsing.py)
test_orchestrata_tools_diagram_to_code.py — relationship parsing tests The seven Mermaid arrows A new gen-skeleton relationship suite, but only if gen-skeleton learns to track composition/aggregation/dependency/association/link
test_orchestrata_tools_diagram_to_code.py — generate_code() output tests (enum, dataclass, pydantic, abstract, Protocol, typing imports) Code emission for stereotypes tests/scripts/test_ast_gen_skeleton.py for enum/dataclass/Protocol; pydantic/ABC tests would need new gen-skeleton emitters or be deleted
test_orchestrata_tools_diagram_to_code.py — per_module=True and write_modules tests Namespace/multi-file output Extend tests/scripts/test_ast_gen_skeleton.py namespace tests and/or add gen-facade integration tests
test_diagram_to_code_roundtrip.py Forward generator round-trip for inheritance only; model tracking for all seven arrows Retire the round-trip assertions that have no gen-skeleton equivalent, or keep DiagramToCode as the relationship parser

tests/scripts/test_ast_gen_skeleton.py and tests/scripts/test_ast_skeleton_gates.py already cover the gen-skeleton side and would not need to migrate.


6. Recommendation

Do not retire or deprecate DiagramToCode. The two tools serve different purposes and each has capabilities the other lacks:

Recommended actions:

  1. Keep both tools. Treat gen-skeleton as the preferred generator for new build-factory contracts, and DiagramToCode as the supported general-purpose parser/generator.
  2. Do not delete any tests. The DiagramToCode test files cover behaviour (especially relationships and pydantic/ABC emission) that gen-skeleton does not yet implement.
  3. If future unification is desired, port the truly unique DiagramToCode features into gen-skeleton as new emitters/options rather than removing them:
    • Add <<pydantic>> and <<abstract>> class-level emitters.
    • Add relationship tracking for composition/aggregation/dependency/association/link.
    • Add an optional per-class file writer with __init__.py generation.
    • Reconcile the realization-arrow direction convention (A ..|> B) so both tools can agree on a single interpretation.
  4. Add cross-references in docstrings so future maintainers know which generator to use for which workflow.

Until the unique DiagramToCode features are duplicated in gen-skeleton, retiring it would break the round-trip tests, the audit pipeline, and any workflow that relies on pydantic/ABC classes or per-class file output.