metrolith 4.0.0__py3-none-any.whl

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.
Files changed (368) hide show
  1. archlens_json.py +489 -0
  2. config/__init__.py +1 -0
  3. config/exclusions.v1.json +39 -0
  4. examples/README.md +33 -0
  5. examples/__init__.py +1 -0
  6. examples/input_template.csv +2 -0
  7. examples/local_project/app.py +16 -0
  8. examples/local_project/web.js +12 -0
  9. examples/quickstart_repositories.csv +5 -0
  10. examples/ratchet-rules.json +11 -0
  11. metrolith-4.0.0.dist-info/METADATA +154 -0
  12. metrolith-4.0.0.dist-info/RECORD +368 -0
  13. metrolith-4.0.0.dist-info/WHEEL +5 -0
  14. metrolith-4.0.0.dist-info/entry_points.txt +4 -0
  15. metrolith-4.0.0.dist-info/licenses/LICENSE +202 -0
  16. metrolith-4.0.0.dist-info/top_level.txt +6 -0
  17. modules/acquisition.py +1008 -0
  18. modules/admission.py +162 -0
  19. modules/benchmark_qualification.py +895 -0
  20. modules/benchmark_runner.py +1866 -0
  21. modules/callable_analysis/__init__.py +253 -0
  22. modules/callable_analysis/cognitive.py +304 -0
  23. modules/callable_analysis/cognitive_rules.py +314 -0
  24. modules/callable_analysis/go.py +254 -0
  25. modules/callable_analysis/java.py +359 -0
  26. modules/callable_analysis/js_ts.py +448 -0
  27. modules/callable_analysis/metrics.py +203 -0
  28. modules/callable_analysis/model.py +181 -0
  29. modules/callable_analysis/python.py +569 -0
  30. modules/callable_ledger.py +346 -0
  31. modules/changed_code.py +1345 -0
  32. modules/classify.py +74 -0
  33. modules/cli/__init__.py +21 -0
  34. modules/cli/baseline_command.py +160 -0
  35. modules/cli/changed_command.py +277 -0
  36. modules/cli/check_command.py +875 -0
  37. modules/cli/check_output.py +104 -0
  38. modules/cli/compare_command.py +531 -0
  39. modules/cli/diff_command.py +591 -0
  40. modules/cli/dossier_command.py +169 -0
  41. modules/cli/duplication_command.py +195 -0
  42. modules/cli/explain_command.py +482 -0
  43. modules/cli/export_output.py +276 -0
  44. modules/cli/hotspots_command.py +286 -0
  45. modules/cli/performance_command.py +309 -0
  46. modules/cli/policy_command.py +417 -0
  47. modules/cli/report_command.py +1326 -0
  48. modules/cli/reproduce_command.py +791 -0
  49. modules/cli/schema_command.py +96 -0
  50. modules/cli/transport.py +103 -0
  51. modules/cli/trust_command.py +318 -0
  52. modules/cli/validate_command.py +235 -0
  53. modules/clone_repo.py +897 -0
  54. modules/complexity_distribution.py +402 -0
  55. modules/complexity_view.py +572 -0
  56. modules/config.py +494 -0
  57. modules/contribution_ledger.py +442 -0
  58. modules/core_metrics.py +1815 -0
  59. modules/coverage.py +80 -0
  60. modules/db_analysis.py +139 -0
  61. modules/deployability.py +109 -0
  62. modules/diagnostics.py +444 -0
  63. modules/dossier.py +884 -0
  64. modules/duplication/__init__.py +92 -0
  65. modules/duplication/candidates.py +602 -0
  66. modules/duplication/grouping.py +178 -0
  67. modules/duplication/lexical.py +366 -0
  68. modules/duplication/model.py +309 -0
  69. modules/duplication/output.py +1195 -0
  70. modules/duplication/structural.py +934 -0
  71. modules/duplication/structural_grouping.py +465 -0
  72. modules/endpoints.py +299 -0
  73. modules/export_csv.py +216 -0
  74. modules/fact_sheet.py +351 -0
  75. modules/git_change_extractor.py +312 -0
  76. modules/go_analysis.py +48 -0
  77. modules/hotspots.py +973 -0
  78. modules/inventory.py +1665 -0
  79. modules/inventory_audits.py +232 -0
  80. modules/js_ts_audit.py +30 -0
  81. modules/local_source.py +501 -0
  82. modules/message_brokers.py +50 -0
  83. modules/metadata.py +190 -0
  84. modules/metric_warnings.py +98 -0
  85. modules/normalized_input.py +399 -0
  86. modules/policy/__init__.py +1 -0
  87. modules/policy/check.py +2643 -0
  88. modules/policy/document.py +233 -0
  89. modules/policy/document_v2.py +706 -0
  90. modules/policy/engine.py +164 -0
  91. modules/policy/evidence.py +961 -0
  92. modules/policy/findings.py +472 -0
  93. modules/policy/metrics.py +1284 -0
  94. modules/policy/rules.py +412 -0
  95. modules/policy/sarif.py +1035 -0
  96. modules/policy/trust.py +579 -0
  97. modules/preflight.py +699 -0
  98. modules/presentation.py +200 -0
  99. modules/ratchet/__init__.py +178 -0
  100. modules/ratchet/admission.py +382 -0
  101. modules/ratchet/capture.py +633 -0
  102. modules/ratchet/check_service.py +983 -0
  103. modules/ratchet/cli_request.py +161 -0
  104. modules/ratchet/comparison.py +174 -0
  105. modules/ratchet/contract.py +1051 -0
  106. modules/ratchet/observations.py +620 -0
  107. modules/ratchet/orchestration.py +299 -0
  108. modules/ratchet/pairing.py +290 -0
  109. modules/ratchet/semantics.py +497 -0
  110. modules/report_composition.py +302 -0
  111. modules/report_view.py +1226 -0
  112. modules/report_view_validation.py +788 -0
  113. modules/repository_files.py +81 -0
  114. modules/repository_input.py +396 -0
  115. modules/revision_diff.py +1609 -0
  116. modules/revision_source.py +361 -0
  117. modules/run_artifacts.py +1989 -0
  118. modules/source_composition.py +479 -0
  119. modules/source_frontend.py +875 -0
  120. modules/standalone_contracts.py +228 -0
  121. modules/static_analysis.py +46 -0
  122. modules/subject.py +428 -0
  123. modules/summary.py +1117 -0
  124. modules/syntax_predicates.py +266 -0
  125. modules/vocabularies.py +324 -0
  126. pipeline.py +1827 -0
  127. validation/__init__.py +1 -0
  128. validation/artifact_io/__init__.py +128 -0
  129. validation/artifact_io/compatibility.py +419 -0
  130. validation/artifact_io/contracts.py +169 -0
  131. validation/artifact_io/errors.py +150 -0
  132. validation/artifact_io/known_exceptions.py +369 -0
  133. validation/artifact_io/legacy_cells.py +186 -0
  134. validation/artifact_io/paths.py +308 -0
  135. validation/artifact_io/reader.py +688 -0
  136. validation/artifact_io/schema_store.py +972 -0
  137. validation/artifact_io/strict_csv.py +512 -0
  138. validation/artifact_io/strict_json.py +161 -0
  139. validation/conformance/__init__.py +18 -0
  140. validation/conformance/__main__.py +84 -0
  141. validation/conformance/data/REVIEW.md +76 -0
  142. validation/conformance/data/cases/compatibility-cap-oversized-jsx-is-not-rewritten/huge.jsx +20003 -0
  143. validation/conformance/data/cases/content-ambiguous-marker-does-not-exclude/config.ts +4 -0
  144. validation/conformance/data/cases/content-qt-linguist-ts-is-excluded/app.ts +3 -0
  145. validation/conformance/data/cases/content-qt-linguist-ts-is-excluded/i18n/messages_de.ts +7 -0
  146. validation/conformance/data/cases/encoding-binary-nul-fails/binary.js +0 -0
  147. validation/conformance/data/cases/encoding-cr-only-line-endings/cr.py +1 -0
  148. validation/conformance/data/cases/encoding-invalid-utf8-fails-loc/invalid.py +3 -0
  149. validation/conformance/data/cases/encoding-no-final-newline/nofinal.py +2 -0
  150. validation/conformance/data/cases/encoding-python-docstring-counts-as-code/docstring.py +8 -0
  151. validation/conformance/data/cases/encoding-truncated-utf16-fails-loc/truncated.py +0 -0
  152. validation/conformance/data/cases/encoding-unicode-path-is-inventoried/caf/303/251//346/227/245/346/234/254/350/252/236.py +1 -0
  153. validation/conformance/data/cases/encoding-utf16be-bom-python/utf16be.py +0 -0
  154. validation/conformance/data/cases/encoding-utf16le-bom-python/utf16le.py +0 -0
  155. validation/conformance/data/cases/encoding-utf8-bom-python/bom.py +2 -0
  156. validation/conformance/data/cases/entity-go-interface-is-secondary/iface.go +9 -0
  157. validation/conformance/data/cases/entity-go-struct-and-receiver-method/shape.go +13 -0
  158. validation/conformance/data/cases/entity-java-constructor-is-secondary/Delta.java +5 -0
  159. validation/conformance/data/cases/entity-java-enum-is-secondary/Colour.java +6 -0
  160. validation/conformance/data/cases/entity-java-interface-is-secondary/Gamma.java +3 -0
  161. validation/conformance/data/cases/entity-java-nested-class-counts/Outer.java +7 -0
  162. validation/conformance/data/cases/entity-java-record-counts/Point.java +2 -0
  163. validation/conformance/data/cases/entity-js-anonymous-callback-is-secondary/callbacks.js +5 -0
  164. validation/conformance/data/cases/entity-js-arrow-lambda-is-secondary/arrow.js +1 -0
  165. validation/conformance/data/cases/entity-js-commonjs-exported-function/helper.js +3 -0
  166. validation/conformance/data/cases/entity-js-stable-assigned-class/alpha.js +3 -0
  167. validation/conformance/data/cases/entity-python-class-inside-function/factory.py +6 -0
  168. validation/conformance/data/cases/entity-python-init-is-secondary/widget.py +6 -0
  169. validation/conformance/data/cases/entity-python-nested-function-is-secondary/nested.py +5 -0
  170. validation/conformance/data/cases/git-dirty-working-tree-is-still-measured/app.py +1 -0
  171. validation/conformance/data/cases/git-mode-120000-symlink-excluded/app.py +1 -0
  172. validation/conformance/data/cases/git-mode-120000-symlink-excluded/link.py +1 -0
  173. validation/conformance/data/cases/git-mode-160000-submodule-excluded/app.py +1 -0
  174. validation/conformance/data/cases/git-mode-160000-submodule-excluded/vendorlib/inner.py +1 -0
  175. validation/conformance/data/cases/git-mode-map-available-in-a-repository/app.py +1 -0
  176. validation/conformance/data/cases/git-mode-map-failed-downgrades-inventory/app.py +1 -0
  177. validation/conformance/data/cases/git-mode-map-timed-out-downgrades-inventory/app.py +1 -0
  178. validation/conformance/data/cases/git-mode-map-unavailable-outside-a-repository/app.py +1 -0
  179. validation/conformance/data/cases/git-native-symlink-excluded/app.py +1 -0
  180. validation/conformance/data/cases/git-native-symlink-excluded/target.py +1 -0
  181. validation/conformance/data/cases/loc-block-comment-only-lines-excluded/block.java +5 -0
  182. validation/conformance/data/cases/loc-comment-and-blank-lines-excluded/Alpha.java +5 -0
  183. validation/conformance/data/cases/loc-inline-comment-counts-once/Beta.java +3 -0
  184. validation/conformance/data/cases/loc-string-content-counts-as-code/strings.js +4 -0
  185. validation/conformance/data/cases/malformed-empty-file-is-a-verified-zero/empty.go +0 -0
  186. validation/conformance/data/cases/malformed-java-unclosed-brace/Broken.java +3 -0
  187. validation/conformance/data/cases/malformed-python2-print-statement/legacy.py +4 -0
  188. validation/conformance/data/cases/recovery-clean-javascript-uses-no-strategy/clean.js +3 -0
  189. validation/conformance/data/cases/recovery-genuinely-broken-javascript-is-not-recovered/broken.js +2 -0
  190. validation/conformance/data/cases/recovery-javascript-import-assertion-positive/cfg.mjs +2 -0
  191. validation/conformance/data/cases/recovery-jsx-reserved-attribute-positive/comp.jsx +3 -0
  192. validation/conformance/data/cases/recovery-raw-jsx-ampersand-positive/amp.jsx +3 -0
  193. validation/conformance/data/cases/recovery-typed-javascript-tsx-negative/typed.js +4 -0
  194. validation/conformance/data/cases/recovery-typed-javascript-tsx-positive/.flowconfig +1 -0
  195. validation/conformance/data/cases/recovery-typed-javascript-tsx-positive/package.json +1 -0
  196. validation/conformance/data/cases/recovery-typed-javascript-tsx-positive/src/typed.js +4 -0
  197. validation/conformance/data/cases/recovery-typescript-keyword-parameter-positive/keywords.ts +3 -0
  198. validation/conformance/data/cases/scope-declaration-only-typescript-is-excluded/index.ts +3 -0
  199. validation/conformance/data/cases/scope-declaration-only-typescript-is-excluded/types/index.d.ts +3 -0
  200. validation/conformance/data/cases/scope-generated-header-is-excluded/generated.go +7 -0
  201. validation/conformance/data/cases/scope-generated-header-is-excluded/handwritten.go +5 -0
  202. validation/conformance/data/cases/scope-go-test-suffix-is-excluded/service.go +5 -0
  203. validation/conformance/data/cases/scope-go-test-suffix-is-excluded/service_test.go +3 -0
  204. validation/conformance/data/cases/scope-test-file-is-excluded/src/Main.java +3 -0
  205. validation/conformance/data/cases/scope-test-file-is-excluded/src/test/java/MainTest.java +3 -0
  206. validation/conformance/data/cases/scope-unsupported-extension-is-not-a-source-file/README.md +1 -0
  207. validation/conformance/data/cases/scope-unsupported-extension-is-not-a-source-file/app.py +2 -0
  208. validation/conformance/data/cases/scope-unsupported-extension-is-not-a-source-file/config.yaml +1 -0
  209. validation/conformance/data/cases/scope-vendor-directory-is-excluded/app.go +5 -0
  210. validation/conformance/data/cases/scope-vendor-directory-is-excluded/vendor/github.com/other/lib/lib.go +5 -0
  211. validation/conformance/data/cases/scope-verified-zero-entities/config.go +3 -0
  212. validation/conformance/data/cases/size-boundary-file-at-the-limit-is-readable/atlimit.py +10 -0
  213. validation/conformance/data/cases/size-boundary-oversized-file-stays-in-source-files/big.py +400 -0
  214. validation/conformance/data/cases/size-boundary-oversized-file-stays-in-source-files/small.py +1 -0
  215. validation/conformance/data/expected/compatibility-cap-oversized-jsx-is-not-rewritten.json +7 -0
  216. validation/conformance/data/expected/content-ambiguous-marker-does-not-exclude.json +11 -0
  217. validation/conformance/data/expected/content-qt-linguist-ts-is-excluded.json +11 -0
  218. validation/conformance/data/expected/encoding-binary-nul-fails.json +7 -0
  219. validation/conformance/data/expected/encoding-cr-only-line-endings.json +8 -0
  220. validation/conformance/data/expected/encoding-invalid-utf8-fails-loc.json +8 -0
  221. validation/conformance/data/expected/encoding-no-final-newline.json +9 -0
  222. validation/conformance/data/expected/encoding-python-docstring-counts-as-code.json +12 -0
  223. validation/conformance/data/expected/encoding-truncated-utf16-fails-loc.json +8 -0
  224. validation/conformance/data/expected/encoding-unicode-path-is-inventoried.json +11 -0
  225. validation/conformance/data/expected/encoding-utf16be-bom-python.json +11 -0
  226. validation/conformance/data/expected/encoding-utf16le-bom-python.json +11 -0
  227. validation/conformance/data/expected/encoding-utf8-bom-python.json +13 -0
  228. validation/conformance/data/expected/entity-go-interface-is-secondary.json +9 -0
  229. validation/conformance/data/expected/entity-go-struct-and-receiver-method.json +11 -0
  230. validation/conformance/data/expected/entity-java-constructor-is-secondary.json +11 -0
  231. validation/conformance/data/expected/entity-java-enum-is-secondary.json +9 -0
  232. validation/conformance/data/expected/entity-java-interface-is-secondary.json +11 -0
  233. validation/conformance/data/expected/entity-java-nested-class-counts.json +9 -0
  234. validation/conformance/data/expected/entity-java-record-counts.json +8 -0
  235. validation/conformance/data/expected/entity-js-anonymous-callback-is-secondary.json +8 -0
  236. validation/conformance/data/expected/entity-js-arrow-lambda-is-secondary.json +10 -0
  237. validation/conformance/data/expected/entity-js-commonjs-exported-function.json +8 -0
  238. validation/conformance/data/expected/entity-js-stable-assigned-class.json +9 -0
  239. validation/conformance/data/expected/entity-python-class-inside-function.json +11 -0
  240. validation/conformance/data/expected/entity-python-init-is-secondary.json +10 -0
  241. validation/conformance/data/expected/entity-python-nested-function-is-secondary.json +9 -0
  242. validation/conformance/data/expected/git-dirty-working-tree-is-still-measured.json +11 -0
  243. validation/conformance/data/expected/git-mode-120000-symlink-excluded.json +13 -0
  244. validation/conformance/data/expected/git-mode-160000-submodule-excluded.json +13 -0
  245. validation/conformance/data/expected/git-mode-map-available-in-a-repository.json +11 -0
  246. validation/conformance/data/expected/git-mode-map-failed-downgrades-inventory.json +12 -0
  247. validation/conformance/data/expected/git-mode-map-timed-out-downgrades-inventory.json +12 -0
  248. validation/conformance/data/expected/git-mode-map-unavailable-outside-a-repository.json +13 -0
  249. validation/conformance/data/expected/git-native-symlink-excluded.json +10 -0
  250. validation/conformance/data/expected/loc-block-comment-only-lines-excluded.json +9 -0
  251. validation/conformance/data/expected/loc-comment-and-blank-lines-excluded.json +13 -0
  252. validation/conformance/data/expected/loc-inline-comment-counts-once.json +9 -0
  253. validation/conformance/data/expected/loc-string-content-counts-as-code.json +10 -0
  254. validation/conformance/data/expected/malformed-empty-file-is-a-verified-zero.json +11 -0
  255. validation/conformance/data/expected/malformed-java-unclosed-brace.json +9 -0
  256. validation/conformance/data/expected/malformed-python2-print-statement.json +8 -0
  257. validation/conformance/data/expected/recovery-clean-javascript-uses-no-strategy.json +10 -0
  258. validation/conformance/data/expected/recovery-genuinely-broken-javascript-is-not-recovered.json +8 -0
  259. validation/conformance/data/expected/recovery-javascript-import-assertion-positive.json +10 -0
  260. validation/conformance/data/expected/recovery-jsx-reserved-attribute-positive.json +10 -0
  261. validation/conformance/data/expected/recovery-raw-jsx-ampersand-positive.json +10 -0
  262. validation/conformance/data/expected/recovery-typed-javascript-tsx-negative.json +8 -0
  263. validation/conformance/data/expected/recovery-typed-javascript-tsx-positive.json +10 -0
  264. validation/conformance/data/expected/recovery-typescript-keyword-parameter-positive.json +10 -0
  265. validation/conformance/data/expected/scope-declaration-only-typescript-is-excluded.json +8 -0
  266. validation/conformance/data/expected/scope-generated-header-is-excluded.json +8 -0
  267. validation/conformance/data/expected/scope-go-test-suffix-is-excluded.json +8 -0
  268. validation/conformance/data/expected/scope-test-file-is-excluded.json +8 -0
  269. validation/conformance/data/expected/scope-unsupported-extension-is-not-a-source-file.json +8 -0
  270. validation/conformance/data/expected/scope-vendor-directory-is-excluded.json +8 -0
  271. validation/conformance/data/expected/scope-verified-zero-entities.json +13 -0
  272. validation/conformance/data/expected/size-boundary-file-at-the-limit-is-readable.json +10 -0
  273. validation/conformance/data/expected/size-boundary-oversized-file-stays-in-source-files.json +13 -0
  274. validation/conformance/data/manifest.json +1118 -0
  275. validation/conformance/runner.py +425 -0
  276. validation/resources/__init__.py +13 -0
  277. validation/resources/schemas/analysis-1.10.schema.json +10 -0
  278. validation/resources/schemas/analysis-1.5.schema.json +10 -0
  279. validation/resources/schemas/analysis-1.6.schema.json +10 -0
  280. validation/resources/schemas/analysis-1.7.schema.json +10 -0
  281. validation/resources/schemas/analysis-1.9.schema.json +10 -0
  282. validation/resources/schemas/benchmark_qualification-1.0.schema.json +186 -0
  283. validation/resources/schemas/benchmark_qualification_registry-1.0.schema.json +252 -0
  284. validation/resources/schemas/callable_row-1.10.schema.json +242 -0
  285. validation/resources/schemas/callable_row-1.9.schema.json +234 -0
  286. validation/resources/schemas/catalog_row-1.11.schema.json +665 -0
  287. validation/resources/schemas/catalog_row-1.5.schema.json +472 -0
  288. validation/resources/schemas/catalog_row-1.7.schema.json +484 -0
  289. validation/resources/schemas/check_result-1.0.schema.json +742 -0
  290. validation/resources/schemas/check_result-1.1.schema.json +936 -0
  291. validation/resources/schemas/check_result-1.2.schema.json +939 -0
  292. validation/resources/schemas/check_result-1.3.schema.json +1127 -0
  293. validation/resources/schemas/check_result-1.4.schema.json +1404 -0
  294. validation/resources/schemas/compare_output-1.0.schema.json +93 -0
  295. validation/resources/schemas/compare_output-1.1.schema.json +114 -0
  296. validation/resources/schemas/contribution_container-1.5.schema.json +85 -0
  297. validation/resources/schemas/contribution_row-1.5.schema.json +353 -0
  298. validation/resources/schemas/contribution_row-1.7.schema.json +366 -0
  299. validation/resources/schemas/contribution_row-1.9.schema.json +392 -0
  300. validation/resources/schemas/environment-1.12.schema.json +136 -0
  301. validation/resources/schemas/environment-1.5.schema.json +107 -0
  302. validation/resources/schemas/errors_row-1.5.schema.json +796 -0
  303. validation/resources/schemas/errors_row-1.7.schema.json +808 -0
  304. validation/resources/schemas/explain_output-1.0.schema.json +130 -0
  305. validation/resources/schemas/explain_output-1.1.schema.json +182 -0
  306. validation/resources/schemas/explain_output-1.2.schema.json +208 -0
  307. validation/resources/schemas/explain_output-1.3.schema.json +216 -0
  308. validation/resources/schemas/file_inventory-1.6.schema.json +331 -0
  309. validation/resources/schemas/file_inventory-1.7.schema.json +335 -0
  310. validation/resources/schemas/frozen_input_row-1.5.schema.json +50 -0
  311. validation/resources/schemas/language_metrics_row-1.11.schema.json +377 -0
  312. validation/resources/schemas/language_metrics_row-1.5.schema.json +244 -0
  313. validation/resources/schemas/language_metrics_row-1.7.schema.json +256 -0
  314. validation/resources/schemas/log_event-1.5.schema.json +72 -0
  315. validation/resources/schemas/normalized_input_row-1.5.schema.json +155 -0
  316. validation/resources/schemas/normalized_input_row-1.7.schema.json +168 -0
  317. validation/resources/schemas/performance_profile-1.0.schema.json +118 -0
  318. validation/resources/schemas/policy_document-1.0.schema.json +114 -0
  319. validation/resources/schemas/policy_document-2.0.schema.json +284 -0
  320. validation/resources/schemas/policy_document-2.1.schema.json +296 -0
  321. validation/resources/schemas/policy_document-2.2.schema.json +312 -0
  322. validation/resources/schemas/policy_result-1.0.schema.json +338 -0
  323. validation/resources/schemas/ratchet_baseline-1.0.schema.json +381 -0
  324. validation/resources/schemas/recoveries_row-1.5.schema.json +378 -0
  325. validation/resources/schemas/recoveries_row-1.7.schema.json +390 -0
  326. validation/resources/schemas/recoveries_row-1.8.schema.json +394 -0
  327. validation/resources/schemas/report_metadata-1.0.schema.json +48 -0
  328. validation/resources/schemas/report_view-1.0.schema.json +334 -0
  329. validation/resources/schemas/repository_document-1.10.schema.json +728 -0
  330. validation/resources/schemas/repository_document-1.5.schema.json +164 -0
  331. validation/resources/schemas/repository_document-1.6.schema.json +418 -0
  332. validation/resources/schemas/repository_document-1.7.schema.json +495 -0
  333. validation/resources/schemas/repository_document-1.9.schema.json +717 -0
  334. validation/resources/schemas/reproduction_output-1.0.schema.json +131 -0
  335. validation/resources/schemas/reproduction_output-1.1.schema.json +164 -0
  336. validation/resources/schemas/retry_input_row-1.5.schema.json +53 -0
  337. validation/resources/schemas/revision_diff-1.0.schema.json +936 -0
  338. validation/resources/schemas/revision_diff-1.1.schema.json +1241 -0
  339. validation/resources/schemas/revision_diff-1.2.schema.json +1419 -0
  340. validation/resources/schemas/run_manifest-1.10.schema.json +384 -0
  341. validation/resources/schemas/run_manifest-1.11.schema.json +535 -0
  342. validation/resources/schemas/run_manifest-1.12.schema.json +564 -0
  343. validation/resources/schemas/run_manifest-1.5.schema.json +379 -0
  344. validation/resources/schemas/run_manifest-1.6.schema.json +378 -0
  345. validation/resources/schemas/run_manifest-1.7.schema.json +378 -0
  346. validation/resources/schemas/run_manifest-1.9.schema.json +384 -0
  347. validation/resources/schemas/run_status-1.10.schema.json +182 -0
  348. validation/resources/schemas/run_status-1.5.schema.json +120 -0
  349. validation/resources/schemas/run_status-1.6.schema.json +168 -0
  350. validation/resources/schemas/run_status-1.7.schema.json +168 -0
  351. validation/resources/schemas/run_status-1.9.schema.json +175 -0
  352. validation/resources/schemas/sheet_metrics_row-1.11.schema.json +476 -0
  353. validation/resources/schemas/sheet_metrics_row-1.5.schema.json +283 -0
  354. validation/resources/schemas/sheet_metrics_row-1.7.schema.json +295 -0
  355. validation/resources/schemas/trusted_evidence_receipt-1.0.schema.json +150 -0
  356. validation/scripts/__init__.py +1 -0
  357. validation/scripts/cache_contamination_check.py +115 -0
  358. validation/scripts/capture_environment.py +56 -0
  359. validation/scripts/compare_semantic_runs.py +65 -0
  360. validation/scripts/derive_tabular_schemas.py +392 -0
  361. validation/scripts/generate_source_manifest.py +447 -0
  362. validation/scripts/independent_inventory_check.py +279 -0
  363. validation/scripts/independent_loc_check.py +126 -0
  364. validation/scripts/loc_file_differences.py +79 -0
  365. validation/scripts/manual_entity_samples.py +344 -0
  366. validation/scripts/schema_closure_audit.py +351 -0
  367. validation/scripts/semantic_projection.py +731 -0
  368. validation/scripts/validate_outputs.py +860 -0
archlens_json.py ADDED
@@ -0,0 +1,489 @@
1
+ """Neutral bounded JSON input and output for external ArchLens documents.
2
+
3
+ This module deliberately has no dependency on Policy, Ratchet, CLI, or the
4
+ validation package. Those domains translate :class:`StrictJsonError` into
5
+ their own public error types while sharing one byte-exact JSON boundary.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import math
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class StrictJsonLimits:
19
+ """Resource limits applied before and after JSON decoding."""
20
+
21
+ max_bytes: int
22
+ max_depth: int
23
+ max_collection_items: int
24
+ max_total_items: int
25
+ max_string_bytes: int
26
+
27
+
28
+ POLICY_JSON_LIMITS = StrictJsonLimits(
29
+ max_bytes=4 * 1024 * 1024,
30
+ max_depth=64,
31
+ max_collection_items=20_000,
32
+ max_total_items=100_000,
33
+ max_string_bytes=1 * 1024 * 1024,
34
+ )
35
+ BASELINE_JSON_LIMITS = StrictJsonLimits(
36
+ max_bytes=64 * 1024 * 1024,
37
+ max_depth=64,
38
+ max_collection_items=1_000_000,
39
+ max_total_items=2_000_000,
40
+ max_string_bytes=16 * 1024 * 1024,
41
+ )
42
+ EVIDENCE_JSON_LIMITS = StrictJsonLimits(
43
+ max_bytes=512 * 1024 * 1024,
44
+ max_depth=64,
45
+ max_collection_items=5_000_000,
46
+ max_total_items=10_000_000,
47
+ max_string_bytes=64 * 1024 * 1024,
48
+ )
49
+ CHECK_JSON_LIMITS = StrictJsonLimits(
50
+ max_bytes=512 * 1024 * 1024,
51
+ max_depth=64,
52
+ max_collection_items=5_000_000,
53
+ max_total_items=10_000_000,
54
+ max_string_bytes=64 * 1024 * 1024,
55
+ )
56
+ SARIF_JSON_LIMITS = CHECK_JSON_LIMITS
57
+ ARTIFACT_JSON_LIMITS = EVIDENCE_JSON_LIMITS
58
+
59
+
60
+ class StrictJsonError(ValueError):
61
+ """One bounded JSON admission or serialization failure."""
62
+
63
+ def __init__(
64
+ self,
65
+ code: str,
66
+ source: str,
67
+ message: str,
68
+ *,
69
+ location: str | None = None,
70
+ detail: dict[str, Any] | None = None,
71
+ ) -> None:
72
+ super().__init__(message)
73
+ self.code = code
74
+ self.source = source
75
+ self.message = message
76
+ self.location = location
77
+ self.detail = detail or {}
78
+
79
+ def __str__(self) -> str:
80
+ where = f" at {self.location}" if self.location else ""
81
+ return f"[{self.code}] {self.source}{where}: {self.message}"
82
+
83
+
84
+ _UTF8_BOM = b"\xef\xbb\xbf"
85
+ _UTF16_LE_BOM = b"\xff\xfe"
86
+ _UTF16_BE_BOM = b"\xfe\xff"
87
+ _UTF32_LE_BOM = b"\xff\xfe\x00\x00"
88
+ _UTF32_BE_BOM = b"\x00\x00\xfe\xff"
89
+
90
+
91
+ def read_bounded_bytes(
92
+ path: Path | str,
93
+ *,
94
+ source: str | None = None,
95
+ limits: StrictJsonLimits,
96
+ ) -> bytes:
97
+ """Read a regular external JSON payload without unbounded allocation."""
98
+
99
+ candidate = Path(path)
100
+ label = source or str(candidate)
101
+ try:
102
+ size = candidate.stat().st_size
103
+ except OSError as exc:
104
+ raise StrictJsonError(
105
+ "artifact_unreadable", label, f"cannot stat JSON input: {exc}"
106
+ ) from exc
107
+ if size > limits.max_bytes:
108
+ raise StrictJsonError(
109
+ "artifact_too_large",
110
+ label,
111
+ f"JSON input is {size} bytes; limit is {limits.max_bytes}",
112
+ detail={"actual_bytes": size, "max_bytes": limits.max_bytes},
113
+ )
114
+ try:
115
+ # The stat check gives an early, cheap rejection. The bounded read is
116
+ # still required because an external file can grow between stat/open.
117
+ with candidate.open("rb") as handle:
118
+ payload = handle.read(limits.max_bytes + 1)
119
+ except OSError as exc:
120
+ raise StrictJsonError(
121
+ "artifact_unreadable", label, f"cannot read JSON input: {exc}"
122
+ ) from exc
123
+ _check_byte_count(payload, label, limits)
124
+ return payload
125
+
126
+
127
+ def _check_byte_count(
128
+ payload: bytes, source: str, limits: StrictJsonLimits
129
+ ) -> None:
130
+ if len(payload) > limits.max_bytes:
131
+ raise StrictJsonError(
132
+ "artifact_too_large",
133
+ source,
134
+ f"JSON input is {len(payload)} bytes; limit is {limits.max_bytes}",
135
+ detail={"actual_bytes": len(payload), "max_bytes": limits.max_bytes},
136
+ )
137
+
138
+
139
+ def decode_utf8(
140
+ payload: bytes,
141
+ *,
142
+ source: str,
143
+ limits: StrictJsonLimits,
144
+ ) -> str:
145
+ """Decode strict UTF-8 bytes without universal-newline translation."""
146
+
147
+ if not isinstance(payload, bytes):
148
+ raise StrictJsonError("invalid_input_type", source, "JSON input must be bytes")
149
+ _check_byte_count(payload, source, limits)
150
+ if payload.startswith(_UTF32_LE_BOM) or payload.startswith(_UTF32_BE_BOM):
151
+ raise StrictJsonError(
152
+ "unsupported_encoding", source, "UTF-32 JSON is not supported; use UTF-8"
153
+ )
154
+ if payload.startswith(_UTF16_LE_BOM) or payload.startswith(_UTF16_BE_BOM):
155
+ raise StrictJsonError(
156
+ "unsupported_encoding", source, "UTF-16 JSON is not supported; use UTF-8"
157
+ )
158
+ if payload.startswith(_UTF8_BOM):
159
+ raise StrictJsonError(
160
+ "unexpected_byte_order_mark", source, "a UTF-8 BOM is not permitted"
161
+ )
162
+ try:
163
+ return payload.decode("utf-8", errors="strict")
164
+ except UnicodeDecodeError as exc:
165
+ raise StrictJsonError(
166
+ "invalid_utf8",
167
+ source,
168
+ f"JSON input is not valid UTF-8: {exc.reason}",
169
+ location=f"byte offset {exc.start}",
170
+ detail={"byte_offset": exc.start},
171
+ ) from exc
172
+
173
+
174
+ def _text_as_utf8(
175
+ text: str, *, source: str, limits: StrictJsonLimits
176
+ ) -> bytes:
177
+ if not isinstance(text, str):
178
+ raise StrictJsonError("invalid_input_type", source, "JSON input must be text")
179
+ if text.startswith("\ufeff"):
180
+ raise StrictJsonError(
181
+ "unexpected_byte_order_mark", source, "a Unicode BOM is not permitted"
182
+ )
183
+ try:
184
+ encoded = text.encode("utf-8", errors="strict")
185
+ except UnicodeEncodeError as exc:
186
+ raise StrictJsonError(
187
+ "invalid_utf8",
188
+ source,
189
+ "JSON text contains a value that cannot be encoded as UTF-8",
190
+ location=f"character offset {exc.start}",
191
+ ) from exc
192
+ _check_byte_count(encoded, source, limits)
193
+ return encoded
194
+
195
+
196
+ def _check_text_depth(text: str, source: str, limit: int) -> None:
197
+ """Bound container nesting before ``json.loads`` can recurse."""
198
+
199
+ depth = 0
200
+ in_string = False
201
+ escaped = False
202
+ for offset, character in enumerate(text):
203
+ if in_string:
204
+ if escaped:
205
+ escaped = False
206
+ elif character == "\\":
207
+ escaped = True
208
+ elif character == '"':
209
+ in_string = False
210
+ continue
211
+ if character == '"':
212
+ in_string = True
213
+ elif character in "[{":
214
+ depth += 1
215
+ if depth > limit:
216
+ raise StrictJsonError(
217
+ "json_depth_exceeded",
218
+ source,
219
+ f"JSON nesting exceeds the {limit} level limit",
220
+ location=f"character offset {offset}",
221
+ detail={"max_depth": limit},
222
+ )
223
+ elif character in "]}":
224
+ depth -= 1
225
+
226
+
227
+ def _duplicate_hook(source: str):
228
+ def hook(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
229
+ result: dict[str, Any] = {}
230
+ for key, value in pairs:
231
+ if key in result:
232
+ raise StrictJsonError(
233
+ "json_duplicate_key",
234
+ source,
235
+ f"duplicate JSON object key {key!r}",
236
+ location=f"key {key!r}",
237
+ detail={"key": key},
238
+ )
239
+ result[key] = value
240
+ return result
241
+
242
+ return hook
243
+
244
+
245
+ def _constant_hook(source: str):
246
+ def reject(value: str) -> Any:
247
+ raise StrictJsonError(
248
+ "json_non_standard_number",
249
+ source,
250
+ f"{value} is not valid JSON; numbers must be finite",
251
+ detail={"constant": value},
252
+ )
253
+
254
+ return reject
255
+
256
+
257
+ def _expected_name(expect: type | tuple[type, ...]) -> str:
258
+ if isinstance(expect, type):
259
+ return expect.__name__
260
+ return "/".join(item.__name__ for item in expect)
261
+
262
+
263
+ def validate_json_value(
264
+ value: Any,
265
+ *,
266
+ source: str,
267
+ limits: StrictJsonLimits,
268
+ expect: type | tuple[type, ...] | None = None,
269
+ ) -> Any:
270
+ """Recursively enforce JSON types, finite numbers, and item bounds."""
271
+
272
+ if expect is not None and not isinstance(value, expect):
273
+ raise StrictJsonError(
274
+ "json_root_type_unexpected",
275
+ source,
276
+ f"root value is {type(value).__name__}, expected {_expected_name(expect)}",
277
+ )
278
+
279
+ stack: list[tuple[Any, int, str, bool]] = [(value, 1, "$", False)]
280
+ active_containers: set[int] = set()
281
+ total = 0
282
+ while stack:
283
+ item, depth, location, leaving = stack.pop()
284
+ if leaving:
285
+ active_containers.remove(id(item))
286
+ continue
287
+ total += 1
288
+ if total > limits.max_total_items:
289
+ raise StrictJsonError(
290
+ "json_item_count_exceeded",
291
+ source,
292
+ f"JSON value contains more than {limits.max_total_items} items",
293
+ location=location,
294
+ detail={"max_total_items": limits.max_total_items},
295
+ )
296
+ if item is None or isinstance(item, bool) or isinstance(item, int):
297
+ continue
298
+ if isinstance(item, float):
299
+ if not math.isfinite(item):
300
+ raise StrictJsonError(
301
+ "json_non_standard_number",
302
+ source,
303
+ "JSON numbers must be finite",
304
+ location=location,
305
+ )
306
+ continue
307
+ if isinstance(item, str):
308
+ if len(item) > limits.max_string_bytes:
309
+ raise StrictJsonError(
310
+ "json_item_too_large",
311
+ source,
312
+ f"JSON string exceeds the {limits.max_string_bytes} byte limit",
313
+ location=location,
314
+ )
315
+ try:
316
+ item_size = len(item.encode("utf-8", errors="strict"))
317
+ except UnicodeEncodeError as exc:
318
+ raise StrictJsonError(
319
+ "invalid_utf8",
320
+ source,
321
+ "JSON string cannot be encoded as UTF-8",
322
+ location=location,
323
+ ) from exc
324
+ if item_size > limits.max_string_bytes:
325
+ raise StrictJsonError(
326
+ "json_item_too_large",
327
+ source,
328
+ f"JSON string is {item_size} bytes; limit is "
329
+ f"{limits.max_string_bytes}",
330
+ location=location,
331
+ )
332
+ continue
333
+ if isinstance(item, (dict, list)):
334
+ identity = id(item)
335
+ if identity in active_containers:
336
+ raise StrictJsonError(
337
+ "json_circular_reference",
338
+ source,
339
+ "JSON value contains a circular reference",
340
+ location=location,
341
+ )
342
+ if depth > limits.max_depth:
343
+ raise StrictJsonError(
344
+ "json_depth_exceeded",
345
+ source,
346
+ f"JSON nesting exceeds the {limits.max_depth} level limit",
347
+ location=location,
348
+ detail={"max_depth": limits.max_depth},
349
+ )
350
+ active_containers.add(identity)
351
+ stack.append((item, depth, location, True))
352
+ if len(item) > limits.max_collection_items:
353
+ raise StrictJsonError(
354
+ "json_collection_size_exceeded",
355
+ source,
356
+ f"JSON collection contains {len(item)} items; limit is "
357
+ f"{limits.max_collection_items}",
358
+ location=location,
359
+ )
360
+ if isinstance(item, dict):
361
+ children: list[tuple[Any, int, str, bool]] = []
362
+ for key, child in item.items():
363
+ if not isinstance(key, str):
364
+ raise StrictJsonError(
365
+ "json_object_key_type_invalid",
366
+ source,
367
+ "JSON object keys must be strings",
368
+ location=location,
369
+ )
370
+ children.append((key, depth, f"{location}.<key>", False))
371
+ children.append((child, depth + 1, f"{location}.{key}", False))
372
+ stack.extend(reversed(children))
373
+ else:
374
+ for index in range(len(item) - 1, -1, -1):
375
+ stack.append(
376
+ (item[index], depth + 1, f"{location}[{index}]", False)
377
+ )
378
+ continue
379
+ raise StrictJsonError(
380
+ "json_value_type_invalid",
381
+ source,
382
+ f"{type(item).__name__} is not a JSON value type",
383
+ location=location,
384
+ )
385
+ return value
386
+
387
+
388
+ def loads_text(
389
+ text: str,
390
+ *,
391
+ source: str,
392
+ limits: StrictJsonLimits,
393
+ expect: type | tuple[type, ...] | None = dict,
394
+ ) -> Any:
395
+ """Parse bounded JSON text with duplicate-key and finite-number rejection."""
396
+
397
+ _text_as_utf8(text, source=source, limits=limits)
398
+ _check_text_depth(text, source, limits.max_depth)
399
+ try:
400
+ value = json.loads(
401
+ text,
402
+ object_pairs_hook=_duplicate_hook(source),
403
+ parse_constant=_constant_hook(source),
404
+ )
405
+ except json.JSONDecodeError as exc:
406
+ raise StrictJsonError(
407
+ "json_malformed",
408
+ source,
409
+ f"malformed JSON: {exc.msg}",
410
+ location=f"line {exc.lineno}, column {exc.colno}",
411
+ detail={"line": exc.lineno, "column": exc.colno},
412
+ ) from exc
413
+ return validate_json_value(value, source=source, limits=limits, expect=expect)
414
+
415
+
416
+ def loads_bytes(
417
+ payload: bytes,
418
+ *,
419
+ source: str,
420
+ limits: StrictJsonLimits,
421
+ expect: type | tuple[type, ...] | None = dict,
422
+ ) -> Any:
423
+ text = decode_utf8(payload, source=source, limits=limits)
424
+ return loads_text(text, source=source, limits=limits, expect=expect)
425
+
426
+
427
+ def load_file(
428
+ path: Path | str,
429
+ *,
430
+ source: str | None = None,
431
+ limits: StrictJsonLimits,
432
+ expect: type | tuple[type, ...] | None = dict,
433
+ ) -> Any:
434
+ label = source or str(path)
435
+ payload = read_bounded_bytes(path, source=label, limits=limits)
436
+ return loads_bytes(payload, source=label, limits=limits, expect=expect)
437
+
438
+
439
+ def dumps_strict(
440
+ value: Any,
441
+ *,
442
+ source: str,
443
+ limits: StrictJsonLimits,
444
+ ensure_ascii: bool = False,
445
+ sort_keys: bool = True,
446
+ separators: tuple[str, str] | None = None,
447
+ indent: int | None = None,
448
+ trailing_newline: bool = False,
449
+ ) -> str:
450
+ """Serialize only bounded, recursively valid JSON with ``allow_nan=False``."""
451
+
452
+ validate_json_value(value, source=source, limits=limits)
453
+ try:
454
+ rendered = json.dumps(
455
+ value,
456
+ ensure_ascii=ensure_ascii,
457
+ sort_keys=sort_keys,
458
+ separators=separators,
459
+ indent=indent,
460
+ allow_nan=False,
461
+ )
462
+ except (TypeError, ValueError) as exc:
463
+ raise StrictJsonError(
464
+ "json_serialization_failed", source, f"cannot serialize JSON: {exc}"
465
+ ) from exc
466
+ if trailing_newline:
467
+ rendered += "\n"
468
+ encoded = _text_as_utf8(rendered, source=source, limits=limits)
469
+ _check_byte_count(encoded, source, limits)
470
+ return rendered
471
+
472
+
473
+ __all__ = [
474
+ "ARTIFACT_JSON_LIMITS",
475
+ "BASELINE_JSON_LIMITS",
476
+ "CHECK_JSON_LIMITS",
477
+ "EVIDENCE_JSON_LIMITS",
478
+ "POLICY_JSON_LIMITS",
479
+ "SARIF_JSON_LIMITS",
480
+ "StrictJsonError",
481
+ "StrictJsonLimits",
482
+ "decode_utf8",
483
+ "dumps_strict",
484
+ "load_file",
485
+ "loads_bytes",
486
+ "loads_text",
487
+ "read_bounded_bytes",
488
+ "validate_json_value",
489
+ ]
config/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Packaged Metrolith policy data."""
@@ -0,0 +1,39 @@
1
+ {
2
+ "version": "1.5.0",
3
+ "excluded_directories": [
4
+ ".git", ".gradle", ".hg", ".idea", ".mypy_cache", ".next",
5
+ ".nuxt", ".pytest_cache", ".svn", ".tox", ".venv", ".vscode",
6
+ "__pycache__", "bin", "bower_components", "build", "coverage",
7
+ "deps", "dist", "generated", "node_modules", "obj", "out",
8
+ "target", "third-party", "third_party", "vendor", "vendors", "venv"
9
+ ],
10
+ "dependency_directories": [
11
+ "bower_components", "deps", "node_modules", "third-party",
12
+ "third_party", "vendor", "vendors"
13
+ ],
14
+ "build_output_directories": [
15
+ ".next", ".nuxt", "bin", "build", "coverage", "dist", "obj", "out", "target"
16
+ ],
17
+ "cache_directories": [
18
+ ".gradle", ".mypy_cache", ".pytest_cache", ".tox", ".venv",
19
+ "__pycache__", "venv"
20
+ ],
21
+ "test_directories": [
22
+ "__tests__", "componenttest", "componenttests", "contracttest",
23
+ "contracttests", "e2e", "fixture", "fixtures", "functionaltest",
24
+ "functionaltests", "integration-test", "integration-tests",
25
+ "integrationtest", "integrationtests", "spec", "specs", "test",
26
+ "testdata", "tests", "unittest", "unittests"
27
+ ],
28
+ "generated_suffixes": [
29
+ ".gen.go", ".gen.java", ".gen.js", ".gen.jsx", ".gen.ts", ".gen.tsx",
30
+ ".generated.go", ".generated.java", ".generated.js", ".generated.jsx",
31
+ ".generated.ts", ".generated.tsx", ".generated.mjs", ".generated.cjs",
32
+ ".generated.mts", ".generated.cts", ".pb.go", ".pb.gw.go", "_generated.go"
33
+ ],
34
+ "minified_or_bundle_suffixes": [
35
+ ".bundle.js", ".bundle.mjs", ".bundle.cjs", ".bundle.ts", ".bundle.tsx",
36
+ ".bundle.min.js", ".chunk.js", ".chunk.mjs", ".min.js", ".min.mjs",
37
+ ".min.cjs", ".min.ts", ".min.tsx"
38
+ ]
39
+ }
examples/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Examples
2
+
3
+ The beginner example is `local_project/app.py` and `local_project/web.js`.
4
+ Use CPython 3.13 (`>=3.13,<3.14`; tested 3.13.9) and Git on PATH.
5
+ Git is required even though these files are not a Git repository.
6
+ Run it without network access after installation:
7
+
8
+ ```console
9
+ metrolith example run --local
10
+ ```
11
+
12
+ The command copies the packaged sources into controlled temporary storage and
13
+ runs the normal analysis pipeline. Read the printed Run path and `summary.md`;
14
+ `metrolith report RUN` creates an offline HTML report.
15
+
16
+ From the source project you can use the same files directly:
17
+
18
+ ```console
19
+ metrolith analyze examples/local_project --workspace ../metrolith-demo
20
+ ```
21
+
22
+ `quickstart_repositories.csv` supports the existing optional `example run`
23
+ language selections. It contains exact frozen commits; network access or an
24
+ already populated cache is required. `input_template.csv` documents the CSV
25
+ columns for explicit repository-list workflows and is not an offline demo.
26
+
27
+ `ratchet-rules.json` is an illustrative rule array for `baseline capture`.
28
+ Choose your own tolerance. Capture requires a qualifying exact-revision Run;
29
+ the baseline's `.source-run/` sidecar and the current CLI ancestry limitation
30
+ are explained in [USAGE](../docs/USAGE.md) in the source distribution.
31
+
32
+ Start with the [source setup](../README.md); see [USAGE](../docs/USAGE.md)
33
+ for quoted Run paths and the example wrapper's exit 1 on preflight refusal.
examples/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Packaged Metrolith quickstart inputs."""
@@ -0,0 +1,2 @@
1
+ url,architecture_type,expected_language,commit_sha,enabled,notes
2
+ https://github.com/owner/project,unknown,,,,Replace this demonstration URL before running
@@ -0,0 +1,16 @@
1
+ """Small, deterministic Python half of the network-free tutorial fixture."""
2
+
3
+
4
+ def classify_order(total: float, item_count: int) -> str:
5
+ if item_count <= 0:
6
+ return "empty"
7
+ if total >= 100 and item_count >= 3:
8
+ return "large"
9
+ if total >= 25:
10
+ return "medium"
11
+ return "small"
12
+
13
+
14
+ def summarize_orders(orders: list[dict[str, float]]) -> dict[str, float]:
15
+ total = sum(float(order.get("total", 0)) for order in orders)
16
+ return {"count": len(orders), "total": total}
@@ -0,0 +1,12 @@
1
+ /** Small JavaScript half of the network-free tutorial fixture. */
2
+ export function visibleOrders(orders, includeArchived = false) {
3
+ return orders.filter((order) => {
4
+ if (includeArchived) return true;
5
+ return order.status !== "archived";
6
+ });
7
+ }
8
+
9
+ export function orderLabel(order) {
10
+ const customer = order.customer || "Anonymous";
11
+ return `${customer}: ${order.status || "unknown"}`;
12
+ }
@@ -0,0 +1,5 @@
1
+ url,architecture_type,expected_language,commit_sha,enabled,notes
2
+ https://github.com/MstfTurgut/hotel-reservation-system,unknown,Java,0912d283fc54caadc361740e675271b0c69be336,true,Frozen Java tutorial
3
+ https://github.com/googlecodelabs/monolith-to-microservices,unknown,JavaScript,deeb3099f6a29c0ec8b92a20013fdc94f69f0d47,true,Frozen JavaScript tutorial
4
+ https://github.com/allegro/ralph,unknown,Python,f0d695e9b24a7d1fa475b81bbee87a897e389eb2,true,Frozen Python tutorial; Complete repository and Python metrics in candidate evidence
5
+ https://github.com/golang-app/ecommerce,unknown,Go,5708b2b784f83825c2550abdc35d1d330e4b1283,true,Frozen Go tutorial
@@ -0,0 +1,11 @@
1
+ [
2
+ {
3
+ "rule_id": "ratchet.source-files",
4
+ "metric": "repository.source_files",
5
+ "metric_contract": "metrics",
6
+ "scope": "repository",
7
+ "direction": "increase_is_worse",
8
+ "max_regression": 0,
9
+ "severity": "violation"
10
+ }
11
+ ]