Cycle detected: Fix for Circular dependency report by ArchUnit (again)#131
Cycle detected: Fix for Circular dependency report by ArchUnit (again)#131ichxorya wants to merge 90 commits into
Conversation
- Note: Run `mvn package` and you will see these in the result. This will be updated as we fix, then deleted in the merge to the main branch.
|
2026/05/06 example; see more hier
|
This commit resolves a circular dependency (Bug 4) where the root org.tzi.use.api package depended on org.tzi.use.api.impl through factory methods in UseSystemApi.java, while the impl package naturally depended on the root package. Changes: * Extracted the create() factory methods from UseSystemApi into a new UseSystemApiFactory class located in the api.impl package. * Removed all imports of api.impl classes from UseSystemApi, severing the root -> impl dependency edge. * Added 'exports org.tzi.use.api.impl' to module-info.java to allow downstream modules (like use-gui) to access the factory. * Updated all 26 call sites across 10 test and integration files to use UseSystemApiFactory.create() instead of UseSystemApi.create(). * Archived the old ArchUnit failure report for historical comparison and updated the PR notes with inline before/after Mermaid diagrams. This ensures a clean, unidirectional dependency graph: api.impl -> api
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR aims to address recurring ArchUnit-reported circular dependencies by refactoring API construction (breaking the api ↔ api.impl cycle), expanding cycle-report generation in ArchUnit tests, and adding documentation tooling to visualize cycles.
Changes:
- Introduced
UseSystemApiFactoryinorg.tzi.use.api.impland migrated call sites away fromUseSystemApi.create(...)to break theapi↔api.implcycle. - Enhanced ArchUnit cycle tests to emit per-area failure reports and results into
target/archunit-*directories; added additional GUI subpackage cycle checks. - Added Mermaid-generation tooling and a PR-notes README that embeds cycle diagrams; committed ArchUnit output snapshots under
target_archunit_temp.
Reviewed changes
Copilot reviewed 33 out of 38 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| use-gui/target_archunit_temp/archunit-results/ant_cyclic_dependencies_xmlparser_results.csv | Adds a committed snapshot of ArchUnit “ant cyclic dependencies” result output. |
| use-gui/target_archunit_temp/archunit-results/ant_cyclic_dependencies_views_results.csv | Adds a committed snapshot of ArchUnit “ant cyclic dependencies” result output. |
| use-gui/target_archunit_temp/archunit-results/ant_cyclic_dependencies_util_gui_results.csv | Adds a committed snapshot of ArchUnit “ant cyclic dependencies” result output. |
| use-gui/target_archunit_temp/archunit-results/ant_cyclic_dependencies_main_gui_results.csv | Adds a committed snapshot of ArchUnit “ant cyclic dependencies” result output. |
| use-gui/target_archunit_temp/archunit-results/ant_cyclic_dependencies_graphlayout_results.csv | Adds a committed snapshot of ArchUnit “ant cyclic dependencies” result output. |
| use-gui/target_archunit_temp/archunit-reports/failure_report_maven_layers.txt | Adds a committed snapshot of a layers violation report used for documentation/visualization. |
| use-gui/target_archunit_temp/archunit-reports/failure_report_maven_cycles_shell.txt | Adds a committed snapshot of a cycle report to support diagnosis/visualization. |
| use-gui/target_archunit_temp/archunit-reports/failure_report_maven_cycles_gui_views.txt | Adds a committed snapshot of GUI views cycle report to support diagnosis/visualization. |
| use-gui/target_archunit_temp/archunit-reports/failure_report_maven_cycles_gui_main.txt | Adds a committed snapshot of GUI main cycle report to support diagnosis/visualization. |
| use-gui/target_archunit_temp/archunit-reports/failure_report_maven_cycles_gui.txt | Adds a committed snapshot of the broader GUI cycle report to support diagnosis/visualization. |
| use-gui/src/test/java/org/tzi/use/gui/views/diagrams/util/CreationTimeRecorderTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-gui/src/test/java/org/tzi/use/architecture/MavenCyclicDependenciesGUITest.java | Improves ArchUnit cycle counting tests to write detailed failure reports and results under target/. |
| use-gui/src/test/java/org/tzi/use/architecture/AntCyclicDependenciesGUITest.java | Redirects ArchUnit “ant” result output into target/archunit-results. |
| use-core/target_archunit_temp/archunit-reports/failure_report_maven_cycles_uml.txt | Adds a committed snapshot of core UML cycle report used for tracking/visualization. |
| use-core/target_archunit_temp/archunit-reports/failure_report_maven_cycles_parser.txt | Adds a committed snapshot of parser cycle report used for tracking/visualization. |
| use-core/target_archunit_temp/archunit-reports/failure_report_maven_cycles_gen.txt | Adds a committed snapshot of generator cycle report used for tracking/visualization. |
| use-core/src/test/java/org/tzi/use/utilcore/AbstractBagTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/sys/soil/StatementEffectTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/sys/ObjectCreation.java | Updates helper test code to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/sys/MSystemStateTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/sys/MCmdDestroyObjectsTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/sys/LinkTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/sys/DeletionTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/test/java/org/tzi/use/uml/mm/MAssociationClassTest.java | Updates tests to create UseSystemApi via the new factory. |
| use-core/src/main/java/org/tzi/use/api/impl/UseSystemApiFactory.java | Introduces a factory in api.impl to instantiate implementations without coupling api back to api.impl. |
| use-core/src/main/java/org/tzi/use/api/UseSystemApi.java | Removes static factory methods that created a circular api ↔ api.impl dependency. |
| use-core/src/main/java/module-info.java | Exports org.tzi.use.api.impl so UseSystemApiFactory is accessible to external modules/tests. |
| use-core/src/it/java/org/tzi/use/OCLExpressionIT.java | Updates integration test to create UseSystemApi via the new factory. |
| use-core/pom.xml | Updates dependency coordinates/versions and build properties for Java 21. |
| pom.xml | Adds centralized compiler plugin management and Java release property. |
| docs/scripts/generate_cycle_mermaid.py | Adds tooling to rewrite README marker blocks with Mermaid diagrams from ArchUnit reports. |
| docs/archunit-history/before-fix/bug-4_failure_report_maven_cycles_api.txt | Archives the pre-fix cycle report for the api ↔ api.impl issue. |
| README_nghiabt_notes_on_this_pr/nghiabt_notes_on_this_pr.md | Adds a dedicated PR-notes README tracking cycles with embedded Mermaid diagrams. |
| .vscode/settings.json | Adds VS Code Java settings for null analysis and build configuration updates. |
Comments suppressed due to low confidence (1)
use-gui/src/test/java/org/tzi/use/architecture/MavenCyclicDependenciesGUITest.java:158
- The test ensures
target/archunit-reportsexists (inwriteFailureReport), but does not ensuretarget/archunit-resultsexists before openingFileWriter(filename, true). This can fail on a clean checkout/CI. CreateRESULTS_DIR(e.g., insetup()or insidewriteResult) similarly to howREPORTS_DIRis handled.
private void writeResult(int result, String filename) {
try (PrintWriter out = new PrintWriter(new FileWriter(filename, true))) {
out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 38 out of 43 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
use-gui/src/test/java/org/tzi/use/architecture/MavenCyclicDependenciesGUITest.java:153
FileWriteris opened undertarget/archunit-results, but this class never creates that directory. On a clean checkout the write fails withFileNotFoundException; createRESULTS_DIRbefore opening result files, similar to the report directory handling.
try (PrintWriter out = new PrintWriter(new FileWriter(filename, true))) {
…to major version bump if this is merged
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 51 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
use-gui/src/test/java/org/tzi/use/architecture/MavenCyclicDependenciesGUITest.java:153
RESULTS_DIRis never created in this class before opening the result file, so these tests can fail on a clean checkout wheretarget/archunit-resultsdoes not already exist. Create the parent directory insetup()or insidewriteResult()before constructing theFileWriter.
use-gui/src/test/java/org/tzi/use/architecture/MavenCyclicDependenciesGUITest.java:132- When a package's cycle count drops to zero, this returns without deleting any existing
failure_report_maven_cycles_<shortName>.txt. A stale report from a previous run will remain undertarget/archunit-reportsand can make the README generator continue showing cycles that were fixed; delete the report file (or write an empty/no-cycles report) before returning.
- Removed unnecessary imports from various classes in the `use-core` and `use-gui` modules to clean up the codebase and improve readability. - Adjusted module dependencies in `module-info.java` to use `transitive` where appropriate for better encapsulation and access control. - Cleaned up import statements in GUI-related classes to enhance maintainability and reduce clutter.
|
Hi @ichxorya! |
Thanks for your response! This PR was initially done as AIs assisted me (mostly Claude Opus 4.7) with my work. However in later phases (from the point I forgot documenting again), I let the AI to autonomously taking the job and plan to review it later. Sorry if any troubles happen when you do peer review @h-man2 . I should have mark this PR as draft before I'm ready with my review. You can tell me if you stop reviewing for now since I will have to take my responsible for my messy, gigantic PR with lots of breaking changes. Thank you. Edit: as for changes relevant that are non-showing, I will keep that in mind and see if the problem is just end-of-lines added or not. I have to check and document all these changes in order to keep the PR coherent with the repository. |
|
No worries. I was just starting to review it. |
…assert 0 cycles Pin maven-surefire-plugin 3.5.2 (parent) and add junit-vintage-engine to both modules so the JUnit 5 ArchUnit tests (MavenCyclicDependenciesCoreTest, MavenLayeredArchitectureTest) actually execute instead of being silently skipped, while the existing JUnit 4 suite keeps running. Add assertEquals(0,..) to all 6 arch tests so a cycle/layer regression now fails the build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Independently re-measured pass/fail dashboard, before->after cycle counts, the Surefire test-harness fix, and an honest note on the unmeasured gui.views.diagrams cycle cluster. Delete before merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Multi-pass per-domain review of the 777-file diff (correctness + behavior- preservation + completeness), every candidate adversarially verified against the code: 27 candidates -> 14 low-severity findings (0 critical/high/medium), 13 refuted. Visualized change-map + per-domain verdicts + reproducible evidence. Delete before merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tive error getSelf() is statically an MInstance, whose state() returns the broad MInstanceState (impls: IObjectState/MObjectState + MDataTypeValueState). Three sites cast that result straight to MObjectState to read protocol state machines: MSystem.calculatePossibleTransition / doTransition and OpEnterOpExitPPCHandler.handleTransitionsPre. These are unreachable today (only objects enter non-query operations), but the casts are unguarded. Replace each with an instanceof pattern guard that throws a descriptive IllegalStateException instead of a bare ClassCastException. The method is deliberately NOT hoisted back onto MInstance/MInstanceState: that would re-introduce a uml.mm -> uml.sys dependency edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…notes
Ship a consolidated, verified MIGRATING.md (package moves, SPI signature
changes, removed methods, new types, module-info, visibility, plus a sed
cheat-sheet) for the 7.5.0 decycle API reshape.
Fix two inaccuracies in the breaking-change catalog, both verified against the
tree:
- IMainWindow/IModelBrowser live in gui.main.runtime, not gui.main (the
"New SPI types" list dropped the .runtime segment).
- "impls moved out of runtime" overclaimed: the GUI/shell impls moved out, but
the core plugin runtime (Plugin/PluginRuntime, Plugin*Model, registries and
descriptors) remains under runtime.{impl,model,util}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…age cycle views/diagrams/util/GUI.java is an orphan Swing "DrawTest" demo (a main() with no production callers) whose only diagram dependency is DirectedEdgeFactory in the edges package. That single import was the sole util->edges back-edge, which together with the existing edges->util dependency formed an edges<->util package cycle. Move the demo into the edges package (next to the only type it draws). util no longer depends on edges, so both slices leave the cyclic set: measured via the slice-SCC metric, gui.views.diagrams drops from 11 to 9 cyclic sub-slices and 55 to 42 inter-slice feedback edges. No new inter-package dependency is added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No gated arch test roots a slice at org.tzi.use.gui.views.diagrams (the deepest GUI root is gui.views), so its large pre-existing framework cycle web is invisible to CI. Add a non-asserting measurement that reports two STABLE metrics computed via Tarjan SCC over ArchUnit's dependency graph: the number of first-level sub-slices in a non-trivial SCC, and the inter-slice feedback-edge count. These replace the raw cycle count, which is useless here: it is combinatorial AND ArchUnit caps detection at cycles.maxNumberToDetect (=600 in this project), so the oft-cited "~600 cycles" is merely that saturated cap, not a real count. The cyclic-slice/feedback-edge metrics move monotonically as back-edges are removed (currently 9 slices / 42 edges) and let later decycling phases show progress. Kept non-gating until the framework subtree is actually untangled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the pure diagram-foundation contracts out of the overloaded gui.views.diagrams root package into a new gui.views.diagrams.framework slice: DiagramOptions, DiagramOptionChangedListener, PositionChangedListener, ToolTipProvider, DiagramType, PrintableView, HighlightChangeEvent, HighlightChangeListener, ObjectNodeActivity, SelectionBox. These have no outgoing diagram-slice dependencies, so the new slice is a pure sink that elements/types/waypoints can depend on without a cycle. Groundwork for decycling gui.views.diagrams: by itself this removes the waypoints->root back-edge (waypoints now reaches only framework); the bulk of the SCC (DiagramView, MainWindow, elements<->waypoints) is broken in following commits. NOTE: DiagramOptions is a plugin SPI type, so this is an SPI package move (MIGRATING.md + use_plugins updated alongside the final commit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The waypoints package and elements were mutually dependent (elements use WayPoint/AttachedWayPoint/...; waypoints extend EdgeBase/PlaceableNode). waypoints depends only on elements, framework and util, so fold it in as the elements.waypoints sub-package. The waypoints slice disappears and the elements<->waypoints coupling becomes intra-slice. gui.views.diagrams cyclic sub-slices: 9 -> 8 (feedback edges 41 -> 36). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
elements (the most-depended-on diagram slice) had only two outgoing edges that kept it in the cycle: elements->root (it called DiagramView.getOptions()) and elements->objectdiagram (shared association/link edges referenced NewObjectDiagram and ObjectNode for greying). Both are now inverted through the foundation slice: - new framework.IDiagram (DiagramView implements it) exposes getOptions(); element constructors take IDiagram instead of DiagramView. - new framework.IObjectDiagram extends IDiagram with containsLink()/ isObjectNodeGreyed(); NewObjectDiagram implements it, moving the ObjectDiagramData.fObjectToNodeMap reach-through into the diagram itself. - framework.ObjectNodeActivity gains isGreyed(); ObjectNode already has it, the two communication-diagram boxes return false (they have no greyed state). Behaviour-preserving (interface extraction + extract-method only; same runtime objects and calls). gui.views.diagrams cyclic sub-slices: 8 -> 7 (feedback edges 36 -> 27). elements now depends only on the framework foundation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract framework.IMainWindowServices (getModelBrowser/getSelectedView/ logWriter/statusBar) and behavior.shared.IBehaviorMainWindow (the two VisibleDataManager factory ops) so diagram type/selection slices depend on the host abstraction instead of concrete MainWindow. Move ModelBrowser + ModelBrowserMouseHandling into framework (so getModelBrowser()'s return type is foundation-safe). MainWindow implements IBehaviorMainWindow; the few genuine Frame/Component uses (dialog parents) use the view component or window ancestor. Groundwork: this lightens the type->root edges but does not yet drop the cyclic-slice count, because the type slices still 'extends DiagramView' (broken in the following DiagramView base-slice relocation). Behaviour-preserving (interface extraction + relocation; same runtime objects/calls). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…framework AllLayoutTypes<N extends Layoutable> (the layout engine) and StyleInfoBase discriminated nodes with instanceof ClassNode/ObjectNode + casts, coupling these otherwise-foundational classes to the classdiagram/objectdiagram type slices. Extract framework.IClassNode (cls/getClassifier) and framework.IObjectNode (marker extends ObjectNodeActivity, implemented ONLY by ObjectNode — not the communication-diagram boxes, so instanceof stays precise). Cast PlaceableNode methods (moveToPosition/name) to elements.PlaceableNode instead of the concrete node types. Behaviour-preserving. Prepares DiagramView's relocation to a base slice (DiagramView creates/drives AllLayoutTypes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move DiagramView, DiagramGraph, DiagramViewWithObjectNode, AllLayoutTypes, StyleInfoBase, StyleInfoProvider and IPluginDiagramExtensionPoint into a new gui.views.diagrams.base slice that depends only on elements/framework. This lets the diagram type slices satisfy 'extends DiagramView' downward (type -> base) instead of type -> root. Supporting changes: DiagramView's layout-action fields become javax.swing.Action; DataHolder moves to framework; the DiagramViewWithObjectNode.getAction factory is inlined at its two callers (so base no longer references event); the static MainWindow.instance() owner lookup uses SwingUtilities.getWindowAncestor(this). base is now an acyclic foundation layer. Feedback edges 27 -> 24; cyclic-slice count still 7 (the remaining core is event<->types and selection<->types). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…input handler) Move the three type-specific hide actions out of the event slice into the diagram type they act on: ActionHideClassDiagram -> classdiagram, ActionHideObjectDiagram -> objectdiagram, ActionHideCommunicationDiagram -> behavior.communicationdiagram. Invert DiagramInputHandling's NewObjectDiagram special-casing through framework.IObjectDiagram (extended with mayBeShowObjectInfo / mayBeDisposeObjectInfo / dropObjectFromModelBrowser). Widen ActionHide's ctor/fields to protected so the relocated subclasses (now in other packages) still compile. event now has no dependency on any diagram type slice (types -> event remains, one-directional). Feedback edges 24 -> 21. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ow handle Eliminate the static references to the concrete MainWindow (root) from the diagram slices: add IMainWindowServices.INSTANCE / JAVA_FX_MODE holders (set by MainWindow), move the host-service methods (addNewViewFrame, showStateMachineView x2, showObjectPropertiesView, createObject, getObjectDiagrams) onto the interface with foundation-safe signatures, and replace MainWindow.instance()/getJavaFxCall() across the type/selection/event slices with the framework handle (window-owner lookups use SwingUtilities.getWindowAncestor). Relocate the clean UI containers ViewFrame and ObjectPropertiesView into framework. gui.views.diagrams cyclic sub-slices: 7 -> 5 (event and statemachine leave the cycle); feedback edges 21 -> 13. Remaining: root/selection <-> the class/object/ behavior type slices. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sdiagram IFXWindowHost (foundation FX-host SPI, referenced by every diagram type via a static handle) moves from the diagrams root into framework. StyleInfoClassNode, StyleInfoEnumNode and StyleInfoEdge style class-diagram nodes (reference classdiagram.ClassNode/EnumNode, used only by ClassDiagram) move from root into classdiagram, dissolving the classdiagram<->root coupling. gui.views.diagrams cyclic sub-slices: 5 -> 3 (root and behavior leave). Remaining: selection <-> classdiagram/objectdiagram. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… at 0 cycles Split the bidirectionally-coupled selection slice into the diagram types it serves: the class-selection cluster (ClassSelection, the class selection views, SelectionComparator) moves to classdiagram.selection; the object-selection cluster (ObjectSelection, ActionSelection*, the object selection views) moves to objectdiagram.selection; the type-agnostic TableModel moves to framework. The selection<->classdiagram and selection<->objectdiagram cycles become intra-slice references (the only cross-type edge, classdiagram->objectdiagram, is one-way). gui.views.diagrams now has 0 cyclic sub-slices (down from 11). Flip the measurement test to a gated assertEquals(0) so CI keeps the diagram-editor subtree acyclic. Full reactor: 633 tests green; all arch tests assert 0 cycles.
…5.0 API Replace the prebuilt plugin jars in use-assembly with versions compiled against the cycle-free gui.views.diagrams API (framework/base relocations): OCLComplexity, ObjectToClass, AssociationExtend, Filmstrip, and ModelValidator (fat jar with its kodkod/sat4j/commons/log4j dependencies bundled, plugin Main-Class preserved). Each useplugin.xml now declares <?use version="7.5.0"?>. Sources: use_plugins branch decycle-compat. jdeps confirms all references resolve against use 7.5.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add section 6b cataloguing the diagram-editor decycling moves (types relocated to the new framework/base slices and into classdiagram/objectdiagram), the new foundation interfaces (IDiagram/IObjectDiagram/IClassNode/IObjectNode/ IMainWindowServices/IBehaviorMainWindow), and the one signature change plugin subclassers must make: the diagram host type MainWindow -> IMainWindowServices. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s with the new use
Brings the gsbase -> guava-testlib equals-test migration from main (PR #1, upstream useocl#4) into the decycling branch. Git applied main's edits through decycle-2's test relocations: - uml/ocl/type/TypeTest -> integration/uml/mm/types/TypeTest - uml/ocl/value/ValueTest -> uml/mm/values/ValueTest - gui/views/diagrams/util/DirectedLineTest (in place) gsbase dropped from use-core/use-gui poms; guava-testlib EqualsTester used. No production sources or package structure changed, so the 0-cycle gate holds. Verified: TypeTest (38), ValueTest (11), DirectedLineTest (1) all pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Edit: As of 9 of June 2026 I decide to withhold this until I have much more free time, and hopefully I can break this down into multiple commits that each should be easier to prove their functional correctness. |


I'm back after a month of hiatus, this time @h-man2 's suggestion is included right away in the temporary README of the repo (which should be useful to fully rewired into the
docsfolder after merging).Edit: As of 9 of June 2026 I decide to withhold this until I have much more free time, and hopefully I can break this down into multiple commits that each should be easier to prove their functional correctness.