pluvy 0.0.1__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.
Files changed (247) hide show
  1. pluvy-0.0.1/.agents/skills/pluvy/SKILL.md +361 -0
  2. pluvy-0.0.1/.agents/skills/pluvy/references/agent-integration.md +75 -0
  3. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/TEMPLATE.md +122 -0
  4. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/architecture.dispatch.md +42 -0
  5. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/architecture.import.md +56 -0
  6. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/architecture.object_model.md +59 -0
  7. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/architecture.owner.md +56 -0
  8. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/architecture.structure.md +50 -0
  9. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/flow.handling.md +91 -0
  10. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/function_size.md +69 -0
  11. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/imports.wildcard.md +31 -0
  12. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/structure.guarded_projection.md +96 -0
  13. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/test.deletion.md +121 -0
  14. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/test.integrity.md +111 -0
  15. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.annotation.md +39 -0
  16. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.assertion.md +53 -0
  17. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.boundary.md +63 -0
  18. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.broad.md +99 -0
  19. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.contract.md +83 -0
  20. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.signature.md +39 -0
  21. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/type.suppression.md +61 -0
  22. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/unused.field.md +44 -0
  23. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/unused.function.md +52 -0
  24. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/unused.parameter.md +61 -0
  25. pluvy-0.0.1/.agents/skills/pluvy/references/checkers/unused.return.md +46 -0
  26. pluvy-0.0.1/.agents/skills/pluvy/references/checkers.md +117 -0
  27. pluvy-0.0.1/.agents/skills/pluvy/references/comment-suppressions.md +110 -0
  28. pluvy-0.0.1/.agents/skills/pluvy/references/policy-sample.md +79 -0
  29. pluvy-0.0.1/.github/ISSUE_TEMPLATE/false-positive.md +83 -0
  30. pluvy-0.0.1/.github/workflows/ci.yml +15 -0
  31. pluvy-0.0.1/.github/workflows/deploy-website.yml +62 -0
  32. pluvy-0.0.1/.github/workflows/engineering.yml +33 -0
  33. pluvy-0.0.1/.github/workflows/release.yml +314 -0
  34. pluvy-0.0.1/.gitignore +7 -0
  35. pluvy-0.0.1/.pluvy/pluvy.json +272 -0
  36. pluvy-0.0.1/AGENTS.md +21 -0
  37. pluvy-0.0.1/Dockerfile +28 -0
  38. pluvy-0.0.1/LICENSE +21 -0
  39. pluvy-0.0.1/PKG-INFO +133 -0
  40. pluvy-0.0.1/README.md +104 -0
  41. pluvy-0.0.1/docs/agent-review.md +131 -0
  42. pluvy-0.0.1/docs/checker-configuration.md +791 -0
  43. pluvy-0.0.1/docs/comment-suppressions.md +6 -0
  44. pluvy-0.0.1/docs/engineering-standards.md +315 -0
  45. pluvy-0.0.1/docs/getting-started.md +279 -0
  46. pluvy-0.0.1/docs/go-consumed-fields.md +54 -0
  47. pluvy-0.0.1/docs/plugin-architecture.md +233 -0
  48. pluvy-0.0.1/docs/zh/getting-started.md +231 -0
  49. pluvy-0.0.1/examples/.pluvy/pluvy.json +47 -0
  50. pluvy-0.0.1/pyproject.toml +84 -0
  51. pluvy-0.0.1/src/pluvy/__init__.py +1 -0
  52. pluvy-0.0.1/src/pluvy/__main__.py +5 -0
  53. pluvy-0.0.1/src/pluvy/analysis/__init__.py +1 -0
  54. pluvy-0.0.1/src/pluvy/analysis/analyzer.py +115 -0
  55. pluvy-0.0.1/src/pluvy/analysis/context.py +51 -0
  56. pluvy-0.0.1/src/pluvy/analysis/contracts.py +69 -0
  57. pluvy-0.0.1/src/pluvy/analysis/requirements.py +10 -0
  58. pluvy-0.0.1/src/pluvy/analysis/scopes.py +53 -0
  59. pluvy-0.0.1/src/pluvy/analysis/session.py +88 -0
  60. pluvy-0.0.1/src/pluvy/architecture/__init__.py +17 -0
  61. pluvy-0.0.1/src/pluvy/architecture/design.py +68 -0
  62. pluvy-0.0.1/src/pluvy/architecture/design_contracts.py +15 -0
  63. pluvy-0.0.1/src/pluvy/architecture/imports.py +27 -0
  64. pluvy-0.0.1/src/pluvy/architecture/models.py +12 -0
  65. pluvy-0.0.1/src/pluvy/architecture/options.py +109 -0
  66. pluvy-0.0.1/src/pluvy/architecture/rules.py +98 -0
  67. pluvy-0.0.1/src/pluvy/architecture.py +6 -0
  68. pluvy-0.0.1/src/pluvy/cli.py +155 -0
  69. pluvy-0.0.1/src/pluvy/diagnostics.py +72 -0
  70. pluvy-0.0.1/src/pluvy/engine.py +21 -0
  71. pluvy-0.0.1/src/pluvy/imports.py +21 -0
  72. pluvy-0.0.1/src/pluvy/languages/__init__.py +1 -0
  73. pluvy-0.0.1/src/pluvy/languages/alias_syntax.py +27 -0
  74. pluvy-0.0.1/src/pluvy/languages/change_scopes.py +46 -0
  75. pluvy-0.0.1/src/pluvy/languages/comments.py +44 -0
  76. pluvy-0.0.1/src/pluvy/languages/design_syntax.py +64 -0
  77. pluvy-0.0.1/src/pluvy/languages/encoding_syntax.py +93 -0
  78. pluvy-0.0.1/src/pluvy/languages/go/__init__.py +5 -0
  79. pluvy-0.0.1/src/pluvy/languages/go/consumed_fields.py +125 -0
  80. pluvy-0.0.1/src/pluvy/languages/go/encoding.py +68 -0
  81. pluvy-0.0.1/src/pluvy/languages/go/encoding_members.py +75 -0
  82. pluvy-0.0.1/src/pluvy/languages/go/plugin.py +25 -0
  83. pluvy-0.0.1/src/pluvy/languages/go/projection.py +27 -0
  84. pluvy-0.0.1/src/pluvy/languages/go/rules.py +38 -0
  85. pluvy-0.0.1/src/pluvy/languages/go/semantic_syntax.py +152 -0
  86. pluvy-0.0.1/src/pluvy/languages/go/serialization_fields.py +156 -0
  87. pluvy-0.0.1/src/pluvy/languages/go/syntax.py +21 -0
  88. pluvy-0.0.1/src/pluvy/languages/go/test_syntax.py +82 -0
  89. pluvy-0.0.1/src/pluvy/languages/go/value_origins.py +221 -0
  90. pluvy-0.0.1/src/pluvy/languages/import_syntax.py +45 -0
  91. pluvy-0.0.1/src/pluvy/languages/java/__init__.py +5 -0
  92. pluvy-0.0.1/src/pluvy/languages/java/encoding.py +78 -0
  93. pluvy-0.0.1/src/pluvy/languages/java/encoding_members.py +96 -0
  94. pluvy-0.0.1/src/pluvy/languages/java/encoding_types.py +28 -0
  95. pluvy-0.0.1/src/pluvy/languages/java/failure.py +49 -0
  96. pluvy-0.0.1/src/pluvy/languages/java/plugin.py +19 -0
  97. pluvy-0.0.1/src/pluvy/languages/java/projection.py +22 -0
  98. pluvy-0.0.1/src/pluvy/languages/java/rules.py +59 -0
  99. pluvy-0.0.1/src/pluvy/languages/java/semantic_syntax.py +251 -0
  100. pluvy-0.0.1/src/pluvy/languages/java/syntax.py +112 -0
  101. pluvy-0.0.1/src/pluvy/languages/java/test_syntax.py +149 -0
  102. pluvy-0.0.1/src/pluvy/languages/java/type_syntax.py +103 -0
  103. pluvy-0.0.1/src/pluvy/languages/javascript/__init__.py +5 -0
  104. pluvy-0.0.1/src/pluvy/languages/javascript/encoding.py +63 -0
  105. pluvy-0.0.1/src/pluvy/languages/javascript/encoding_members.py +82 -0
  106. pluvy-0.0.1/src/pluvy/languages/javascript/failure.py +187 -0
  107. pluvy-0.0.1/src/pluvy/languages/javascript/plugin.py +21 -0
  108. pluvy-0.0.1/src/pluvy/languages/javascript/projection.py +98 -0
  109. pluvy-0.0.1/src/pluvy/languages/javascript/semantic_syntax.py +353 -0
  110. pluvy-0.0.1/src/pluvy/languages/javascript/syntax.py +55 -0
  111. pluvy-0.0.1/src/pluvy/languages/javascript/test_syntax.py +258 -0
  112. pluvy-0.0.1/src/pluvy/languages/metric_syntax.py +129 -0
  113. pluvy-0.0.1/src/pluvy/languages/projection_syntax.py +211 -0
  114. pluvy-0.0.1/src/pluvy/languages/python/__init__.py +5 -0
  115. pluvy-0.0.1/src/pluvy/languages/python/encoding.py +24 -0
  116. pluvy-0.0.1/src/pluvy/languages/python/encoding_members.py +93 -0
  117. pluvy-0.0.1/src/pluvy/languages/python/failure.py +71 -0
  118. pluvy-0.0.1/src/pluvy/languages/python/outputs.py +108 -0
  119. pluvy-0.0.1/src/pluvy/languages/python/plugin.py +19 -0
  120. pluvy-0.0.1/src/pluvy/languages/python/projection.py +78 -0
  121. pluvy-0.0.1/src/pluvy/languages/python/qualified_names.py +12 -0
  122. pluvy-0.0.1/src/pluvy/languages/python/result_syntax.py +146 -0
  123. pluvy-0.0.1/src/pluvy/languages/python/rules.py +266 -0
  124. pluvy-0.0.1/src/pluvy/languages/python/semantic_syntax.py +193 -0
  125. pluvy-0.0.1/src/pluvy/languages/python/syntax.py +106 -0
  126. pluvy-0.0.1/src/pluvy/languages/python/test_syntax.py +180 -0
  127. pluvy-0.0.1/src/pluvy/languages/python/type_syntax.py +75 -0
  128. pluvy-0.0.1/src/pluvy/languages/semantic_nodes.py +489 -0
  129. pluvy-0.0.1/src/pluvy/languages/semantic_syntax.py +7 -0
  130. pluvy-0.0.1/src/pluvy/languages/type_checks.py +45 -0
  131. pluvy-0.0.1/src/pluvy/languages/typescript/__init__.py +5 -0
  132. pluvy-0.0.1/src/pluvy/languages/typescript/contract.py +202 -0
  133. pluvy-0.0.1/src/pluvy/languages/typescript/plugin.py +31 -0
  134. pluvy-0.0.1/src/pluvy/languages/typescript/result_syntax.py +102 -0
  135. pluvy-0.0.1/src/pluvy/languages/typescript/rules.py +45 -0
  136. pluvy-0.0.1/src/pluvy/languages/typescript/semantic_syntax.py +84 -0
  137. pluvy-0.0.1/src/pluvy/languages/typescript/syntax.py +15 -0
  138. pluvy-0.0.1/src/pluvy/metrics/__init__.py +19 -0
  139. pluvy-0.0.1/src/pluvy/metrics/contracts.py +31 -0
  140. pluvy-0.0.1/src/pluvy/metrics/measure.py +48 -0
  141. pluvy-0.0.1/src/pluvy/metrics/options.py +48 -0
  142. pluvy-0.0.1/src/pluvy/metrics/rules.py +51 -0
  143. pluvy-0.0.1/src/pluvy/models.py +20 -0
  144. pluvy-0.0.1/src/pluvy/parsing/__init__.py +1 -0
  145. pluvy-0.0.1/src/pluvy/parsing/treesitter.py +79 -0
  146. pluvy-0.0.1/src/pluvy/plugins/__init__.py +1 -0
  147. pluvy-0.0.1/src/pluvy/plugins/builtins.py +347 -0
  148. pluvy-0.0.1/src/pluvy/plugins/bundles.py +70 -0
  149. pluvy-0.0.1/src/pluvy/plugins/languages.py +49 -0
  150. pluvy-0.0.1/src/pluvy/plugins/profiles.py +24 -0
  151. pluvy-0.0.1/src/pluvy/plugins/registry.py +104 -0
  152. pluvy-0.0.1/src/pluvy/policy/__init__.py +1 -0
  153. pluvy-0.0.1/src/pluvy/policy/contracts.py +93 -0
  154. pluvy-0.0.1/src/pluvy/policy/models.py +48 -0
  155. pluvy-0.0.1/src/pluvy/policy/paths.py +8 -0
  156. pluvy-0.0.1/src/pluvy/policy/type_options.py +80 -0
  157. pluvy-0.0.1/src/pluvy/py.typed +0 -0
  158. pluvy-0.0.1/src/pluvy/review/__init__.py +1 -0
  159. pluvy-0.0.1/src/pluvy/review/acp.py +173 -0
  160. pluvy-0.0.1/src/pluvy/review/channel.py +111 -0
  161. pluvy-0.0.1/src/pluvy/review/contracts.py +56 -0
  162. pluvy-0.0.1/src/pluvy/review/mcp_server.py +58 -0
  163. pluvy-0.0.1/src/pluvy/review/prompt.py +33 -0
  164. pluvy-0.0.1/src/pluvy/review/rules.py +33 -0
  165. pluvy-0.0.1/src/pluvy/review/submission_models.py +72 -0
  166. pluvy-0.0.1/src/pluvy/review/submissions.py +106 -0
  167. pluvy-0.0.1/src/pluvy/review/wire.py +78 -0
  168. pluvy-0.0.1/src/pluvy/semantics/__init__.py +1 -0
  169. pluvy-0.0.1/src/pluvy/semantics/aliases.py +72 -0
  170. pluvy-0.0.1/src/pluvy/semantics/boundaries.py +214 -0
  171. pluvy-0.0.1/src/pluvy/semantics/encoding.py +121 -0
  172. pluvy-0.0.1/src/pluvy/semantics/encoding_members.py +38 -0
  173. pluvy-0.0.1/src/pluvy/semantics/encoding_options.py +81 -0
  174. pluvy-0.0.1/src/pluvy/semantics/failure.py +116 -0
  175. pluvy-0.0.1/src/pluvy/semantics/failure_options.py +36 -0
  176. pluvy-0.0.1/src/pluvy/semantics/flow.py +63 -0
  177. pluvy-0.0.1/src/pluvy/semantics/index.py +508 -0
  178. pluvy-0.0.1/src/pluvy/semantics/model.py +181 -0
  179. pluvy-0.0.1/src/pluvy/semantics/options.py +62 -0
  180. pluvy-0.0.1/src/pluvy/semantics/overrides.py +164 -0
  181. pluvy-0.0.1/src/pluvy/semantics/parameter_properties.py +98 -0
  182. pluvy-0.0.1/src/pluvy/semantics/project.py +99 -0
  183. pluvy-0.0.1/src/pluvy/semantics/projection/__init__.py +1 -0
  184. pluvy-0.0.1/src/pluvy/semantics/projection/contracts.py +81 -0
  185. pluvy-0.0.1/src/pluvy/semantics/projection/matching.py +129 -0
  186. pluvy-0.0.1/src/pluvy/semantics/projection/options.py +34 -0
  187. pluvy-0.0.1/src/pluvy/semantics/projection/rules.py +80 -0
  188. pluvy-0.0.1/src/pluvy/semantics/results.py +86 -0
  189. pluvy-0.0.1/src/pluvy/semantics/testing/__init__.py +1 -0
  190. pluvy-0.0.1/src/pluvy/semantics/testing/contracts.py +54 -0
  191. pluvy-0.0.1/src/pluvy/semantics/testing/gate.py +203 -0
  192. pluvy-0.0.1/src/pluvy/semantics/testing/options.py +75 -0
  193. pluvy-0.0.1/src/pluvy/semantics/testing/rules.py +111 -0
  194. pluvy-0.0.1/src/pluvy/semantics/usage.py +123 -0
  195. pluvy-0.0.1/src/pluvy/snapshots.py +370 -0
  196. pluvy-0.0.1/src/pluvy/source.py +50 -0
  197. pluvy-0.0.1/src/pluvy/suppressions.py +189 -0
  198. pluvy-0.0.1/src/pluvy/syntax.py +16 -0
  199. pluvy-0.0.1/tests/fixtures/acp_review_agent.py +221 -0
  200. pluvy-0.0.1/tests/test_acp_review.py +191 -0
  201. pluvy-0.0.1/tests/test_checker_options.py +221 -0
  202. pluvy-0.0.1/tests/test_checker_registration.py +167 -0
  203. pluvy-0.0.1/tests/test_cli.py +651 -0
  204. pluvy-0.0.1/tests/test_deletion_gate.py +613 -0
  205. pluvy-0.0.1/tests/test_encoding_configuration.py +190 -0
  206. pluvy-0.0.1/tests/test_encoding_flow.py +116 -0
  207. pluvy-0.0.1/tests/test_extensions.py +525 -0
  208. pluvy-0.0.1/tests/test_failure_flow.py +507 -0
  209. pluvy-0.0.1/tests/test_gate.py +234 -0
  210. pluvy-0.0.1/tests/test_generic_result_snapshots.py +106 -0
  211. pluvy-0.0.1/tests/test_generic_results.py +233 -0
  212. pluvy-0.0.1/tests/test_go_consumed_fields.py +185 -0
  213. pluvy-0.0.1/tests/test_go_consumption_regressions.py +108 -0
  214. pluvy-0.0.1/tests/test_go_method_snapshots.py +108 -0
  215. pluvy-0.0.1/tests/test_guarded_projection.py +331 -0
  216. pluvy-0.0.1/tests/test_guarded_projection_cli.py +100 -0
  217. pluvy-0.0.1/tests/test_guidance_contract.py +399 -0
  218. pluvy-0.0.1/tests/test_inheritance_resolution.py +225 -0
  219. pluvy-0.0.1/tests/test_inheritance_snapshots.py +95 -0
  220. pluvy-0.0.1/tests/test_inheritance_usage.py +233 -0
  221. pluvy-0.0.1/tests/test_instance_output_languages.py +493 -0
  222. pluvy-0.0.1/tests/test_instance_outputs.py +234 -0
  223. pluvy-0.0.1/tests/test_java.py +271 -0
  224. pluvy-0.0.1/tests/test_java_semantics.py +346 -0
  225. pluvy-0.0.1/tests/test_json_erasure.py +287 -0
  226. pluvy-0.0.1/tests/test_language_adapters.py +135 -0
  227. pluvy-0.0.1/tests/test_line_suppressions.py +523 -0
  228. pluvy-0.0.1/tests/test_metrics.py +65 -0
  229. pluvy-0.0.1/tests/test_object_design.py +88 -0
  230. pluvy-0.0.1/tests/test_override_usage.py +269 -0
  231. pluvy-0.0.1/tests/test_plugins.py +271 -0
  232. pluvy-0.0.1/tests/test_projection_access_identity.py +80 -0
  233. pluvy-0.0.1/tests/test_projection_construction.py +107 -0
  234. pluvy-0.0.1/tests/test_projection_language_boundaries.py +112 -0
  235. pluvy-0.0.1/tests/test_python_tree_rules.py +113 -0
  236. pluvy-0.0.1/tests/test_review.py +117 -0
  237. pluvy-0.0.1/tests/test_review_submissions.py +103 -0
  238. pluvy-0.0.1/tests/test_semantic_element_evidence.py +955 -0
  239. pluvy-0.0.1/tests/test_semantic_element_project.py +521 -0
  240. pluvy-0.0.1/tests/test_semantic_resolution.py +609 -0
  241. pluvy-0.0.1/tests/test_semantic_usage.py +2907 -0
  242. pluvy-0.0.1/tests/test_structure.py +145 -0
  243. pluvy-0.0.1/tests/test_syntax_warnings.py +94 -0
  244. pluvy-0.0.1/tests/test_test_integrity.py +854 -0
  245. pluvy-0.0.1/tests/test_type_contract.py +710 -0
  246. pluvy-0.0.1/tests/test_type_contract_supplement.py +61 -0
  247. pluvy-0.0.1/tests/test_typescript_parameter_properties.py +353 -0
@@ -0,0 +1,361 @@
1
+ ---
2
+ name: pluvy
3
+ description: Run the pluvy gate after code tasks only in repositories with a project policy or an explicit project requirement, or when the user explicitly requests a run. Use also to adopt, install, configure, or explain pluvy, edit `.pluvy/pluvy.json`, triage findings, inspect checker coverage and options, or use approved line suppression with a reason in every supported language. Global installation or skill availability does not enable a repository; without project adoption or an explicit run request, skip automatic runs and gate reporting.
4
+ ---
5
+
6
+ # pluvy
7
+
8
+ pluvy is a read-only, parse-based architecture policy gate. It audits this
9
+ repository's sources against the policy in `.pluvy/pluvy.json` and
10
+ reports findings without fixing anything: no mode modifies, stashes, stages,
11
+ restores, deletes, or rewrites source files, and the tool never changes Git
12
+ configuration.
13
+
14
+ ## Decide whether the gate applies
15
+
16
+ Before any automatic post-task run or gate report, check the target repository
17
+ (the explicit `--root`, or the repository being worked on). The gate applies when
18
+ at least one of these is true:
19
+
20
+ - The repository has `.pluvy/pluvy.json`.
21
+ - Project instructions, scripts or CI explicitly require the gate, including a
22
+ policy selected through `--config`. Use that project's root and config path.
23
+ - The user explicitly requests a gate run for this task.
24
+
25
+ A globally installed executable, a discoverable skill, an example policy or a
26
+ skill pointer alone does not enable the gate. Do not run the CLI to discover
27
+ whether the repository has adopted it. If none of the conditions applies, stop
28
+ this gate workflow silently: do not run it, create a policy, suggest setup, or
29
+ include a skipped, missing-config or "not counted as passed" gate notice in the
30
+ task report. Continue the project's own checks normally.
31
+
32
+ Keep missing or invalid configuration distinct from non-adoption. If the project
33
+ requires the gate or the user requests a run, missing configuration remains a
34
+ failure. An invalid or unreadable policy also remains a failure; do not silently
35
+ skip it or change the CLI's exit-code semantics.
36
+
37
+ Use this skill for adoption, installation, configuration, explanation and triage
38
+ when requested, even before adoption. Loading it for those purposes does not by
39
+ itself require a gate run or authorize creating a project policy.
40
+
41
+ ## Gate policy (binding)
42
+
43
+ 1. `After completing a code task in a repository where the gate applies, run the pluvy gate before reporting the task done, and report the mode, exit code and findings truthfully.`
44
+ 2. `When the gate applies, never claim it passed when it was not run, failed with exit code 2, or the tool is not installed; state the reason explicitly instead. When it does not apply, omit gate execution and reporting.`
45
+ 3. `Do not add suppressions or weaken the policy to make the gate appear green; policy changes require a separate full audit and review.`
46
+
47
+ ## Post-task gate commands
48
+
49
+ | Situation | Command | Notes |
50
+ |---|---|---|
51
+ | Task changes are uncommitted (working tree, staged/unstaged/untracked) | `pluvy --base HEAD` | Compares HEAD with the working tree, including untracked files (new files get a full audit) |
52
+ | The task produced commits | `pluvy --base <task start commit>` (the parent of the task's first commit) | On a clean tree, `pluvy --base HEAD` audits 0 files and exits 0 — a silent false pass that must be avoided |
53
+ | The task start commit is unknown | `pluvy --all`, and state that this is a full audit that may include pre-existing debt | Prefer over-reporting to under-reporting |
54
+ | `--staged` | Not used for post-task gating | It only reads index blobs, which does not match the agent workflow boundary |
55
+
56
+ Canonical commands:
57
+
58
+ ```sh
59
+ pluvy --all
60
+ pluvy --staged
61
+ pluvy --base <commit>
62
+ pluvy --root /path/to/project --all
63
+ pluvy --all --format json
64
+ ```
65
+
66
+ `--root` defaults to the current directory (Git modes require the repository
67
+ root); `--config` defaults to `.pluvy/pluvy.json`, resolved relative
68
+ to the root.
69
+
70
+ ## Exit codes
71
+
72
+ | Exit code | Meaning |
73
+ |---|---|
74
+ | `0` | No error-severity findings (warnings do not affect it) |
75
+ | `1` | Error-severity findings are present |
76
+ | `2` | Configuration/encoding/filesystem/Git failure (including a staged policy change, until a separate `--all` full audit passes and the change is committed) |
77
+
78
+ ## Post-run checks
79
+
80
+ 1. The gate output ends with a line like `Architecture base: N files`. If N is 0
81
+ while the task really changed code, the base choice is wrong: reselect the
82
+ base and rerun; do not treat the run as a pass.
83
+ 2. If the task changed the policy file, do not judge the task by a `--base` run
84
+ alone: run a separate `pluvy --all` and have the policy change reviewed.
85
+ 3. Unrelated dirty changes that existed before the task are swept into
86
+ `pluvy --base HEAD` runs: list them truthfully in the report; do not
87
+ filter them silently.
88
+
89
+ ## Report format
90
+
91
+ Apply this section only when the gate applies under the decision above. Report
92
+ the mode you ran, the exit code, and the findings (error/warning counts plus the
93
+ key items). If an applicable gate was not run, exited with code 2, or the tool is
94
+ not installed, state that explicitly — never report the task as gated or passed.
95
+ For a repository where the gate does not apply, omit gate status entirely. Keep
96
+ test-suite execution and its reporting independent of gate applicability.
97
+
98
+ ## Triage
99
+
100
+ 1. Fix the code first: findings are evidence of policy violations.
101
+ 2. Every supported language (Python, Go, Java and the JavaScript family): a
102
+ standalone next-line suppression comment naming the exact rule id is allowed
103
+ only for an exception that received explicit (human) approval beforehand —
104
+ the approval precedes and is independent of the reason; the written reason
105
+ only records that approval. Use the language's own line-comment marker: `#`
106
+ in Python, `//` in Go, Java and the JavaScript family. Block-comment forms,
107
+ wildcards, lists, `type.suppression` itself and a missing reason are invalid
108
+ and report `type.suppression`. Syntax and validation rules:
109
+ [approved next-line diagnostic exceptions](references/comment-suppressions.md).
110
+ 3. Policy changes (thresholds, excludes, owners, boundaries) are architecture
111
+ decisions: they require a separate `--all` full audit and review. Never
112
+ weaken the policy to force a green report (policy item 3).
113
+
114
+ ## Capability boundaries and language support
115
+
116
+ The gate is read-only and parse-based: it parses the selected sources with
117
+ tree-sitter, collects syntax evidence and applies the policy. No mode fixes,
118
+ stashes, stages, restores, deletes or rewrites source files.
119
+
120
+ Six analyzers cover seven languages:
121
+
122
+ | Analyzer | Source suffixes |
123
+ |---|---|
124
+ | Python | `.py`, `.pyi` |
125
+ | Go | `.go` |
126
+ | Java | `.java` |
127
+ | JavaScript and JSX | `.js`, `.jsx`, `.mjs`, `.cjs` |
128
+ | TypeScript | `.ts`, `.mts`, `.cts` |
129
+ | TSX | `.tsx` |
130
+
131
+ Every checker is enabled by default for every selected file. `enabled: false`
132
+ turns one checker off for all selected files; there is no `enabled` flag scoped
133
+ to one language or one file. The global `include` and `exclude` lists drop files
134
+ from every checker at once, except that changed snapshot paths reported by the
135
+ run-level `test.deletion` gate follow the changed set instead (see [its checker
136
+ page](references/checkers/test.deletion.md)). Architecture `placements`,
137
+ `boundaries` and `paths` only select where that one architecture check applies.
138
+ `owners` is not a file selector: once owners are configured and the checker is
139
+ enabled, every selected
140
+ file must match exactly one owner, so an unmatched or doubly matched file is a
141
+ finding. Coverage differences therefore come from the diagnostics each analyzer
142
+ registers.
143
+
144
+ What the gate does not do:
145
+
146
+ - It does not infer types. Broad markers, annotations and conversions are
147
+ syntactic evidence, not resolved types or compiler proofs.
148
+ - It does not resolve package aliases, build-system aliases, dynamic imports,
149
+ reflection or computed members, and it does not prove runtime ownership or
150
+ which implementation a call reaches.
151
+ - It does not detect all unused code, prove validators or logging safety, or
152
+ measure coverage. Unused findings describe the checked snapshot view and
153
+ unresolved external usage, not a repository-wide proof.
154
+ - It is not a linter, formatter, type checker or coverage tool. Keep the
155
+ language formatter, type checker, tests, branch coverage and semantic
156
+ architecture review as separate required checks.
157
+ - JavaScript and JSX have no type annotations to check: type safety needs a
158
+ configured type-aware checker, even though usage, boundary and encoding
159
+ analysis still run on those files.
160
+ - A passing syntax gate does not certify compliance with every policy rule.
161
+
162
+ ## Checkers and per-language coverage
163
+
164
+ The schema has 22 logical checkers, which produce 29 built-in diagnostic IDs.
165
+ `function_size` owns the five `size.*` IDs, `test.integrity` owns
166
+ `test.tautology` and `test.vacuous`, `flow.handling` owns `flow.swallow` and
167
+ `flow.fallback`, and `type.contract` owns `type.nullable` and `type.union`;
168
+ the run-level `test.deletion` owns its single diagnostic outside analyzer
169
+ registration, and every other checker name matches its diagnostic ID.
170
+
171
+ | Checker | Reports | Languages |
172
+ |---|---|---|
173
+ | [`structure.guarded_projection`](references/checkers/structure.guarded_projection.md) | Repeated guarded field copies in one local region (three distinct destination fields by default) | All |
174
+ | [`function_size`](references/checkers/function_size.md) | Function lines, statements, complexity, nesting and parameters; one switch for five diagnostics | All |
175
+ | [`test.integrity`](references/checkers/test.integrity.md) | Tautological assertions and tests without a meaningful assertion; one switch for two diagnostics | All |
176
+ | [`test.deletion`](references/checkers/test.deletion.md) | Removed test files, assertion erosion and changed snapshot paths in changed audits; one run-level diagnostic with error and warning forms | All; run-level |
177
+ | [`flow.handling`](references/checkers/flow.handling.md) | Swallowed exception handlers and long `??` chains whose defaults are all bare literals; one switch for two diagnostics | Python, Java, JavaScript, JSX, TypeScript, TSX |
178
+ | [`type.contract`](references/checkers/type.contract.md) | Aggregated nullable declarations, bare nullish fallbacks and oversized unions; one switch for two diagnostics | TypeScript, TSX |
179
+ | [`type.annotation`](references/checkers/type.annotation.md) | Broad or bare Python annotations and invalid forward annotations | Python |
180
+ | [`type.signature`](references/checkers/type.signature.md) | Missing Python parameter and return annotations | Python |
181
+ | [`type.broad`](references/checkers/type.broad.md) | Recognized broad type markers and JSON serialization outside proven outputs and permitted library inputs | All; markers differ per language |
182
+ | [`type.boundary`](references/checkers/type.boundary.md) | Recognized library results and callback inputs used without typed conversion | All |
183
+ | [`unused.parameter`](references/checkers/unused.parameter.md) | Parameters without resolved read usage | All |
184
+ | [`unused.function`](references/checkers/unused.function.md) | Non-exported functions and methods without resolved references | All |
185
+ | [`unused.field`](references/checkers/unused.field.md) | Fields without reads; initialization and writes alone do not count | All |
186
+ | [`unused.return`](references/checkers/unused.return.md) | Known non-void results discarded at a call site; always a warning | All |
187
+ | [`type.assertion`](references/checkers/type.assertion.md) | Unchecked casts and assertions | Python, Java, TypeScript, TSX |
188
+ | [`type.suppression`](references/checkers/type.suppression.md) | Foreign suppression directives and malformed or unused next-line exceptions | All |
189
+ | [`imports.wildcard`](references/checkers/imports.wildcard.md) | Python wildcard imports | Python |
190
+ | [`architecture.owner`](references/checkers/architecture.owner.md) | Missing or overlapping responsibility owners | All |
191
+ | [`architecture.structure`](references/checkers/architecture.structure.md) | Selected modules outside their configured owning locations | All |
192
+ | [`architecture.import`](references/checkers/architecture.import.md) | Dependencies forbidden by configured boundaries | All |
193
+ | [`architecture.dispatch`](references/checkers/architecture.dispatch.md) | Language and rule identity comparison or match/switch dispatch | All |
194
+ | [`architecture.object_model`](references/checkers/architecture.object_model.md) | Non-underscore module-level functions in configured extension paths; Go reports unexported lowercase package-level functions too, receiver methods and Java are not reported | Python, Go, JavaScript, TypeScript, TSX |
195
+
196
+ Every analyzer registers the same base set of 19 diagnostic IDs:
197
+ `unused.parameter`, `unused.function`, `unused.field`, `unused.return`,
198
+ `type.boundary`, `type.suppression`, `size.lines`, `size.statements`,
199
+ `size.complexity`, `size.nesting`, `size.parameters`, `architecture.owner`,
200
+ `architecture.structure`, `architecture.import`, `architecture.dispatch`,
201
+ `architecture.object_model`, `structure.guarded_projection`, `test.tautology`
202
+ and `test.vacuous`.
203
+
204
+ `test.deletion` is not part of that per-language set: no analyzer registers it,
205
+ and it is produced by the run-level deletion gate only in `--base` and `--staged`
206
+ mode.
207
+
208
+ The remaining nine diagnostic IDs are registered per language, and a registered
209
+ diagnostic can only report for the analyzer that registered it:
210
+
211
+ | Analyzer | Additional diagnostic IDs |
212
+ |---|---|
213
+ | Python | `type.annotation`, `type.signature`, `type.assertion`, `type.broad`, `imports.wildcard`, `flow.swallow` |
214
+ | Go | `type.broad` |
215
+ | Java | `type.assertion`, `type.broad`, `flow.swallow` |
216
+ | JavaScript and JSX | `type.broad`, `flow.swallow`, `flow.fallback` |
217
+ | TypeScript and TSX | `type.assertion`, `type.broad`, `type.nullable`, `type.union`, `flow.swallow`, `flow.fallback` |
218
+
219
+ Key differences:
220
+
221
+ - `type.annotation`, `type.signature` and `imports.wildcard` appear in Python
222
+ files only.
223
+ - `type.assertion` covers Python, Java, TypeScript and TSX, but not Go or
224
+ JavaScript.
225
+ - `type.nullable` and `type.union` appear in TypeScript and TSX files only.
226
+ - `flow.swallow` covers Python, Java and the JavaScript family; `flow.fallback`
227
+ appears in JavaScript, JSX, TypeScript and TSX files only.
228
+ - `unused.return` is always a warning, snapshot-only `test.vacuous` findings are
229
+ warnings, and `test.deletion` reports a changed snapshot path as a warning;
230
+ every other diagnostic reports errors. Warnings never change the exit code.
231
+ - `architecture.object_model` is registered by every analyzer, but only
232
+ grammars with module-level functions can report. Go is included: a
233
+ package-level function whose name does not start with `_` is reported, so an
234
+ unexported `func check(...)` is a finding — the test is the leading underscore,
235
+ not export status. Go receiver methods and Java files produce no findings.
236
+ - `type.broad` markers differ per language: `typing.Any`,
237
+ `typing_extensions.Any` and `pydantic.JsonValue` references plus internal
238
+ `json.dumps` results in Python; `any`, empty interfaces and imported
239
+ `encoding/json` results in Go; `java.lang.Object`, unbounded wildcards and
240
+ recognized raw collections plus Gson/Jackson results in Java; `JSON.stringify`
241
+ results in JavaScript and JSX; `any`, `unknown`, `object` and empty object
242
+ types plus `JSON.stringify` results in TypeScript and TSX.
243
+ - One Python annotation can report twice: `typing.Any` triggers both
244
+ `type.annotation` and `type.broad`.
245
+
246
+ Coverage does not promise complete evidence for every construct. The resolution
247
+ limits for boundaries, usage, encodings and guarded projections are recorded on
248
+ the corresponding checker pages and remain review obligations.
249
+
250
+ Full detail for every checker — role, language differences, options, defaults,
251
+ types and correct/incorrect configuration — is on its own page linked from the
252
+ [checker index](references/checkers.md).
253
+
254
+ ## Checker configuration
255
+
256
+ Each repository commits its policy at `.pluvy/pluvy.json`; the gate
257
+ never changes it. The policy separates global file selection from checker
258
+ options:
259
+
260
+ ```json
261
+ {
262
+ "version": 2,
263
+ "include": ["src/*", "tests/*"],
264
+ "exclude": [],
265
+ "third_party_modules": [],
266
+ "checkers": {
267
+ "function_size": {"lines": 40},
268
+ "architecture.owner": {
269
+ "owners": [{"name": "domain", "paths": ["src/domain/*"]}]
270
+ }
271
+ }
272
+ }
273
+ ```
274
+
275
+ Rules that apply to every checker:
276
+
277
+ - `enabled` is a strict boolean, defaulting to `true`. JSON `true` or `false`
278
+ only: `"false"`, `0` and `null` are errors. Omit `enabled: true` and entries
279
+ that only repeat defaults.
280
+ - Unknown checker IDs and unknown options are errors, including when the checker
281
+ is disabled. The same rejection applies inside nested option objects.
282
+ - `function_size` is one checker with one switch: it owns `size.lines`,
283
+ `size.statements`, `size.complexity`, `size.nesting` and `size.parameters`.
284
+ Suppression comments name those diagnostic IDs, not the checker key.
285
+ - `include` and `exclude` are the only global file selectors and must stay at the
286
+ top level. Architecture `placements`, `boundaries` and `paths` select where
287
+ that one architecture check applies, not which files other checkers read.
288
+ `owners` instead declares the required responsibility mapping: when it is
289
+ configured and the checker is enabled, every selected file must match exactly
290
+ one owner.
291
+ - `third_party_modules` is a top-level list of exact import specifiers. It grants
292
+ a directional library-input exception for erased dictionaries, not a file or
293
+ checker exemption.
294
+ - `type.broad.encoding` holds four lists, all empty by default: `encoders`,
295
+ `decoders`, `outputs` and `instance_outputs`. They extend built-in
296
+ serialization recognition; they never exempt broad annotations, casts or
297
+ incoming library results.
298
+
299
+ ```json
300
+ {
301
+ "version": 2,
302
+ "third_party_modules": ["vendor.client"],
303
+ "checkers": {
304
+ "structure.guarded_projection": {"min_fields": 4},
305
+ "type.broad": {
306
+ "encoding": {
307
+ "encoders": ["vendor.client.encode"],
308
+ "outputs": [{"call": "vendor.client.publish", "arguments": [0]}]
309
+ }
310
+ }
311
+ }
312
+ }
313
+ ```
314
+
315
+ An invalid policy, rejected before any file is checked:
316
+
317
+ ```json
318
+ {
319
+ "version": 2,
320
+ "checkers": {
321
+ "function_size": {"enabled": "false"},
322
+ "type.broad": {"maximum": 3},
323
+ "syntax.invalid": {"enabled": false}
324
+ }
325
+ }
326
+ ```
327
+
328
+ The string flag fails the strict boolean, `maximum` is not a `type.broad`
329
+ option, and `syntax.invalid` is not a checker. Malformed configuration fails
330
+ closed with exit code 2.
331
+
332
+ Thresholds, excludes, owners, boundaries and enable flags are architecture
333
+ decisions. Changing any of them requires a separate full audit and review, and a
334
+ staged policy change stays blocked until that audit passes and the change is
335
+ committed. Disabling a checker or raising a limit is never remediation. The full
336
+ option list, defaults, types and examples for each checker are on the page
337
+ linked from the [checker index](references/checkers.md).
338
+
339
+ ## Pointing other agents at this skill
340
+
341
+ For agents that take their instructions from a repository instruction file
342
+ (such as AGENTS.md), paste this pointer into that file (manual paste only; do
343
+ not auto-rewrite a user's instruction file):
344
+
345
+ ```markdown
346
+ <!-- pluvy skill pointer -->
347
+ When adopting, configuring, or running pluvy, or triaging its findings,
348
+ first read `.agents/skills/pluvy/SKILL.md` and follow it.
349
+ ```
350
+
351
+ ## References
352
+
353
+ - [Agent integration notes](references/agent-integration.md) — integration
354
+ paths for any agent and the limits that apply.
355
+ - [Checker index](references/checkers.md) — the 22 checkers and the 29
356
+ diagnostic IDs, with coverage tables and one page per checker for its role,
357
+ language differences, options, defaults, types and configuration examples.
358
+ - [Approved next-line diagnostic exceptions](references/comment-suppressions.md)
359
+ — the suppression directive syntax, validation rules and their snapshot scope.
360
+ - [Policy sample](references/policy-sample.md) — a compact policy covering file
361
+ selection, owners, import boundaries and function-size thresholds.
@@ -0,0 +1,75 @@
1
+ # Agent integration notes — pluvy skill
2
+
3
+ How a consuming repository connects an agent to this skill, and the limits
4
+ that apply. Anchors in these notes point inside the skill directory; bare file
5
+ names such as `README.md` name files of the pluvy source repository for
6
+ reference only, not resolvable anchors; no network location is assumed.
7
+
8
+ ## Generic statement (any agent)
9
+
10
+ This skill is a plain directory with a `SKILL.md` in Agent Skills format. Any
11
+ agent that implements the Agent Skills directory convention can discover it
12
+ under `.agents/skills/` (project level, found from the working directory up
13
+ through its ancestors) or `~/.agents/skills/` (user level). Any agent that can
14
+ read files and run shell commands can also use it without a discovery
15
+ mechanism: read `.agents/skills/pluvy/SKILL.md` from the consuming
16
+ repository and follow it, for example through the AGENTS.md pointer snippet in
17
+ SKILL.md. Nothing here promises that a specific tool will discover or load the
18
+ skill on its own; the concrete integration steps are up to each agent.
19
+
20
+ ## Repository adoption and automatic runs
21
+
22
+ Skill discovery is separate from repository adoption. Installing this skill at
23
+ user level makes it available across repositories; it does not require every
24
+ repository to run the gate. Follow [the applicability decision](../SKILL.md#decide-whether-the-gate-applies)
25
+ before automatic execution or reporting. A pointer to this skill is a reading
26
+ instruction, not a project requirement to run the gate.
27
+
28
+ | Repository or task context | Expected agent behavior |
29
+ |---|---|
30
+ | Global executable or skill only; no project policy, requirement or explicit run request | No gate command and no gate notice in the task report |
31
+ | Default project policy exists | Run the post-task gate and report its actual result |
32
+ | Project instructions, scripts or CI explicitly require the gate via a custom `--config` | Use the declared root and config, even without the default policy |
33
+ | Project requires the gate but its policy is missing | Report the configuration failure; do not treat it as non-adoption |
34
+ | Policy is malformed or unreadable | Report the failure; preserve exit code 2 |
35
+ | User explicitly requests a run without a policy | Attempt the requested run and report the configuration failure |
36
+ | User asks about capabilities before adoption | Answer the question without automatically running the gate or creating a policy |
37
+
38
+ Do not create a policy to silence a post-task warning. Adoption is a separate
39
+ requested task. Repository tests, linting and type checks retain their own
40
+ requirements regardless of whether this gate applies.
41
+
42
+ ## Adapting agent-specific skill directories
43
+
44
+ Some agents load skills only from their own directory. Where that directory
45
+ lives decides the form:
46
+
47
+ - Outside the audited repository root (`--root`), for example a user-level or
48
+ agent-specific skills directory, a symlink keeps a single source of truth:
49
+
50
+ ```sh
51
+ ln -s /absolute/path/to/repo/.agents/skills/pluvy <agent-skills-dir>/pluvy
52
+ ```
53
+
54
+ - Inside the repository root, copy the directory instead; never adapt with a
55
+ directory symlink there. The full audit `--all` refuses to traverse any
56
+ directory symlink under the root and exits 2, and the refusal happens
57
+ during enumeration, before include/exclude filtering — no exclude pattern
58
+ bypasses it. The Git-based modes do not enumerate the disk, which hides the
59
+ damage: on an unchanged tree `pluvy --base HEAD` still exits 0 with
60
+ a 0-files report while every full audit (bootstrap, policy re-review, and
61
+ the D12 fallback row) is permanently broken; a `--base` run with changed
62
+ files exits 2 as soon as usage evidence is read from disk. Treat the copy
63
+ as a drifting duplicate: re-copy after every update of the source,
64
+ otherwise the two locations diverge silently. Windows systems without
65
+ usable symlinks use the copy form everywhere.
66
+
67
+ ## Team / CI usage
68
+
69
+ Any CI system that can run the gate container image integrates with the same
70
+ commands used locally. The pluvy repository itself runs the same
71
+ engineering checks in GitHub Actions (`.github/workflows/engineering.yml`).
72
+ A pre-commit hook can run `uv run --no-sync pluvy --staged` once the
73
+ policy file is committed; the hook must be installed explicitly, because the
74
+ tool never changes Git configuration. The `README.md` integration section
75
+ carries the background.
@@ -0,0 +1,122 @@
1
+ # Checker page template
2
+
3
+ This file is the template for a new checker page in this directory. `TEMPLATE.md`
4
+ is the only non-checker file under `checkers/`: every other page is named after a
5
+ checker ID and linked from the checker index. Copy it to
6
+ `checkers/<checker-id>.md`, fill in the placeholders, and then work through the
7
+ checklist at the end of this file.
8
+
9
+ ## Format rules
10
+
11
+ Every checker page uses the same language format. The rules below are the
12
+ contract; a page that breaks one of them is a documentation defect.
13
+
14
+ 1. The **Languages** line carries the scope in one sentence only. It may end with
15
+ one short guiding phrase that announces the difference table, and it never
16
+ states behavioral facts.
17
+ 2. Per-language differences are graded by length. A difference of three or more
18
+ sentences, or one that needs a code example, becomes a `#### <Language>`
19
+ section. A shorter difference preferably becomes one row of a
20
+ `| Language | <column> |` table; existing pages keep their frozen forms.
21
+ Count sentences by their closing period: a language name prefix does not
22
+ count as a sentence, and semicolons do not split a sentence.
23
+ 3. A language difference never appears as a bullet list — neither as standalone
24
+ bullets nor inside a table cell or a `####` section.
25
+ 4. The same fact appears in exactly one carrier per page: a table row, a `####`
26
+ section, or common prose, never two of them.
27
+ 5. Common rules — behavior that is language-independent, or every behavioral
28
+ detail of a single-language page — is ordinary prose outside the language
29
+ block, that is, outside the **Languages** line, the difference table and the
30
+ `####` sections.
31
+ 6. A page contains a `## Configuration` heading if and only if it contains a
32
+ `####` language section. `## Configuration` sits immediately before the
33
+ **Options** line and introduces the configuration content that runs from
34
+ **Options** to the end of the file. Pages without a `####` section have no
35
+ `## Configuration` heading.
36
+
37
+ ## Skeleton
38
+
39
+ Copy the block below and replace every `<...>` placeholder. The inner fences are
40
+ shorter than the outer one so the skeleton can be copied verbatim.
41
+
42
+ ````markdown
43
+ # `<checker-id>`
44
+
45
+ [Back to the checker index](../checkers.md)
46
+
47
+ Reports <one sentence stating what the checker reports>.
48
+
49
+ **Severity**: <error or warning>.
50
+
51
+ **Languages**: <one scope sentence naming the analyzers or languages that register the checker>.<Optional guiding phrase that announces the table below:>
52
+
53
+ | Language | <Difference column named after the content> |
54
+ | --- | --- |
55
+ | <Language> | <one or two sentences unique to this language> |
56
+
57
+ <Common rules that hold for every language, as ordinary prose outside the
58
+ language block.>
59
+
60
+ #### <Language>
61
+
62
+ <Three or more sentences, or a code example, that only apply to this language.
63
+ Delete this section and its `## Configuration` heading when no language needs
64
+ one.>
65
+
66
+ ## Configuration
67
+
68
+ **Options**: <each option with its type and default value, or "None beyond
69
+ `enabled` (boolean, default `true`).">.
70
+
71
+ Correct:
72
+
73
+ ```json
74
+ {
75
+ "checkers": {
76
+ "<checker-id>": {}
77
+ }
78
+ }
79
+ ```
80
+
81
+ Incorrect — <the reason this configuration is rejected>:
82
+
83
+ ```json
84
+ {
85
+ "checkers": {
86
+ "<checker-id>": {"<unknown-option>": true}
87
+ }
88
+ }
89
+ ```
90
+
91
+ <What the checker does not prove and where review remains necessary, as the
92
+ closing prose of the page.>
93
+ ````
94
+
95
+ The skeleton is correct as copied: the title keeps the checker ID in backticks
96
+ and the back link points at `../checkers.md`, so a copied page already resolves
97
+ its links and satisfies the contract that each page names its own checker.
98
+
99
+ ## Checklist for adding a checker
100
+
101
+ 1. Copy the skeleton to `checkers/<checker-id>.md` and fill it in. Keep the
102
+ checker ID in backticks in the page title: the guidance contract test
103
+ `test_checker_document_names_its_own_checker` asserts that backticked ID on
104
+ every listed page.
105
+ 2. Add a row to the `## Checker index` table in `checkers.md`, and update the
106
+ `## Coverage` section of the same file with the analyzers that register the
107
+ new diagnostic.
108
+ 3. Update `.agents/skills/pluvy/SKILL.md` in the same batch: the checker
109
+ table, the logical-checker and diagnostic-ID count sentence, and the
110
+ base-set diagnostic-ID list.
111
+ 4. Update the explicit lists in `tests/test_guidance_contract.py` — `CHECKER_IDS`
112
+ and `DIAGNOSTIC_IDS`; the per-page link arguments are derived from the ID set.
113
+ Missing this step fails no test: it only silently drops the contract
114
+ protection for the new page.
115
+ 5. Keep the Correct example consistent with the policy schema in
116
+ `src/pluvy/policy/` and with `docs/checker-configuration.md`; the index,
117
+ the checker pages and `docs/checker-configuration.md` change in the same batch.
118
+ 6. Write the checker ID in backticks wherever it appears as a name, including the
119
+ index table.
120
+
121
+ Adding the checker itself is a `src/` and `tests/` change, which is outside the
122
+ scope of this documentation set and needs its own task.
@@ -0,0 +1,42 @@
1
+ # `architecture.dispatch`
2
+
3
+ [Back to the checker index](../checkers.md)
4
+
5
+ Reports language and rule identity dispatch: comparisons of selectors named
6
+ `language` or `rule_id` with literal identities or uppercase member constants,
7
+ and `match`/`switch` statements on those selectors.
8
+
9
+ **Severity**: error.
10
+
11
+ **Languages**: every analyzer, using each language's own comparison and switch
12
+ syntax. One Java difference:
13
+
14
+ | Language | Difference |
15
+ | --- | --- |
16
+ | Java | additionally recognizes `switch` expressions and `equals` comparisons on those selectors, with enum constants. |
17
+
18
+ Comparing two runtime identities for compatibility is allowed. The check cannot
19
+ detect every alias or indirect dispatch, so cohesion and interface conformance
20
+ remain review obligations.
21
+
22
+ **Options**: None beyond `enabled` (boolean, default `true`).
23
+
24
+ Correct:
25
+
26
+ ```json
27
+ {
28
+ "checkers": {
29
+ "architecture.dispatch": {"enabled": false}
30
+ }
31
+ }
32
+ ```
33
+
34
+ Incorrect — unknown option:
35
+
36
+ ```json
37
+ {
38
+ "checkers": {
39
+ "architecture.dispatch": {"selectors": ["language"]}
40
+ }
41
+ }
42
+ ```
@@ -0,0 +1,56 @@
1
+ # `architecture.import`
2
+
3
+ [Back to the checker index](../checkers.md)
4
+
5
+ Reports dependencies forbidden by the configured boundaries.
6
+
7
+ **Severity**: error.
8
+
9
+ **Languages**: every analyzer; selectors use language-appropriate names.
10
+
11
+ | Language | Selector rules |
12
+ | --- | --- |
13
+ | Python and Go | Python and Go boundaries use module-name patterns, and Python relative imports are normalized using their source directory. |
14
+ | JavaScript and TypeScript | JavaScript and TypeScript boundaries use module specifiers or normalized relative path patterns, with and without source extensions, and relative imports also match root-relative paths. |
15
+ | Java | Java imports use qualified type and member names, including static imports and wildcard suffixes. |
16
+
17
+ Type-only imports, re-exports, literal `require()` and literal `import()` are
18
+ checked. Include both a package itself and its descendants where needed. All
19
+ matching boundaries apply. Dynamic imports, escaped module strings and build
20
+ aliases still need language-aware review.
21
+
22
+ **Options**: `enabled` (boolean, default `true`); `boundaries` (list, default
23
+ empty). Each boundary is `{"name": ..., "sources": [...], "forbidden_imports":
24
+ [...]}` with a non-empty name and at least one source and one forbidden pattern.
25
+ Ownership does not infer dependency permissions, and an empty boundary list
26
+ provides no project-specific facts to enforce.
27
+
28
+ Correct:
29
+
30
+ ```json
31
+ {
32
+ "checkers": {
33
+ "architecture.import": {
34
+ "boundaries": [
35
+ {
36
+ "name": "domain does not depend on adapters",
37
+ "sources": ["src/domain/*"],
38
+ "forbidden_imports": ["adapters", "adapters.*"]
39
+ }
40
+ ]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Incorrect — a boundary without forbidden imports:
47
+
48
+ ```json
49
+ {
50
+ "checkers": {
51
+ "architecture.import": {
52
+ "boundaries": [{"name": "domain", "sources": ["src/domain/*"]}]
53
+ }
54
+ }
55
+ }
56
+ ```