sourcecode 2.5.4__tar.gz → 2.5.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sourcecode-2.5.4 → sourcecode-2.5.6}/CHANGELOG.md +116 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/PKG-INFO +1 -1
- {sourcecode-2.5.4 → sourcecode-2.5.6}/pyproject.toml +1 -1
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/__init__.py +1 -1
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/architecture_analyzer.py +75 -4
- sourcecode-2.5.6/src/sourcecode/reconciliation.py +457 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/summarizer.py +27 -5
- sourcecode-2.5.4/src/sourcecode/reconciliation.py +0 -201
- {sourcecode-2.5.4 → sourcecode-2.5.6}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/.gitignore +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/.ruff.toml +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/CONTRIBUTING.md +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/LICENSE +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/README.md +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/SECURITY.md +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/raw +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/archetype.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/cache.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/classifier.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/cli.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/explain.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/license.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/progress.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/redactor.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/repository_ir.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/ris.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/scanner.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/schema.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/security_config.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/serializer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_impact.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/version_check.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/src/sourcecode/workspace.py +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/functions/README.md +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-2.5.4 → sourcecode-2.5.6}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -1,5 +1,121 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.5.6] — 2026-07-17
|
|
4
|
+
|
|
5
|
+
**A layer must be attested by code.** The architecture fallback chain reported
|
|
6
|
+
directories as architecture; measured on the field-test repos, every fabricated
|
|
7
|
+
claim rested on files that are not code.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Build and asset directories were reported as modules (P1-D-REMAINING)**
|
|
12
|
+
(`reconciliation.py`, `architecture_analyzer.py`, `summarizer.py`):
|
|
13
|
+
|
|
14
|
+
| repo | claimed | what it rested on |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| ofbiz | *"modular: docker, framework, runtime, gradle"* | shell scripts, a wrapper jar, a `.gitignore` |
|
|
17
|
+
| neo4j | *"modular: packaging, annotations, community"* | `packaging/` is a build directory |
|
|
18
|
+
| eureka | *"modular: …, images, …"* | five PNGs |
|
|
19
|
+
| openmrs | *"modular: …, webapp, monitoring"* | GIFs and yaml |
|
|
20
|
+
| petclinic | *"Layered Architecture with orchestration, data layers"* | ONE directory entry and a `settings.gradle` — with 47 Java files visible and ignored |
|
|
21
|
+
| jobrunr | *"Fullstack with frontend, backend layers"* | a JVM library; the "frontend" is `dashboard/ui/model/`, **16 Java files** |
|
|
22
|
+
|
|
23
|
+
Two new Evidence Reconciliation rules, mirror images of each other and both
|
|
24
|
+
applied to the duplicated layer tables (architecture prose + headline):
|
|
25
|
+
|
|
26
|
+
- **`layer_code_attestation`** — a claimed MODULE or logical code layer that
|
|
27
|
+
holds no code of the repo's runtime is not one. Stricter sibling of
|
|
28
|
+
`layer_language_coherence`, which deliberately ignores runtime-less layers
|
|
29
|
+
so that a real Spring-MVC `templates/` view survives: a view may be
|
|
30
|
+
templates, a module may not.
|
|
31
|
+
- **`composition_runtime_split`** — a pattern that IS the split must show it.
|
|
32
|
+
`fullstack` names a frontend TIER and a backend TIER; if every layer runs on
|
|
33
|
+
one runtime, the layer names are a directory-name coincidence. Not applied
|
|
34
|
+
to `monorepo`/`microservices` — an all-TypeScript monorepo, or same-runtime
|
|
35
|
+
services, are perfectly real.
|
|
36
|
+
|
|
37
|
+
petclinic's stem-based layering is Python-shaped (`cli.py`, `*_analyzer.py`,
|
|
38
|
+
`config.py`) and matches almost nothing in Java, which is why two stray hits
|
|
39
|
+
could mint a whole "Layered Architecture".
|
|
40
|
+
|
|
41
|
+
- **Module names that never name a module** (`architecture_analyzer.py`): the
|
|
42
|
+
reverse-DNS package roots every JVM repo starts its namespace with
|
|
43
|
+
(`org`/`com`/`io`/`net` — jobrunr reported a module called *"org"*) and
|
|
44
|
+
multi-release source sets (`src/main/java17` minted *"java17"*; now as
|
|
45
|
+
transparent as `java/`). Same class as the `core` already excluded.
|
|
46
|
+
|
|
47
|
+
Validation across 10 field-test repos through the real CLI: ofbiz →
|
|
48
|
+
*"framework, applications"* (its two real modules); neo4j → *"annotations,
|
|
49
|
+
community"*; eureka → its eight real `eureka-*` modules; openmrs → *"web,
|
|
50
|
+
test-suite, api, liquibase"*; jobrunr → *"modular: jobrunr, language-support,
|
|
51
|
+
framework-support"* (its real top-level directories); **petclinic emits no
|
|
52
|
+
architecture claim at all** — the fabricated line is gone while every true
|
|
53
|
+
statement survives (*"Java server-side mvc web app using Spring Boot,
|
|
54
|
+
Thymeleaf"*, frameworks, bootstrap annotation). Broadleaf, alfresco, keycloak
|
|
55
|
+
and jenkins: byte-identical prose.
|
|
56
|
+
|
|
57
|
+
Investigated and refuted along the way: these claims are NOT built on a
|
|
58
|
+
truncated file list — the analyzer sees 100% of petclinic's Java and 99.4% of
|
|
59
|
+
ofbiz's.
|
|
60
|
+
|
|
61
|
+
11 new tests; suite 3448 passed.
|
|
62
|
+
|
|
63
|
+
## [2.5.5] — 2026-07-16
|
|
64
|
+
|
|
65
|
+
**An architecture claim must belong to one program.** Third Evidence
|
|
66
|
+
Reconciliation rule, extending the layer from the impact/extraction surfaces to
|
|
67
|
+
the narrative one.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- **A React SPA supplied the "view layer" of a Java MVC claim (SIM-3)**
|
|
72
|
+
(`reconciliation.py`, `architecture_analyzer.py`, `summarizer.py`): keycloak
|
|
73
|
+
read *"MVC pattern with controller, model, view layers"* — the `view` was
|
|
74
|
+
`js/apps/*/src/components` (87 `.tsx`, zero Java), half the `controller` layer
|
|
75
|
+
was the Kubernetes operator's reconcile controllers, and both sat beside a
|
|
76
|
+
1088-file Java `model`. Directory names are language-blind, so one pattern was
|
|
77
|
+
assembled across two separate programs; P1-D's mass gate cannot catch it,
|
|
78
|
+
because the SPA has genuine mass.
|
|
79
|
+
|
|
80
|
+
New rule **`layer_language_coherence`** — the layer table cross-checked
|
|
81
|
+
against the file-language census. A layer is incoherent iff it holds ZERO
|
|
82
|
+
files of the repo's runtime and at least one file of another: it is a
|
|
83
|
+
different program, not a facet of this architecture. Runtimes are grouped by
|
|
84
|
+
FAMILY (`.java`/`.kt`/`.scala`/`.groovy` = one JVM program), so a Kotlin view
|
|
85
|
+
beside a Java model is never a contradiction. A layer holding no runtime files
|
|
86
|
+
at all (templates, assets) is never flagged — that carve-out is what keeps a
|
|
87
|
+
genuine Spring-MVC `templates/` view intact. Patterns describing repo
|
|
88
|
+
COMPOSITION rather than one program's internals (`fullstack`,
|
|
89
|
+
`microservices`, `monorepo`) are exempt: spanning runtimes is their point.
|
|
90
|
+
The dominant runtime is taken from the whole repo, never from the matched
|
|
91
|
+
layers — which directories match a keyword table is the thing under
|
|
92
|
+
suspicion, so it cannot also be the arbiter. Applied to BOTH duplicated layer
|
|
93
|
+
tables (architecture prose + headline), since fixing one leaves the other
|
|
94
|
+
lying.
|
|
95
|
+
|
|
96
|
+
- **"core" minted an onion/hexagonal domain layer (P1-D residue)**
|
|
97
|
+
(`architecture_analyzer.py`, `summarizer.py`): P1-D removed the keywords that
|
|
98
|
+
do not attest their role (`api`, `store`, `db`) from the layered/fullstack/mvc
|
|
99
|
+
tables but left `core` in onion/hexagonal. `core` names a module's importance,
|
|
100
|
+
never a domain layer, and nearly every large JVM repo has one: keycloak's
|
|
101
|
+
`core/` (623 Java files) plus a `representations/adapters/config` package read
|
|
102
|
+
*"Onion Architecture with domain, adapters layers"*.
|
|
103
|
+
|
|
104
|
+
- **Hexagonal was claimed without ports (P1-D residue)**
|
|
105
|
+
(`architecture_analyzer.py`, `summarizer.py`): a pattern must show its
|
|
106
|
+
defining trait, not just any two of its layers — the mechanism that already
|
|
107
|
+
requires a `view` for MVC. Hexagonal is literally "Ports and Adapters", yet
|
|
108
|
+
`models/` + `adapters/` alone was minting it; onion now requires its domain
|
|
109
|
+
centre. The headline's table enforces required keys too, so it can no longer
|
|
110
|
+
contradict the architecture prose.
|
|
111
|
+
|
|
112
|
+
Validation across 10 field-test repos through the real CLI: keycloak's false
|
|
113
|
+
MVC claim became *"Monorepo workspace with apps, packages layers"* — verified
|
|
114
|
+
true in source (`js/apps/{admin-ui,account-ui}`, `js/libs/*`,
|
|
115
|
+
`pnpm-workspace.yaml`). Broadleaf (layered, all four layers), petclinic,
|
|
116
|
+
jobrunr, neo4j, openmrs, eureka, alfresco, ofbiz and jenkins: byte-identical
|
|
117
|
+
prose, zero collateral. 18 new tests; suite 3440 passed.
|
|
118
|
+
|
|
3
119
|
## [2.5.4] — 2026-07-16
|
|
4
120
|
|
|
5
121
|
**Evidence reconciliation + the endpoint axis.** The keycloak real-use simulacro
|
|
@@ -4,6 +4,12 @@ import re
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Literal, Optional
|
|
6
6
|
|
|
7
|
+
from sourcecode.reconciliation import (
|
|
8
|
+
incoherent_layers,
|
|
9
|
+
pattern_contradicted,
|
|
10
|
+
unattested_layers,
|
|
11
|
+
)
|
|
12
|
+
|
|
7
13
|
from sourcecode.schema import (
|
|
8
14
|
ArchitectureAnalysis,
|
|
9
15
|
ArchitectureDomain,
|
|
@@ -39,11 +45,27 @@ _SRC_TRANSPARENT = {
|
|
|
39
45
|
"src", "lib", "app", "pkg",
|
|
40
46
|
"main", "java", "kotlin", "scala", "groovy",
|
|
41
47
|
}
|
|
48
|
+
|
|
49
|
+
# `src/main/java17/` is a MULTI-RELEASE source set, as transparent as `java/`
|
|
50
|
+
# — JobRunr's minted a module called "java17".
|
|
51
|
+
_VERSIONED_SRC_RE = re.compile(r"^(?:java|kotlin|scala)\d+$")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _is_transparent_segment(name: str) -> bool:
|
|
55
|
+
"""A path segment that carries no architectural meaning of its own."""
|
|
56
|
+
low = name.lower()
|
|
57
|
+
return low in _SRC_TRANSPARENT or bool(_VERSIONED_SRC_RE.match(low))
|
|
42
58
|
_CODE_EXTENSIONS = {
|
|
43
59
|
".py", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs",
|
|
44
60
|
".go", ".java", ".kt", ".rs", ".rb",
|
|
45
61
|
}
|
|
46
|
-
|
|
62
|
+
# Names that never identify a module: generic buckets, and the reverse-DNS
|
|
63
|
+
# package roots every JVM repo starts its namespace with (JobRunr reported a
|
|
64
|
+
# module called "org").
|
|
65
|
+
_GENERIC_NAMES = {
|
|
66
|
+
"utils", "helpers", "common", "shared", "misc", "core", "root", "",
|
|
67
|
+
"org", "com", "io", "net",
|
|
68
|
+
}
|
|
47
69
|
|
|
48
70
|
_TEST_DIRS: frozenset[str] = frozenset({
|
|
49
71
|
"tests", "test", "spec", "specs", "__tests__", "e2e",
|
|
@@ -139,8 +161,14 @@ LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
|
|
|
139
161
|
"application": ["application", "usecases", "usecase"],
|
|
140
162
|
"infrastructure": ["infrastructure", "infra", "adapters", "persistence"],
|
|
141
163
|
},
|
|
164
|
+
# P1-D residue (found by measurement, 2026-07-16): "core" belongs with
|
|
165
|
+
# "api"/"store"/"db" — it names a module's importance ("the main module"),
|
|
166
|
+
# never an onion/hexagonal DOMAIN layer, and nearly every large JVM repo has
|
|
167
|
+
# one. With it, keycloak's `core/` (623 Java files) + a
|
|
168
|
+
# `representations/adapters/config` package read "Onion Architecture with
|
|
169
|
+
# domain, adapters layers". Kept are the names that state their role.
|
|
142
170
|
"onion": {
|
|
143
|
-
"domain": ["domain"
|
|
171
|
+
"domain": ["domain"],
|
|
144
172
|
"application": ["application", "usecases"],
|
|
145
173
|
"ports": ["ports", "interfaces"],
|
|
146
174
|
"adapters": ["adapters", "secondary"],
|
|
@@ -173,7 +201,7 @@ LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
|
|
|
173
201
|
"hexagonal": {
|
|
174
202
|
"port": ["port", "ports", "interface", "interfaces"],
|
|
175
203
|
"adapter": ["adapter", "adapters"],
|
|
176
|
-
"domain": ["domain", "
|
|
204
|
+
"domain": ["domain", "model", "models"], # "core" dropped — see onion
|
|
177
205
|
},
|
|
178
206
|
"monorepo": {
|
|
179
207
|
"apps": ["apps", "applications"],
|
|
@@ -196,6 +224,13 @@ LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
|
|
|
196
224
|
# it; without it the pattern falls through to layered / a weaker match.
|
|
197
225
|
_PATTERN_REQUIRED_KEYS: dict[str, frozenset[str]] = {
|
|
198
226
|
"mvc": frozenset({"view"}),
|
|
227
|
+
# Same principle as MVC/view — a pattern must show its DEFINING trait, not
|
|
228
|
+
# just any two of its layers. Hexagonal is literally "Ports and Adapters":
|
|
229
|
+
# a `models/` package next to an `adapters/` package is not one, and that
|
|
230
|
+
# pair alone was enough to label keycloak "Hexagonal Architecture". Onion's
|
|
231
|
+
# defining trait is the domain at the centre.
|
|
232
|
+
"hexagonal": frozenset({"port", "adapter"}),
|
|
233
|
+
"onion": frozenset({"domain"}),
|
|
199
234
|
}
|
|
200
235
|
|
|
201
236
|
# Minimum share of source files a matched layer must hold to count as a LAYER
|
|
@@ -646,6 +681,28 @@ class ArchitectureAnalyzer:
|
|
|
646
681
|
continue
|
|
647
682
|
matched[layer_key] = matched_dirs
|
|
648
683
|
layer_files[layer_key] = files
|
|
684
|
+
|
|
685
|
+
# SIM-3: a layer must belong to the same PROGRAM as the rest of the
|
|
686
|
+
# claim. Directory names are language-blind, so on a polyglot
|
|
687
|
+
# monorepo one pattern was assembled across two subsystems: keycloak
|
|
688
|
+
# read "MVC with controller, model, view layers" where `view` was a
|
|
689
|
+
# React SPA (87 .tsx, zero Java) sitting beside a 1088-file Java
|
|
690
|
+
# `model`. Mass cannot catch this — the SPA has genuine mass. The
|
|
691
|
+
# reconciliation rule drops layers owned by a different runtime;
|
|
692
|
+
# template/asset-only layers are untouched, so a real Spring-MVC
|
|
693
|
+
# `templates/` view survives.
|
|
694
|
+
_layer_paths = {k: [source_paths[i] for i in v] for k, v in layer_files.items()}
|
|
695
|
+
for _bad in incoherent_layers(pattern_name, _layer_paths, source_paths):
|
|
696
|
+
matched.pop(_bad, None)
|
|
697
|
+
layer_files.pop(_bad, None)
|
|
698
|
+
|
|
699
|
+
# A claim that IS a runtime split must show one: JobRunr, a JVM
|
|
700
|
+
# library, read "Fullstack with frontend, backend layers" off a
|
|
701
|
+
# `dashboard/ui/model/` package of 16 Java files. Nothing to prune —
|
|
702
|
+
# the pattern itself is what the evidence contradicts.
|
|
703
|
+
if pattern_contradicted(pattern_name, _layer_paths, source_paths):
|
|
704
|
+
continue
|
|
705
|
+
|
|
649
706
|
# A pattern with unmet required keys cannot qualify (e.g. mvc needs a view).
|
|
650
707
|
required = _PATTERN_REQUIRED_KEYS.get(pattern_name)
|
|
651
708
|
if required and not required.issubset(matched.keys()):
|
|
@@ -809,6 +866,14 @@ class ArchitectureAnalyzer:
|
|
|
809
866
|
k: v for k, v in layer_files.items()
|
|
810
867
|
if v and (not total or len(v) / total >= _LAYER_MIN_SHARE)
|
|
811
868
|
}
|
|
869
|
+
# P1-D residue: these stem patterns are Python-shaped (cli.py,
|
|
870
|
+
# *_analyzer.py, config.py). On a Java repo almost nothing matches, so
|
|
871
|
+
# the few stray hits are not a layering — petclinic's "Layered
|
|
872
|
+
# Architecture with orchestration, data layers" rested on ONE directory
|
|
873
|
+
# entry plus a settings.gradle, with 47 Java files visible and ignored.
|
|
874
|
+
# A logical code layer that holds no code of this repo is not one.
|
|
875
|
+
for _bad in unattested_layers(non_empty, paths):
|
|
876
|
+
non_empty.pop(_bad, None)
|
|
812
877
|
if len(non_empty) >= 2:
|
|
813
878
|
return "layered", [
|
|
814
879
|
ArchitectureLayer(name=k, pattern="layered", files=v, confidence="low")
|
|
@@ -828,13 +893,19 @@ class ArchitectureAnalyzer:
|
|
|
828
893
|
for p in paths:
|
|
829
894
|
parts = p.replace("\\", "/").split("/")
|
|
830
895
|
for part in parts[:-1]:
|
|
831
|
-
if (
|
|
896
|
+
if (not _is_transparent_segment(part)
|
|
832
897
|
and part.lower() not in _NON_SOURCE_DIRS
|
|
833
898
|
and part.lower() not in _GENERIC_NAMES):
|
|
834
899
|
module_files.setdefault(part, []).append(p)
|
|
835
900
|
break
|
|
836
901
|
|
|
837
902
|
meaningful = {k: v for k, v in module_files.items() if len(v) >= 3}
|
|
903
|
+
# P1-D residue: a MODULE of this repo must hold this repo's code. File
|
|
904
|
+
# count alone let build and asset directories be reported as modules —
|
|
905
|
+
# ofbiz "docker, framework, runtime, gradle", neo4j "packaging",
|
|
906
|
+
# eureka "images" (five PNGs). The real modules survive untouched.
|
|
907
|
+
for _bad in unattested_layers(meaningful, paths):
|
|
908
|
+
meaningful.pop(_bad, None)
|
|
838
909
|
if len(meaningful) >= 2:
|
|
839
910
|
return "modular", [
|
|
840
911
|
ArchitectureLayer(name=k, pattern="modular", files=v, confidence="low")
|