@terpjs/spec 0.14.0 → 0.15.0

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 (392) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +417 -417
  3. package/VERSION +1 -1
  4. package/app-check-report.schema.json +122 -122
  5. package/assurance-profile.schema.json +70 -70
  6. package/catalog/backend/alembic_downgrades_not_empty.json +22 -22
  7. package/catalog/backend/base_query_not_overridden.json +27 -27
  8. package/catalog/backend/canonical_module_shape.json +22 -22
  9. package/catalog/backend/datetime_columns_are_timezone_aware.json +23 -23
  10. package/catalog/backend/escape_hatch_budget.json +21 -21
  11. package/catalog/backend/events_reference_catalog.json +27 -27
  12. package/catalog/backend/input_schemas_exclude_managed_columns.json +27 -27
  13. package/catalog/backend/input_str_fields_have_max_length.json +22 -22
  14. package/catalog/backend/jobs_reference_catalog.json +27 -27
  15. package/catalog/backend/list_routes_paginate.json +32 -32
  16. package/catalog/backend/modules_declare_policy.json +26 -26
  17. package/catalog/backend/mutations_emit_audit.json +26 -26
  18. package/catalog/backend/mutations_require_write_role.json +32 -32
  19. package/catalog/backend/no_adhoc_background_runtime.json +22 -22
  20. package/catalog/backend/no_adhoc_config_decrypt.json +27 -27
  21. package/catalog/backend/no_adhoc_logging_config.json +22 -22
  22. package/catalog/backend/no_adhoc_middleware.json +27 -27
  23. package/catalog/backend/no_adhoc_permission_literals.json +27 -27
  24. package/catalog/backend/no_app_instantiation.json +22 -22
  25. package/catalog/backend/no_blocking_sleep.json +22 -22
  26. package/catalog/backend/no_cross_module_imports.json +21 -21
  27. package/catalog/backend/no_dependency_overrides.json +27 -27
  28. package/catalog/backend/no_destructive_migrations.json +22 -22
  29. package/catalog/backend/no_dynamic_sql.json +22 -22
  30. package/catalog/backend/no_empty_tests.json +22 -22
  31. package/catalog/backend/no_eval_or_exec.json +22 -22
  32. package/catalog/backend/no_hardcoded_credentials.json +21 -21
  33. package/catalog/backend/no_internal_imports.json +22 -22
  34. package/catalog/backend/no_manual_actor_stamping.json +27 -27
  35. package/catalog/backend/no_manual_ownership_checks.json +32 -32
  36. package/catalog/backend/no_manual_scope_filtering.json +27 -27
  37. package/catalog/backend/no_manual_table_schema.json +22 -22
  38. package/catalog/backend/no_manual_version_assignment.json +22 -22
  39. package/catalog/backend/no_mutable_default_args.json +22 -22
  40. package/catalog/backend/no_naive_datetime.json +22 -22
  41. package/catalog/backend/no_oversized_python_files.json +22 -22
  42. package/catalog/backend/no_print.json +22 -22
  43. package/catalog/backend/no_raw_app_routes.json +26 -26
  44. package/catalog/backend/no_raw_connection_access.json +27 -27
  45. package/catalog/backend/no_raw_file_references.json +27 -27
  46. package/catalog/backend/no_raw_outbound_http.json +22 -22
  47. package/catalog/backend/no_raw_session_construction.json +22 -22
  48. package/catalog/backend/no_star_imports.json +22 -22
  49. package/catalog/backend/no_todo_fixme.json +22 -22
  50. package/catalog/backend/no_unique_columns_on_soft_delete_models.json +22 -22
  51. package/catalog/backend/offset_queries_declare_ordering.json +22 -22
  52. package/catalog/backend/path_id_params_are_uuid.json +22 -22
  53. package/catalog/backend/policy_refs_resolve.json +27 -27
  54. package/catalog/backend/public_modules_are_read_only.json +26 -26
  55. package/catalog/backend/reads_use_base_query.json +27 -27
  56. package/catalog/backend/response_model_not_table_model.json +27 -27
  57. package/catalog/backend/routes_declare_response_model.json +27 -27
  58. package/catalog/backend/safe_methods_are_read_only.json +31 -31
  59. package/catalog/backend/schemas_exclude_sensitive_fields.json +32 -32
  60. package/catalog/backend/session_imported_from_sqlmodel.json +22 -22
  61. package/catalog/backend/table_models_use_base_table.json +22 -22
  62. package/catalog/backend/tables_have_migrations.json +27 -27
  63. package/catalog/backend/tenant_scoped_models_use_scoped_service.json +22 -22
  64. package/catalog/backend/ungoverned_escape_hatch.json +21 -21
  65. package/catalog/backend/update_schemas_inherit_base_update_schema.json +32 -32
  66. package/catalog/frontend/escape-hatch.json +21 -21
  67. package/catalog/frontend/generated-client-only.json +26 -26
  68. package/catalog/frontend/layout-contract.json +26 -26
  69. package/catalog/frontend/no-cross-module-imports.json +21 -21
  70. package/catalog/frontend/no-deep-imports.json +25 -25
  71. package/catalog/frontend/no-dom-html-injection.json +22 -22
  72. package/catalog/frontend/no-eval.json +21 -21
  73. package/catalog/frontend/no-inline-styling.json +25 -25
  74. package/catalog/frontend/no-style-imports.json +24 -24
  75. package/catalog/frontend/no-unsafe-href.json +21 -21
  76. package/catalog/frontend/no-unsafe-target-blank.json +21 -21
  77. package/catalog/frontend/router-links.json +22 -22
  78. package/catalog/frontend/token-styled-elements.json +25 -25
  79. package/catalog/schema.json +95 -95
  80. package/corpus/PENDING.json +4 -4
  81. package/corpus/RESIDUALS.json +21 -21
  82. package/corpus/backend/alembic_downgrades_not_empty/compliant-01/modules/notes/migrations/versions/0001_change.py +6 -6
  83. package/corpus/backend/alembic_downgrades_not_empty/violation-01/modules/notes/migrations/versions/0001_change.py +6 -6
  84. package/corpus/backend/base_query_not_overridden/compliant-01/modules/notes/service.py +8 -8
  85. package/corpus/backend/base_query_not_overridden/compliant-02/modules/notes/service.py +8 -8
  86. package/corpus/backend/base_query_not_overridden/violation-01/modules/notes/service.py +8 -8
  87. package/corpus/backend/base_query_not_overridden/violation-02/expected-findings.json +7 -7
  88. package/corpus/backend/base_query_not_overridden/violation-02/modules/notes/service.py +8 -8
  89. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/models.py +6 -6
  90. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/module.py +7 -7
  91. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/router.py +3 -3
  92. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/schemas.py +10 -10
  93. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/service.py +5 -5
  94. package/corpus/backend/canonical_module_shape/violation-01/modules/notes/router.py +3 -3
  95. package/corpus/backend/datetime_columns_are_timezone_aware/compliant-01/modules/notes/models.py +12 -12
  96. package/corpus/backend/datetime_columns_are_timezone_aware/violation-01/modules/notes/models.py +12 -12
  97. package/corpus/backend/datetime_columns_are_timezone_aware/violation-02/modules/notes/models.py +13 -13
  98. package/corpus/backend/datetime_columns_are_timezone_aware/violation-03/modules/notes/models.py +15 -15
  99. package/corpus/backend/escape_hatch_budget/compliant-01/escape-hatch-budget.json +3 -3
  100. package/corpus/backend/escape_hatch_budget/compliant-01/modules/notes/service.py +2 -2
  101. package/corpus/backend/escape_hatch_budget/compliant-02/escape-hatch-budget.json +1 -1
  102. package/corpus/backend/escape_hatch_budget/compliant-02/modules/notes/service.py +4 -4
  103. package/corpus/backend/escape_hatch_budget/violation-01/escape-hatch-budget.json +1 -1
  104. package/corpus/backend/escape_hatch_budget/violation-01/modules/notes/service.py +2 -2
  105. package/corpus/backend/escape_hatch_budget/violation-02/escape-hatch-budget.json +3 -3
  106. package/corpus/backend/escape_hatch_budget/violation-02/modules/notes/service.py +4 -4
  107. package/corpus/backend/events_reference_catalog/compliant-01/modules/notes/module.py +9 -9
  108. package/corpus/backend/events_reference_catalog/violation-01/modules/notes/module.py +8 -8
  109. package/corpus/backend/input_schemas_exclude_managed_columns/compliant-01/modules/notes/schemas.py +6 -6
  110. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/expected-findings.json +7 -7
  111. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/modules/notes/schemas.py +9 -9
  112. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/expected-findings.json +12 -12
  113. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/modules/notes/router.py +14 -14
  114. package/corpus/backend/input_str_fields_have_max_length/compliant-01/modules/notes/schemas.py +4 -4
  115. package/corpus/backend/input_str_fields_have_max_length/violation-01/modules/notes/schemas.py +2 -2
  116. package/corpus/backend/jobs_reference_catalog/compliant-01/modules/notes/service.py +5 -5
  117. package/corpus/backend/jobs_reference_catalog/violation-01/modules/notes/service.py +2 -2
  118. package/corpus/backend/list_routes_paginate/compliant-01/modules/notes/router.py +6 -6
  119. package/corpus/backend/list_routes_paginate/violation-01/modules/notes/router.py +3 -3
  120. package/corpus/backend/modules_declare_policy/compliant-01/modules/notes/module.py +7 -7
  121. package/corpus/backend/modules_declare_policy/violation-01/modules/notes/module.py +6 -6
  122. package/corpus/backend/mutations_emit_audit/compliant-01/modules/notes/service.py +9 -9
  123. package/corpus/backend/mutations_emit_audit/violation-01/modules/notes/service.py +4 -4
  124. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/module.py +7 -7
  125. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/router.py +3 -3
  126. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/module.py +7 -7
  127. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/router.py +3 -3
  128. package/corpus/backend/mutations_require_write_role/violation-01/expected-findings.json +7 -7
  129. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/module.py +7 -7
  130. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/router.py +3 -3
  131. package/corpus/backend/mutations_require_write_role/violation-02/expected-findings.json +7 -7
  132. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/module.py +7 -7
  133. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/router.py +5 -5
  134. package/corpus/backend/mutations_require_write_role/violation-03/expected-findings.json +7 -7
  135. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/module.py +10 -10
  136. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/router.py +3 -3
  137. package/corpus/backend/no_adhoc_background_runtime/compliant-01/modules/notes/service.py +9 -9
  138. package/corpus/backend/no_adhoc_background_runtime/violation-01/modules/notes/service.py +5 -5
  139. package/corpus/backend/no_adhoc_background_runtime/violation-02/modules/notes/service.py +3 -3
  140. package/corpus/backend/no_adhoc_config_decrypt/compliant-01/modules/billing/service.py +2 -2
  141. package/corpus/backend/no_adhoc_config_decrypt/violation-01/modules/billing/service.py +2 -2
  142. package/corpus/backend/no_adhoc_logging_config/compliant-01/modules/notes/service.py +3 -3
  143. package/corpus/backend/no_adhoc_logging_config/violation-01/modules/notes/service.py +3 -3
  144. package/corpus/backend/no_adhoc_middleware/compliant-01/main.py +3 -3
  145. package/corpus/backend/no_adhoc_middleware/violation-01/main.py +4 -4
  146. package/corpus/backend/no_adhoc_middleware/violation-02/main.py +5 -5
  147. package/corpus/backend/no_adhoc_permission_literals/compliant-01/modules/notes/module.py +3 -3
  148. package/corpus/backend/no_adhoc_permission_literals/compliant-02/modules/notes/router.py +5 -5
  149. package/corpus/backend/no_adhoc_permission_literals/violation-01/expected-findings.json +7 -7
  150. package/corpus/backend/no_adhoc_permission_literals/violation-01/modules/notes/module.py +1 -1
  151. package/corpus/backend/no_adhoc_permission_literals/violation-02/expected-findings.json +7 -7
  152. package/corpus/backend/no_adhoc_permission_literals/violation-02/modules/notes/router.py +2 -2
  153. package/corpus/backend/no_adhoc_permission_literals/violation-03/expected-findings.json +12 -12
  154. package/corpus/backend/no_adhoc_permission_literals/violation-03/modules/notes/module.py +10 -10
  155. package/corpus/backend/no_app_instantiation/compliant-01/main.py +3 -3
  156. package/corpus/backend/no_app_instantiation/violation-01/main.py +3 -3
  157. package/corpus/backend/no_blocking_sleep/compliant-01/modules/notes/service.py +2 -2
  158. package/corpus/backend/no_blocking_sleep/violation-01/modules/notes/service.py +5 -5
  159. package/corpus/backend/no_cross_module_imports/compliant-01/modules/a/service.py +1 -1
  160. package/corpus/backend/no_cross_module_imports/violation-01/modules/a/service.py +1 -1
  161. package/corpus/backend/no_cross_module_imports/violation-02/modules/a/service.py +1 -1
  162. package/corpus/backend/no_dependency_overrides/compliant-01/main.py +12 -12
  163. package/corpus/backend/no_dependency_overrides/violation-01/main.py +11 -11
  164. package/corpus/backend/no_destructive_migrations/compliant-01/modules/notes/migrations/versions/0001_change.py +3 -3
  165. package/corpus/backend/no_destructive_migrations/compliant-02/modules/notes/migrations/versions/0001_change.py +8 -8
  166. package/corpus/backend/no_destructive_migrations/violation-01/expected-findings.json +7 -7
  167. package/corpus/backend/no_destructive_migrations/violation-01/modules/notes/migrations/versions/0001_change.py +2 -2
  168. package/corpus/backend/no_destructive_migrations/violation-02/expected-findings.json +7 -7
  169. package/corpus/backend/no_destructive_migrations/violation-02/modules/notes/migrations/versions/0001_change.py +2 -2
  170. package/corpus/backend/no_destructive_migrations/violation-03/expected-findings.json +7 -7
  171. package/corpus/backend/no_destructive_migrations/violation-03/modules/notes/migrations/versions/0001_change.py +3 -3
  172. package/corpus/backend/no_destructive_migrations/violation-04/expected-findings.json +12 -12
  173. package/corpus/backend/no_destructive_migrations/violation-04/modules/notes/migrations/versions/0001_change.py +6 -6
  174. package/corpus/backend/no_destructive_migrations/violation-05/expected-findings.json +12 -12
  175. package/corpus/backend/no_destructive_migrations/violation-05/modules/notes/migrations/versions/0001_change.py +3 -3
  176. package/corpus/backend/no_dynamic_sql/compliant-01/modules/notes/service.py +1 -1
  177. package/corpus/backend/no_dynamic_sql/compliant-02/modules/notes/service.py +20 -20
  178. package/corpus/backend/no_dynamic_sql/violation-01/modules/notes/service.py +2 -2
  179. package/corpus/backend/no_dynamic_sql/violation-02/modules/notes/service.py +2 -2
  180. package/corpus/backend/no_dynamic_sql/violation-03/modules/notes/service.py +7 -7
  181. package/corpus/backend/no_dynamic_sql/violation-04/modules/notes/service.py +16 -16
  182. package/corpus/backend/no_empty_tests/compliant-01/tests/test_notes.py +2 -2
  183. package/corpus/backend/no_empty_tests/violation-01/tests/test_notes.py +2 -2
  184. package/corpus/backend/no_eval_or_exec/compliant-01/modules/notes/service.py +2 -2
  185. package/corpus/backend/no_eval_or_exec/violation-01/modules/notes/service.py +2 -2
  186. package/corpus/backend/no_hardcoded_credentials/compliant-01/modules/billing/service.py +3 -3
  187. package/corpus/backend/no_hardcoded_credentials/compliant-02/modules/billing/service.py +18 -18
  188. package/corpus/backend/no_hardcoded_credentials/violation-01/expected-findings.json +7 -7
  189. package/corpus/backend/no_hardcoded_credentials/violation-01/modules/billing/service.py +2 -2
  190. package/corpus/backend/no_hardcoded_credentials/violation-02/expected-findings.json +7 -7
  191. package/corpus/backend/no_hardcoded_credentials/violation-02/modules/billing/service.py +1 -1
  192. package/corpus/backend/no_hardcoded_credentials/violation-03/expected-findings.json +22 -22
  193. package/corpus/backend/no_hardcoded_credentials/violation-03/modules/billing/service.py +11 -11
  194. package/corpus/backend/no_hardcoded_credentials/violation-04/expected-findings.json +12 -12
  195. package/corpus/backend/no_hardcoded_credentials/violation-04/modules/billing/service.py +8 -8
  196. package/corpus/backend/no_hardcoded_credentials/violation-05/expected-findings.json +12 -12
  197. package/corpus/backend/no_hardcoded_credentials/violation-05/modules/billing/service.py +4 -4
  198. package/corpus/backend/no_internal_imports/compliant-01/modules/notes/service.py +1 -1
  199. package/corpus/backend/no_internal_imports/violation-01/modules/notes/service.py +1 -1
  200. package/corpus/backend/no_manual_actor_stamping/compliant-01/modules/notes/service.py +5 -5
  201. package/corpus/backend/no_manual_actor_stamping/compliant-02/modules/notes/schemas.py +12 -12
  202. package/corpus/backend/no_manual_actor_stamping/violation-01/modules/notes/service.py +3 -3
  203. package/corpus/backend/no_manual_actor_stamping/violation-02/expected-findings.json +7 -7
  204. package/corpus/backend/no_manual_actor_stamping/violation-02/modules/notes/service.py +4 -4
  205. package/corpus/backend/no_manual_ownership_checks/compliant-01/modules/journals/service.py +3 -3
  206. package/corpus/backend/no_manual_ownership_checks/compliant-02/modules/journals/schemas.py +11 -11
  207. package/corpus/backend/no_manual_ownership_checks/violation-01/modules/journals/service.py +3 -3
  208. package/corpus/backend/no_manual_ownership_checks/violation-02/expected-findings.json +7 -7
  209. package/corpus/backend/no_manual_ownership_checks/violation-02/modules/journals/service.py +9 -9
  210. package/corpus/backend/no_manual_ownership_checks/violation-03/expected-findings.json +7 -7
  211. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/jobs.py +1 -1
  212. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/models.py +7 -7
  213. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/module.py +11 -11
  214. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/service.py +7 -7
  215. package/corpus/backend/no_manual_scope_filtering/compliant-01/modules/notes/service.py +8 -8
  216. package/corpus/backend/no_manual_scope_filtering/compliant-02/modules/notes/schemas.py +12 -12
  217. package/corpus/backend/no_manual_scope_filtering/violation-01/modules/notes/service.py +2 -2
  218. package/corpus/backend/no_manual_scope_filtering/violation-02/modules/notes/service.py +2 -2
  219. package/corpus/backend/no_manual_scope_filtering/violation-03/expected-findings.json +7 -7
  220. package/corpus/backend/no_manual_scope_filtering/violation-03/modules/notes/service.py +7 -7
  221. package/corpus/backend/no_manual_table_schema/compliant-01/modules/notes/models.py +6 -6
  222. package/corpus/backend/no_manual_table_schema/violation-01/modules/notes/models.py +8 -8
  223. package/corpus/backend/no_manual_version_assignment/compliant-01/modules/notes/service.py +5 -5
  224. package/corpus/backend/no_manual_version_assignment/violation-01/modules/notes/service.py +3 -3
  225. package/corpus/backend/no_manual_version_assignment/violation-02/modules/notes/service.py +3 -3
  226. package/corpus/backend/no_manual_version_assignment/violation-03/modules/notes/service.py +3 -3
  227. package/corpus/backend/no_mutable_default_args/compliant-01/modules/notes/service.py +3 -3
  228. package/corpus/backend/no_mutable_default_args/violation-01/modules/notes/service.py +2 -2
  229. package/corpus/backend/no_naive_datetime/compliant-01/modules/notes/service.py +2 -2
  230. package/corpus/backend/no_naive_datetime/violation-01/modules/notes/service.py +2 -2
  231. package/corpus/backend/no_naive_datetime/violation-02/modules/notes/service.py +2 -2
  232. package/corpus/backend/no_oversized_python_files/compliant-01/modules/notes/service.py +9 -9
  233. package/corpus/backend/no_oversized_python_files/violation-01/modules/notes/service.py +530 -530
  234. package/corpus/backend/no_print/compliant-01/modules/notes/service.py +7 -7
  235. package/corpus/backend/no_print/violation-01/modules/notes/service.py +2 -2
  236. package/corpus/backend/no_raw_app_routes/compliant-01/main.py +12 -12
  237. package/corpus/backend/no_raw_app_routes/violation-01/main.py +10 -10
  238. package/corpus/backend/no_raw_app_routes/violation-02/main.py +16 -16
  239. package/corpus/backend/no_raw_app_routes/violation-03/main.py +12 -12
  240. package/corpus/backend/no_raw_app_routes/violation-04/main.py +10 -10
  241. package/corpus/backend/no_raw_app_routes/violation-05/main.py +12 -12
  242. package/corpus/backend/no_raw_app_routes/violation-06/main.py +10 -10
  243. package/corpus/backend/no_raw_connection_access/compliant-01/modules/notes/service.py +5 -5
  244. package/corpus/backend/no_raw_connection_access/violation-01/modules/notes/service.py +2 -2
  245. package/corpus/backend/no_raw_connection_access/violation-02/modules/notes/service.py +2 -2
  246. package/corpus/backend/no_raw_file_references/compliant-01/modules/notes/models.py +10 -10
  247. package/corpus/backend/no_raw_file_references/violation-01/modules/notes/models.py +9 -9
  248. package/corpus/backend/no_raw_outbound_http/compliant-01/modules/notes/service.py +2 -2
  249. package/corpus/backend/no_raw_outbound_http/compliant-03/modules/notes/service.py +12 -12
  250. package/corpus/backend/no_raw_outbound_http/violation-01/expected-findings.json +7 -7
  251. package/corpus/backend/no_raw_outbound_http/violation-01/modules/notes/service.py +1 -1
  252. package/corpus/backend/no_raw_outbound_http/violation-02/expected-findings.json +7 -7
  253. package/corpus/backend/no_raw_outbound_http/violation-02/modules/notes/service.py +1 -1
  254. package/corpus/backend/no_raw_outbound_http/violation-03/expected-findings.json +7 -7
  255. package/corpus/backend/no_raw_outbound_http/violation-03/modules/notes/service.py +1 -1
  256. package/corpus/backend/no_raw_outbound_http/violation-04/expected-findings.json +12 -12
  257. package/corpus/backend/no_raw_outbound_http/violation-04/modules/notes/service.py +7 -7
  258. package/corpus/backend/no_raw_outbound_http/violation-05/expected-findings.json +12 -12
  259. package/corpus/backend/no_raw_outbound_http/violation-05/modules/notes/service.py +5 -5
  260. package/corpus/backend/no_raw_outbound_http/violation-06/expected-findings.json +12 -12
  261. package/corpus/backend/no_raw_outbound_http/violation-06/modules/notes/service.py +2 -2
  262. package/corpus/backend/no_raw_session_construction/compliant-01/modules/notes/service.py +3 -3
  263. package/corpus/backend/no_raw_session_construction/violation-01/modules/notes/service.py +3 -3
  264. package/corpus/backend/no_star_imports/compliant-01/modules/notes/service.py +1 -1
  265. package/corpus/backend/no_star_imports/violation-01/modules/notes/service.py +1 -1
  266. package/corpus/backend/no_todo_fixme/compliant-01/modules/notes/service.py +3 -3
  267. package/corpus/backend/no_todo_fixme/violation-01/modules/notes/service.py +3 -3
  268. package/corpus/backend/no_unique_columns_on_soft_delete_models/compliant-01/modules/notes/models.py +17 -17
  269. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/expected-findings.json +7 -7
  270. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/modules/notes/models.py +6 -6
  271. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/expected-findings.json +7 -7
  272. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/modules/notes/models.py +13 -13
  273. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/expected-findings.json +7 -7
  274. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/modules/notes/models.py +16 -16
  275. package/corpus/backend/offset_queries_declare_ordering/compliant-01/modules/notes/service.py +4 -4
  276. package/corpus/backend/offset_queries_declare_ordering/violation-01/modules/notes/service.py +2 -2
  277. package/corpus/backend/path_id_params_are_uuid/compliant-01/modules/notes/router.py +6 -6
  278. package/corpus/backend/path_id_params_are_uuid/violation-01/modules/notes/router.py +6 -6
  279. package/corpus/backend/policy_refs_resolve/compliant-01/modules/notes/module.py +7 -7
  280. package/corpus/backend/policy_refs_resolve/violation-01/modules/notes/module.py +8 -8
  281. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/module.py +7 -7
  282. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/router.py +3 -3
  283. package/corpus/backend/public_modules_are_read_only/violation-01/expected-findings.json +7 -7
  284. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/module.py +7 -7
  285. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/router.py +3 -3
  286. package/corpus/backend/public_modules_are_read_only/violation-02/expected-findings.json +7 -7
  287. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/module.py +7 -7
  288. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/router.py +5 -5
  289. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/models.py +6 -6
  290. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/service.py +8 -8
  291. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/models.py +9 -9
  292. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/service.py +9 -9
  293. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/models.py +6 -6
  294. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/service.py +9 -9
  295. package/corpus/backend/reads_use_base_query/violation-02/expected-findings.json +7 -7
  296. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/models.py +6 -6
  297. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/service.py +8 -8
  298. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/models.py +6 -6
  299. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/router.py +3 -3
  300. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/schemas.py +5 -5
  301. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/models.py +6 -6
  302. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/router.py +3 -3
  303. package/corpus/backend/routes_declare_response_model/compliant-01/modules/notes/router.py +8 -8
  304. package/corpus/backend/routes_declare_response_model/violation-01/modules/notes/router.py +3 -3
  305. package/corpus/backend/safe_methods_are_read_only/compliant-01/modules/notes/router.py +6 -6
  306. package/corpus/backend/safe_methods_are_read_only/compliant-02/modules/notes/router.py +5 -5
  307. package/corpus/backend/safe_methods_are_read_only/violation-01/expected-findings.json +7 -7
  308. package/corpus/backend/safe_methods_are_read_only/violation-01/modules/notes/router.py +3 -3
  309. package/corpus/backend/safe_methods_are_read_only/violation-02/expected-findings.json +7 -7
  310. package/corpus/backend/safe_methods_are_read_only/violation-02/modules/notes/router.py +5 -5
  311. package/corpus/backend/safe_methods_are_read_only/violation-03/expected-findings.json +7 -7
  312. package/corpus/backend/safe_methods_are_read_only/violation-03/modules/notes/router.py +3 -3
  313. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-01/modules/users/schemas.py +5 -5
  314. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-02/modules/accounts/schemas.py +12 -12
  315. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/expected-findings.json +7 -7
  316. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/modules/users/schemas.py +3 -3
  317. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/expected-findings.json +7 -7
  318. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/modules/connectors/router.py +8 -8
  319. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/expected-findings.json +17 -17
  320. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/modules/integrations/schemas.py +7 -7
  321. package/corpus/backend/session_imported_from_sqlmodel/compliant-01/modules/notes/service.py +5 -5
  322. package/corpus/backend/session_imported_from_sqlmodel/violation-01/modules/notes/service.py +5 -5
  323. package/corpus/backend/table_models_use_base_table/compliant-01/modules/notes/models.py +6 -6
  324. package/corpus/backend/table_models_use_base_table/violation-01/modules/notes/models.py +5 -5
  325. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/migrations/versions/0a1b2c3d4e5f_create_notes_tables.py +22 -22
  326. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/models.py +6 -6
  327. package/corpus/backend/tables_have_migrations/compliant-02/capabilities/ledger/models.py +6 -6
  328. package/corpus/backend/tables_have_migrations/violation-01/expected-findings.json +7 -7
  329. package/corpus/backend/tables_have_migrations/violation-01/modules/notes/models.py +6 -6
  330. package/corpus/backend/tables_have_migrations/violation-02/expected-findings.json +7 -7
  331. package/corpus/backend/tables_have_migrations/violation-02/modules/notes/models.py +10 -10
  332. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/models.py +6 -6
  333. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/service.py +5 -5
  334. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/models.py +8 -8
  335. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/service.py +5 -5
  336. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/models.py +6 -6
  337. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/service.py +5 -5
  338. package/corpus/backend/ungoverned_escape_hatch/compliant-01/modules/notes/service.py +5 -5
  339. package/corpus/backend/ungoverned_escape_hatch/compliant-02/modules/notes/service.py +4 -4
  340. package/corpus/backend/ungoverned_escape_hatch/violation-01/modules/notes/service.py +2 -2
  341. package/corpus/backend/update_schemas_inherit_base_update_schema/compliant-01/modules/notes/schemas.py +2 -2
  342. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-01/modules/notes/schemas.py +2 -2
  343. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-02/modules/notes/schemas.py +5 -5
  344. package/corpus/frontend/escape-hatch/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  345. package/corpus/frontend/escape-hatch/violation-01/src/modules/widgets/Widget.tsx +4 -4
  346. package/corpus/frontend/escape-hatch/violation-02/src/modules/widgets/Widget.tsx +7 -7
  347. package/corpus/frontend/escape-hatch/violation-03/src/modules/widgets/Widget.tsx +7 -7
  348. package/corpus/frontend/generated-client-only/compliant-01/src/modules/widgets/Widget.tsx +6 -6
  349. package/corpus/frontend/generated-client-only/compliant-02/src/modules/widgets/Widget.tsx +21 -21
  350. package/corpus/frontend/generated-client-only/compliant-03/src/modules/widgets/Widget.tsx +7 -7
  351. package/corpus/frontend/generated-client-only/compliant-04/src/modules/widgets/Widget.tsx +17 -17
  352. package/corpus/frontend/generated-client-only/violation-01/src/modules/widgets/Widget.tsx +3 -3
  353. package/corpus/frontend/generated-client-only/violation-02/src/modules/widgets/Widget.tsx +9 -9
  354. package/corpus/frontend/generated-client-only/violation-03/src/modules/widgets/Widget.tsx +14 -14
  355. package/corpus/frontend/generated-client-only/violation-04/src/modules/widgets/Widget.tsx +6 -6
  356. package/corpus/frontend/layout-contract/compliant-01/layout-contract.json +3 -3
  357. package/corpus/frontend/layout-contract/compliant-01/src/modules/widgets/Widget.tsx +8 -8
  358. package/corpus/frontend/layout-contract/violation-01/layout-contract.json +3 -3
  359. package/corpus/frontend/layout-contract/violation-01/src/modules/widgets/Widget.tsx +8 -8
  360. package/corpus/frontend/no-cross-module-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  361. package/corpus/frontend/no-cross-module-imports/violation-01/src/modules/widgets/Widget.tsx +3 -3
  362. package/corpus/frontend/no-deep-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  363. package/corpus/frontend/no-deep-imports/violation-01/src/modules/widgets/Widget.tsx +2 -2
  364. package/corpus/frontend/no-dom-html-injection/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  365. package/corpus/frontend/no-dom-html-injection/compliant-02/src/modules/widgets/Widget.tsx +11 -11
  366. package/corpus/frontend/no-dom-html-injection/violation-01/src/modules/widgets/Widget.tsx +3 -3
  367. package/corpus/frontend/no-dom-html-injection/violation-02/src/modules/widgets/Widget.tsx +7 -7
  368. package/corpus/frontend/no-dom-html-injection/violation-03/src/modules/widgets/Widget.tsx +12 -12
  369. package/corpus/frontend/no-eval/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  370. package/corpus/frontend/no-eval/compliant-02/src/modules/widgets/Widget.tsx +11 -11
  371. package/corpus/frontend/no-eval/violation-01/src/modules/widgets/Widget.tsx +3 -3
  372. package/corpus/frontend/no-eval/violation-02/src/modules/widgets/Widget.tsx +7 -7
  373. package/corpus/frontend/no-eval/violation-03/src/modules/widgets/Widget.tsx +10 -10
  374. package/corpus/frontend/no-eval/violation-04/src/modules/widgets/Widget.tsx +6 -6
  375. package/corpus/frontend/no-inline-styling/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  376. package/corpus/frontend/no-inline-styling/violation-01/src/modules/widgets/Widget.tsx +3 -3
  377. package/corpus/frontend/no-inline-styling/violation-02/src/modules/widgets/Widget.tsx +6 -6
  378. package/corpus/frontend/no-style-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  379. package/corpus/frontend/no-style-imports/violation-01/src/modules/widgets/Widget.tsx +2 -2
  380. package/corpus/frontend/no-unsafe-href/compliant-01/src/modules/widgets/Widget.tsx +3 -3
  381. package/corpus/frontend/no-unsafe-href/violation-01/src/modules/widgets/Widget.tsx +3 -3
  382. package/corpus/frontend/no-unsafe-target-blank/compliant-01/src/modules/widgets/Widget.tsx +3 -3
  383. package/corpus/frontend/no-unsafe-target-blank/violation-01/src/modules/widgets/Widget.tsx +3 -3
  384. package/corpus/frontend/router-links/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  385. package/corpus/frontend/router-links/violation-01/src/modules/widgets/Widget.tsx +3 -3
  386. package/corpus/frontend/token-styled-elements/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  387. package/corpus/frontend/token-styled-elements/compliant-02/src/modules/widgets/Widget.tsx +4 -4
  388. package/corpus/frontend/token-styled-elements/violation-01/src/modules/widgets/Widget.tsx +3 -3
  389. package/findings.schema.json +40 -40
  390. package/package.json +23 -23
  391. package/restricted-surface.json +10 -10
  392. package/scorecard.schema.json +52 -52
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_adhoc_logging_config",
3
- "surface": "backend",
4
- "title": "App code never configures logging globally; redaction is centralized",
5
- "intent": "Structured logging plus secret/PII redaction is installed once, centrally, at composition. A module that re-points the global logging configuration itself can silently bypass the central redaction filter.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_adhoc_logging_config"
12
- }
13
- ],
14
- "reference": "configure_logging (called by create_app) installs the redacting handlers; logging.basicConfig / dictConfig / fileConfig calls in app modules are refused.",
15
- "opt_out": "# arch-allow-no-adhoc-logging-config: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "Global logging mutation is ambient interpreter state with no framework chokepoint to refuse it. The paired protection is constructive rather than preventive: the framework installs redaction on every handler it configures, but cannot intercept a module calling logging.basicConfig at import time."
19
- },
20
- "guide_topic": "capability",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_adhoc_logging_config",
3
+ "surface": "backend",
4
+ "title": "App code never configures logging globally; redaction is centralized",
5
+ "intent": "Structured logging plus secret/PII redaction is installed once, centrally, at composition. A module that re-points the global logging configuration itself can silently bypass the central redaction filter.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_adhoc_logging_config"
12
+ }
13
+ ],
14
+ "reference": "configure_logging (called by create_app) installs the redacting handlers; logging.basicConfig / dictConfig / fileConfig calls in app modules are refused.",
15
+ "opt_out": "# arch-allow-no-adhoc-logging-config: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "Global logging mutation is ambient interpreter state with no framework chokepoint to refuse it. The paired protection is constructive rather than preventive: the framework installs redaction on every handler it configures, but cannot intercept a module calling logging.basicConfig at import time."
19
+ },
20
+ "guide_topic": "capability",
21
+ "corpus": true
22
+ }
@@ -1,27 +1,27 @@
1
- {
2
- "id": "backend/no_adhoc_middleware",
3
- "surface": "backend",
4
- "title": "App code never wires HTTP middleware itself; security is centralized",
5
- "intent": "Cross-cutting HTTP security (headers, CORS, rate-limit, body-size, request-id) is declared once as central security configuration and installed at composition. A module that registers middleware itself — by call, decorator, or subclassing — is assembling a security posture outside that single control plane.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_adhoc_middleware"
12
- },
13
- {
14
- "kind": "runtime",
15
- "tool": "terp.core",
16
- "ref": "_freeze_app_middleware_registration"
17
- }
18
- ],
19
- "reference": "SecurityConfig declared once and installed by create_app; add_middleware(...), the @app.middleware(\"http\") decorator, and BaseHTTPMiddleware subclasses are refused in app modules. On the composed app both registration spellings raise BootError at runtime; create_app's middleware parameter is the one sanctioned composition seam.",
20
- "opt_out": "# arch-allow-no-adhoc-middleware: <reason>",
21
- "runtime": {
22
- "applicability": "required",
23
- "rationale": "The composition freeze covers the middleware seam: on the composed app, add_middleware(...) and the @app.middleware(...) decorator are replaced by refusers that raise a BootError naming this rule and the sanctioned alternative (create_app's middleware parameter) -- so middleware registered between composition and serving is refused fail-closed, not just flagged at build time."
24
- },
25
- "guide_topic": "capability",
26
- "corpus": true
27
- }
1
+ {
2
+ "id": "backend/no_adhoc_middleware",
3
+ "surface": "backend",
4
+ "title": "App code never wires HTTP middleware itself; security is centralized",
5
+ "intent": "Cross-cutting HTTP security (headers, CORS, rate-limit, body-size, request-id) is declared once as central security configuration and installed at composition. A module that registers middleware itself — by call, decorator, or subclassing — is assembling a security posture outside that single control plane.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_adhoc_middleware"
12
+ },
13
+ {
14
+ "kind": "runtime",
15
+ "tool": "terp.core",
16
+ "ref": "_freeze_app_middleware_registration"
17
+ }
18
+ ],
19
+ "reference": "SecurityConfig declared once and installed by create_app; add_middleware(...), the @app.middleware(\"http\") decorator, and BaseHTTPMiddleware subclasses are refused in app modules. On the composed app both registration spellings raise BootError at runtime; create_app's middleware parameter is the one sanctioned composition seam.",
20
+ "opt_out": "# arch-allow-no-adhoc-middleware: <reason>",
21
+ "runtime": {
22
+ "applicability": "required",
23
+ "rationale": "The composition freeze covers the middleware seam: on the composed app, add_middleware(...) and the @app.middleware(...) decorator are replaced by refusers that raise a BootError naming this rule and the sanctioned alternative (create_app's middleware parameter) -- so middleware registered between composition and serving is refused fail-closed, not just flagged at build time."
24
+ },
25
+ "guide_topic": "capability",
26
+ "corpus": true
27
+ }
@@ -1,27 +1,27 @@
1
- {
2
- "id": "backend/no_adhoc_permission_literals",
3
- "surface": "backend",
4
- "title": "Modules reference typed authority objects, never bare permission strings",
5
- "intent": "An authority named as a bare string is invisible to the control plane: it cannot be resolved against the app's declared registry, so a typo or a stale name silently grants nothing (or the wrong thing) instead of failing the build. Citing typed authority objects keeps every permission reference greppable and verifiable against the registry, and lets the runtime normalization chokepoint refuse anything unregistered.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_adhoc_permission_literals"
12
- },
13
- {
14
- "kind": "runtime",
15
- "tool": "terp.core",
16
- "ref": "requirement_from"
17
- }
18
- ],
19
- "reference": "Role / Permission objects from the control-plane registry; requirement_from raises TypeError on a bare string.",
20
- "opt_out": "# arch-allow-no-adhoc-permission-literals: <reason>",
21
- "runtime": {
22
- "applicability": "required",
23
- "rationale": "The typed normalization chokepoint refuses a bare string outright (requirement_from raises TypeError for anything but Role / Permission / the legacy enum), and boot refuses a Policy citing an authority the control plane does not declare."
24
- },
25
- "guide_topic": "policy",
26
- "corpus": true
27
- }
1
+ {
2
+ "id": "backend/no_adhoc_permission_literals",
3
+ "surface": "backend",
4
+ "title": "Modules reference typed authority objects, never bare permission strings",
5
+ "intent": "An authority named as a bare string is invisible to the control plane: it cannot be resolved against the app's declared registry, so a typo or a stale name silently grants nothing (or the wrong thing) instead of failing the build. Citing typed authority objects keeps every permission reference greppable and verifiable against the registry, and lets the runtime normalization chokepoint refuse anything unregistered.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_adhoc_permission_literals"
12
+ },
13
+ {
14
+ "kind": "runtime",
15
+ "tool": "terp.core",
16
+ "ref": "requirement_from"
17
+ }
18
+ ],
19
+ "reference": "Role / Permission objects from the control-plane registry; requirement_from raises TypeError on a bare string.",
20
+ "opt_out": "# arch-allow-no-adhoc-permission-literals: <reason>",
21
+ "runtime": {
22
+ "applicability": "required",
23
+ "rationale": "The typed normalization chokepoint refuses a bare string outright (requirement_from raises TypeError for anything but Role / Permission / the legacy enum), and boot refuses a Policy citing an authority the control plane does not declare."
24
+ },
25
+ "guide_topic": "policy",
26
+ "corpus": true
27
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_app_instantiation",
3
- "surface": "backend",
4
- "title": "App code never constructs the web application object directly",
5
- "intent": "The framework's composer owns app composition (deny-by-default guards, the control plane, the error envelope). A hand-built application object is an application assembled outside the framework — it acquires none of those controls.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_app_instantiation"
12
- }
13
- ],
14
- "reference": "terp.core.create_app composes the app; a bare FastAPI() constructor call is refused.",
15
- "opt_out": "# arch-allow-no-app-instantiation: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "A raw FastAPI() is an ordinary object the framework never sees: it acquires no guard, middleware, or error envelope -- and offers no seam through which terp.core could refuse its construction. Only composition through create_app passes framework-owned surface; the rule keeps authorship inside it."
19
- },
20
- "guide_topic": "capability",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_app_instantiation",
3
+ "surface": "backend",
4
+ "title": "App code never constructs the web application object directly",
5
+ "intent": "The framework's composer owns app composition (deny-by-default guards, the control plane, the error envelope). A hand-built application object is an application assembled outside the framework — it acquires none of those controls.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_app_instantiation"
12
+ }
13
+ ],
14
+ "reference": "terp.core.create_app composes the app; a bare FastAPI() constructor call is refused.",
15
+ "opt_out": "# arch-allow-no-app-instantiation: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "A raw FastAPI() is an ordinary object the framework never sees: it acquires no guard, middleware, or error envelope -- and offers no seam through which terp.core could refuse its construction. Only composition through create_app passes framework-owned surface; the rule keeps authorship inside it."
19
+ },
20
+ "guide_topic": "capability",
21
+ "corpus": true
22
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_blocking_sleep",
3
- "surface": "backend",
4
- "title": "Never block the thread with a synchronous sleep",
5
- "intent": "A synchronous sleep parks the thread that is serving the request or running the job, so the whole worker sits idle and the pool starves under load. Waiting must yield the thread — poll with an awaitable, schedule a delayed job, or let the runtime back off — never freeze it.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_blocking_sleep"
12
- }
13
- ],
14
- "reference": "A call to the standard library's time.sleep() is refused; use a non-blocking wait or a scheduled job.",
15
- "opt_out": "# arch-allow-no-blocking-sleep: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The invariant is a property of the authored call. At execution time a blocking sleep is a normal function call on the stack, and by the time it runs the thread is already parked — no seam upstream can turn a blocking wait into a yielding one, so the only faithful check refuses the construct in source."
19
- },
20
- "guide_topic": "jobs",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_blocking_sleep",
3
+ "surface": "backend",
4
+ "title": "Never block the thread with a synchronous sleep",
5
+ "intent": "A synchronous sleep parks the thread that is serving the request or running the job, so the whole worker sits idle and the pool starves under load. Waiting must yield the thread — poll with an awaitable, schedule a delayed job, or let the runtime back off — never freeze it.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_blocking_sleep"
12
+ }
13
+ ],
14
+ "reference": "A call to the standard library's time.sleep() is refused; use a non-blocking wait or a scheduled job.",
15
+ "opt_out": "# arch-allow-no-blocking-sleep: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The invariant is a property of the authored call. At execution time a blocking sleep is a normal function call on the stack, and by the time it runs the thread is already parked — no seam upstream can turn a blocking wait into a yielding one, so the only faithful check refuses the construct in source."
19
+ },
20
+ "guide_topic": "jobs",
21
+ "corpus": true
22
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "id": "backend/no_cross_module_imports",
3
- "surface": "backend",
4
- "title": "A module never imports a sibling module (leaf domains stay independent)",
5
- "intent": "Modules couple only through the platform surface and their own files — a sibling import creates hidden coupling that breaks module isolation and independent evolution. Both absolute and relative sibling imports are caught: a relative import is resolved to its absolute module first, so renaming the import style does not re-couple two leaf modules.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_cross_module_imports"
12
- }
13
- ],
14
- "opt_out": "# arch-allow-no-cross-module-imports: <reason>",
15
- "runtime": {
16
- "applicability": "not-applicable",
17
- "rationale": "Module independence is an import-graph property of the source tree. At runtime the composed app legitimately holds every module in one process, so a cross-module reference is indistinguishable from composition itself."
18
- },
19
- "guide_topic": "module",
20
- "corpus": true
21
- }
1
+ {
2
+ "id": "backend/no_cross_module_imports",
3
+ "surface": "backend",
4
+ "title": "A module never imports a sibling module (leaf domains stay independent)",
5
+ "intent": "Modules couple only through the platform surface and their own files — a sibling import creates hidden coupling that breaks module isolation and independent evolution. Both absolute and relative sibling imports are caught: a relative import is resolved to its absolute module first, so renaming the import style does not re-couple two leaf modules.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_cross_module_imports"
12
+ }
13
+ ],
14
+ "opt_out": "# arch-allow-no-cross-module-imports: <reason>",
15
+ "runtime": {
16
+ "applicability": "not-applicable",
17
+ "rationale": "Module independence is an import-graph property of the source tree. At runtime the composed app legitimately holds every module in one process, so a cross-module reference is indistinguishable from composition itself."
18
+ },
19
+ "guide_topic": "module",
20
+ "corpus": true
21
+ }
@@ -1,27 +1,27 @@
1
- {
2
- "id": "backend/no_dependency_overrides",
3
- "surface": "backend",
4
- "title": "App code never rebinds the composed app's dependency injections",
5
- "intent": "Composition binds the authentication and session seams once. Rebinding the app's dependency-override map in app code (e.g. replacing the principal provider) silently disables authentication or swaps the database session outside every guard. Overrides are a TEST-ONLY seam; application code has no legitimate use.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_dependency_overrides"
12
- },
13
- {
14
- "kind": "runtime",
15
- "tool": "terp.core",
16
- "ref": "_freeze_dependency_overrides"
17
- }
18
- ],
19
- "reference": "app.dependency_overrides on the create_app-composed app is the refused surface; composition binds the seams (create_app(principal_provider=...)) before the freeze.",
20
- "opt_out": "# arch-allow-no-dependency-overrides: <reason>",
21
- "runtime": {
22
- "applicability": "required",
23
- "rationale": "Outside the local environment the composed app's dependency-override map is replaced by a refusing mapping: every mutating spelling raises a BootError naming this rule while per-request override reads keep working. The local environment deliberately keeps the map writable -- overrides are the sanctioned test-only seam (a local test suite rebinding the session against a scratch engine), which is exactly the legitimate use the rule's intent carves out; a deployed app can never rebind its auth or session seams post-composition."
24
- },
25
- "guide_topic": "capability",
26
- "corpus": true
27
- }
1
+ {
2
+ "id": "backend/no_dependency_overrides",
3
+ "surface": "backend",
4
+ "title": "App code never rebinds the composed app's dependency injections",
5
+ "intent": "Composition binds the authentication and session seams once. Rebinding the app's dependency-override map in app code (e.g. replacing the principal provider) silently disables authentication or swaps the database session outside every guard. Overrides are a TEST-ONLY seam; application code has no legitimate use.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_dependency_overrides"
12
+ },
13
+ {
14
+ "kind": "runtime",
15
+ "tool": "terp.core",
16
+ "ref": "_freeze_dependency_overrides"
17
+ }
18
+ ],
19
+ "reference": "app.dependency_overrides on the create_app-composed app is the refused surface; composition binds the seams (create_app(principal_provider=...)) before the freeze.",
20
+ "opt_out": "# arch-allow-no-dependency-overrides: <reason>",
21
+ "runtime": {
22
+ "applicability": "required",
23
+ "rationale": "Outside the local environment the composed app's dependency-override map is replaced by a refusing mapping: every mutating spelling raises a BootError naming this rule while per-request override reads keep working. The local environment deliberately keeps the map writable -- overrides are the sanctioned test-only seam (a local test suite rebinding the session against a scratch engine), which is exactly the legitimate use the rule's intent carves out; a deployed app can never rebind its auth or session seams post-composition."
24
+ },
25
+ "guide_topic": "capability",
26
+ "corpus": true
27
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_destructive_migrations",
3
- "surface": "backend",
4
- "title": "Destructive migration operations require a reason-bearing marker",
5
- "intent": "Dropping a table or column, changing a column's type, and executing raw destructive statements in an upgrade can destroy data or make rollback unsafe — whether spelled directly, on a batch block, through an alias, or smuggled into a raw statement. Each destructive operation is a violation; a reviewed one is justified through the standard governed escape hatch — a justified marker on (or immediately above) the operation, counted against the app's budget ratchet — so every accepted risk is explicit, reviewable, greppable, and ratcheted like any other opt-out.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_destructive_migrations"
12
- }
13
- ],
14
- "reference": "drop_table / drop_column / type-changing alter_column and execute(...) of DROP TABLE / DROP COLUMN / TRUNCATE / DELETE FROM / ALTER TABLE ... DROP in upgrade() are flagged per operation; the standard # arch-allow-no-destructive-migrations marker (budgeted) justifies a reviewed one. The pre-0.6.0 file-level terp-allow-destructive-migration waiver is retired.",
15
- "opt_out": "# arch-allow-no-destructive-migrations: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The invariant is reviewability of the authored revision: a reason-bearing marker is a comment, invisible to Alembic when the DDL executes. What could be checked at apply time is a different (op-classification) control; the justified-marker contract itself only exists in source."
19
- },
20
- "guide_topic": "migrations",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_destructive_migrations",
3
+ "surface": "backend",
4
+ "title": "Destructive migration operations require a reason-bearing marker",
5
+ "intent": "Dropping a table or column, changing a column's type, and executing raw destructive statements in an upgrade can destroy data or make rollback unsafe — whether spelled directly, on a batch block, through an alias, or smuggled into a raw statement. Each destructive operation is a violation; a reviewed one is justified through the standard governed escape hatch — a justified marker on (or immediately above) the operation, counted against the app's budget ratchet — so every accepted risk is explicit, reviewable, greppable, and ratcheted like any other opt-out.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_destructive_migrations"
12
+ }
13
+ ],
14
+ "reference": "drop_table / drop_column / type-changing alter_column and execute(...) of DROP TABLE / DROP COLUMN / TRUNCATE / DELETE FROM / ALTER TABLE ... DROP in upgrade() are flagged per operation; the standard # arch-allow-no-destructive-migrations marker (budgeted) justifies a reviewed one. The pre-0.6.0 file-level terp-allow-destructive-migration waiver is retired.",
15
+ "opt_out": "# arch-allow-no-destructive-migrations: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The invariant is reviewability of the authored revision: a reason-bearing marker is a comment, invisible to Alembic when the DDL executes. What could be checked at apply time is a different (op-classification) control; the justified-marker contract itself only exists in source."
19
+ },
20
+ "guide_topic": "migrations",
21
+ "corpus": true
22
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_dynamic_sql",
3
- "surface": "backend",
4
- "title": "Raw SQL text in app modules must be a static literal, never dynamically built",
5
- "intent": "A raw-SQL construct built dynamically — through interpolation, concatenation, format calls, or a variable — is not statically reviewable and is easy to turn into SQL injection. Keep SQL as a literal and pass data through bound parameters / the query builder's typed expressions instead. As a security rule this also scans test and migration files inside a module — they are importable code, so they are application surface too.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_dynamic_sql"
12
- }
13
- ],
14
- "reference": "text(...) / sqlalchemy.text(...) with an f-string, concatenation, .format, % formatting, or a variable is refused; SQLAlchemy bound parameters / ORM expressions are the compliant path.",
15
- "opt_out": "# arch-allow-no-dynamic-sql: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The invariant is a property of the authored source: whether SQL text was written as a static, reviewable literal. At execution time every statement is just a string \u2014 how it was built is erased, so no runtime seam can distinguish a vetted literal from concatenated input. The adjacent runtime protections are different controls: the write guard refuses smuggled DML (mutations_emit_audit) and parameter binding is the compliant data path."
19
- },
20
- "guide_topic": "service",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_dynamic_sql",
3
+ "surface": "backend",
4
+ "title": "Raw SQL text in app modules must be a static literal, never dynamically built",
5
+ "intent": "A raw-SQL construct built dynamically — through interpolation, concatenation, format calls, or a variable — is not statically reviewable and is easy to turn into SQL injection. Keep SQL as a literal and pass data through bound parameters / the query builder's typed expressions instead. As a security rule this also scans test and migration files inside a module — they are importable code, so they are application surface too.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_dynamic_sql"
12
+ }
13
+ ],
14
+ "reference": "text(...) / sqlalchemy.text(...) with an f-string, concatenation, .format, % formatting, or a variable is refused; SQLAlchemy bound parameters / ORM expressions are the compliant path.",
15
+ "opt_out": "# arch-allow-no-dynamic-sql: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The invariant is a property of the authored source: whether SQL text was written as a static, reviewable literal. At execution time every statement is just a string \u2014 how it was built is erased, so no runtime seam can distinguish a vetted literal from concatenated input. The adjacent runtime protections are different controls: the write guard refuses smuggled DML (mutations_emit_audit) and parameter binding is the compliant data path."
19
+ },
20
+ "guide_topic": "service",
21
+ "corpus": true
22
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_empty_tests",
3
- "surface": "backend",
4
- "title": "Every test must assert a real outcome",
5
- "intent": "A test whose body is empty, is only a docstring or a bare pass, or asserts a constant that can never fail, passes unconditionally — it exercises nothing yet reports green, giving false confidence that the behaviour it names is covered. A test must drive real behaviour and assert a real result.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_empty_tests"
12
- }
13
- ],
14
- "reference": "A test_* function with an empty body, a lone pass, or a single assertion of a constant truthy value is refused. The scan covers every test_*.py file — the test tree the other source rules deliberately skip.",
15
- "opt_out": "# arch-allow-no-empty-tests: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The invariant is a property of the authored test source, evaluated at build time in the test suite itself; there is no production runtime seam that observes whether a test asserts anything. Only a source-form check can see the empty body."
19
- },
20
- "guide_topic": "rules",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_empty_tests",
3
+ "surface": "backend",
4
+ "title": "Every test must assert a real outcome",
5
+ "intent": "A test whose body is empty, is only a docstring or a bare pass, or asserts a constant that can never fail, passes unconditionally — it exercises nothing yet reports green, giving false confidence that the behaviour it names is covered. A test must drive real behaviour and assert a real result.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_empty_tests"
12
+ }
13
+ ],
14
+ "reference": "A test_* function with an empty body, a lone pass, or a single assertion of a constant truthy value is refused. The scan covers every test_*.py file — the test tree the other source rules deliberately skip.",
15
+ "opt_out": "# arch-allow-no-empty-tests: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The invariant is a property of the authored test source, evaluated at build time in the test suite itself; there is no production runtime seam that observes whether a test asserts anything. Only a source-form check can see the empty body."
19
+ },
20
+ "guide_topic": "rules",
21
+ "corpus": true
22
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "backend/no_eval_or_exec",
3
- "surface": "backend",
4
- "title": "Never execute a string as code",
5
- "intent": "Turning a runtime string into executed code is a code-injection hole: any value an attacker can influence on its way into the evaluator becomes arbitrary code the process runs. There is no safe in-app use — parse the data into a structure, dispatch on a lookup table, or import a real module instead of evaluating text.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_eval_or_exec"
12
- }
13
- ],
14
- "reference": "Calls to the eval() and exec() builtins are refused. The scan covers the whole importable tree — including tests and migrations — because any Python that runs is in scope.",
15
- "opt_out": "# arch-allow-no-eval-or-exec: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The invariant is a property of the authored source: whether a dynamic-execution builtin was written. By the time the process runs, an evaluated string is indistinguishable from ordinary control flow, and no runtime seam upstream can tell a vetted call from an injected one — the only faithful check is refusing the construct in source."
19
- },
20
- "guide_topic": "capability",
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "backend/no_eval_or_exec",
3
+ "surface": "backend",
4
+ "title": "Never execute a string as code",
5
+ "intent": "Turning a runtime string into executed code is a code-injection hole: any value an attacker can influence on its way into the evaluator becomes arbitrary code the process runs. There is no safe in-app use — parse the data into a structure, dispatch on a lookup table, or import a real module instead of evaluating text.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_eval_or_exec"
12
+ }
13
+ ],
14
+ "reference": "Calls to the eval() and exec() builtins are refused. The scan covers the whole importable tree — including tests and migrations — because any Python that runs is in scope.",
15
+ "opt_out": "# arch-allow-no-eval-or-exec: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The invariant is a property of the authored source: whether a dynamic-execution builtin was written. By the time the process runs, an evaluated string is indistinguishable from ordinary control flow, and no runtime seam upstream can tell a vetted call from an injected one — the only faithful check is refusing the construct in source."
19
+ },
20
+ "guide_topic": "capability",
21
+ "corpus": true
22
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "id": "backend/no_hardcoded_credentials",
3
- "surface": "backend",
4
- "title": "App modules do not hard-code credentials or recognizable secret tokens",
5
- "intent": "A credential-shaped assignment to a non-empty string literal is almost always a secret that should come from sealed config / environment wiring, not source. The rule also rejects common high-confidence secret literal formats anywhere in a module so leaked keys are caught even when assigned to a bland variable name. As a security rule this also scans test and migration files inside a module — a real secret is a leak wherever it is committed.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "terp.arch",
11
- "ref": "check_no_hardcoded_credentials"
12
- }
13
- ],
14
- "opt_out": "# arch-allow-no-hardcoded-credentials: <reason>",
15
- "runtime": {
16
- "applicability": "not-applicable",
17
- "rationale": "At runtime a credential is a string wherever it came from: a source literal and an injected environment value are indistinguishable once loaded. 'It was committed to the repository' is a source-tree fact only a source scan can state."
18
- },
19
- "guide_topic": "capability",
20
- "corpus": true
21
- }
1
+ {
2
+ "id": "backend/no_hardcoded_credentials",
3
+ "surface": "backend",
4
+ "title": "App modules do not hard-code credentials or recognizable secret tokens",
5
+ "intent": "A credential-shaped assignment to a non-empty string literal is almost always a secret that should come from sealed config / environment wiring, not source. The rule also rejects common high-confidence secret literal formats anywhere in a module so leaked keys are caught even when assigned to a bland variable name. As a security rule this also scans test and migration files inside a module — a real secret is a leak wherever it is committed.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_no_hardcoded_credentials"
12
+ }
13
+ ],
14
+ "opt_out": "# arch-allow-no-hardcoded-credentials: <reason>",
15
+ "runtime": {
16
+ "applicability": "not-applicable",
17
+ "rationale": "At runtime a credential is a string wherever it came from: a source literal and an injected environment value are indistinguishable once loaded. 'It was committed to the repository' is a source-tree fact only a source scan can state."
18
+ },
19
+ "guide_topic": "capability",
20
+ "corpus": true
21
+ }