@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,95 +1,95 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://terp.dev/spec/catalog/schema.json",
4
- "title": "Terp Standard catalog entry",
5
- "description": "One enforced Terp rule as data (ADR 0080). The file lives at catalog/<surface>/<rule>.json and its id must equal '<surface>/<rule>'. The spec version an entry belongs to is the checked-in spec/VERSION.",
6
- "type": "object",
7
- "required": ["id", "surface", "title", "intent", "layer", "enforcement", "runtime", "corpus"],
8
- "additionalProperties": false,
9
- "properties": {
10
- "id": {
11
- "type": "string",
12
- "description": "Stack-neutral rule id: backend/<snake_case> or frontend/<kebab-case>. Findings are attributed to this id (see ../findings.schema.json).",
13
- "pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
14
- },
15
- "surface": { "enum": ["backend", "frontend"] },
16
- "title": { "type": "string", "minLength": 1 },
17
- "intent": { "type": "string", "minLength": 1 },
18
- "layer": {
19
- "description": "Cheapest faithful verification for a NEW stack: black-box (probe a running app), static-portable (generic source patterns), static-bespoke (needs deep framework knowledge).",
20
- "enum": ["black-box", "static-portable", "static-bespoke"]
21
- },
22
- "enforcement": {
23
- "description": "How the reference implementation enforces the rule today. At least one build-time entry; a 'runtime' entry records the fail-closed runtime half of the two-layer discipline, and a 'black-box' entry names the conformance-suite probe.",
24
- "type": "array",
25
- "minItems": 1,
26
- "items": {
27
- "type": "object",
28
- "required": ["kind", "tool", "ref"],
29
- "additionalProperties": false,
30
- "properties": {
31
- "kind": { "enum": ["build-time", "runtime", "black-box"] },
32
- "tool": { "type": "string", "minLength": 1 },
33
- "ref": { "type": "string", "minLength": 1 },
34
- "reported_as": {
35
- "type": "string",
36
- "description": "Frontend build-time only: the ESLint rule id violations surface as. Several catalog rules share a core rule id, so conformance findings are attributed via the adapter's catalogRuleId mapping, never this field."
37
- }
38
- }
39
- }
40
- },
41
- "runtime": {
42
- "description": "The rule's runtime-applicability classification: whether the two-layer discipline pairs this build-time rule with a fail-closed runtime control. 'required' means the reference implementation owns such a control and the entry declares it (a kind 'runtime' enforcement entry whose ref must resolve — the parity test fails otherwise). 'not-applicable' means the invariant is a property of the authored artifact (source form, imports, markers, checked-in files) that is erased or already materialised by the time the app runs, so no runtime seam can enforce it — the rationale states why. 'deferred' means a runtime control would add independent fidelity on a seam the framework owns but has not shipped yet — an explicit, reviewed gap, never a silent one. Coherence is held by the spec suite: 'required' iff a kind 'runtime' enforcement entry exists; an exemption always carries a rationale.",
43
- "type": "object",
44
- "required": ["applicability"],
45
- "additionalProperties": false,
46
- "properties": {
47
- "applicability": { "enum": ["required", "not-applicable", "deferred"] },
48
- "rationale": {
49
- "type": "string",
50
- "description": "Why the rule is exempt from the runtime half (mandatory for 'not-applicable' and 'deferred'; optional for 'required', e.g. to record which vector the control covers).",
51
- "minLength": 1
52
- },
53
- "tracking": {
54
- "type": "string",
55
- "description": "Where the deferral is tracked (mandatory for 'deferred', held by the spec suite): an issue URL, or the reference implementation's tracker document plus the named seam — so a deferred runtime control has a lifecycle instead of being an open-ended gap.",
56
- "minLength": 1
57
- }
58
- }
59
- },
60
- "restricted_surface": {
61
- "type": "array",
62
- "description": "The restricted-surface.json keys this rule realises (frontend prohibition rules only). The structural half of the citation: the spec suite resolves each key against the refused surface and requires every key to be claimed by some rule, so the linkage never depends on parsing prose.",
63
- "minItems": 1,
64
- "items": {
65
- "enum": [
66
- "restrictedElements",
67
- "restrictedAttributes",
68
- "restrictedGlobals",
69
- "restrictedMemberCalls",
70
- "styleImportExtensions",
71
- "deepImportPathSegments"
72
- ]
73
- }
74
- },
75
- "reference": {
76
- "type": "string",
77
- "description": "Reference-implementation metadata: the compliant realisation the reference stack offers (component / helper names). Not normative — the title and intent state the stack-neutral invariant; another stack ships its own realisation.",
78
- "minLength": 1
79
- },
80
- "opt_out": {
81
- "type": "string",
82
- "description": "The reference realisation of the abstract escape-hatch contract: a justified inline marker naming the CATALOG RULE NAME (the <rule> half of the id — never a tool-internal rule id, mirroring findings attribution), counted against the app's checked-in budget ratchet. Another stack implements the same contract with its own comment syntax; only the semantics are normative. Omitted exactly by the escape-hatch governance rules themselves (the budget ratchet, the ungoverned-marker condition) — governance cannot be waived by the mechanism it governs. The spec suite holds both the derivation and the omissions.",
83
- "minLength": 1
84
- },
85
- "guide_topic": {
86
- "type": "string",
87
- "description": "Reference-implementation metadata (backend only): the `terp guide` topic teaching the compliant pattern. Not normative for other stacks.",
88
- "minLength": 1
89
- },
90
- "corpus": {
91
- "type": "boolean",
92
- "description": "Whether corpus/<id>/ cases exist (held to the directory truth by the parity test; a covered rule needs at least one violation-* AND one compliant-* case)."
93
- }
94
- }
95
- }
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://terp.dev/spec/catalog/schema.json",
4
+ "title": "Terp Standard catalog entry",
5
+ "description": "One enforced Terp rule as data (ADR 0080). The file lives at catalog/<surface>/<rule>.json and its id must equal '<surface>/<rule>'. The spec version an entry belongs to is the checked-in spec/VERSION.",
6
+ "type": "object",
7
+ "required": ["id", "surface", "title", "intent", "layer", "enforcement", "runtime", "corpus"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "description": "Stack-neutral rule id: backend/<snake_case> or frontend/<kebab-case>. Findings are attributed to this id (see ../findings.schema.json).",
13
+ "pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
14
+ },
15
+ "surface": { "enum": ["backend", "frontend"] },
16
+ "title": { "type": "string", "minLength": 1 },
17
+ "intent": { "type": "string", "minLength": 1 },
18
+ "layer": {
19
+ "description": "Cheapest faithful verification for a NEW stack: black-box (probe a running app), static-portable (generic source patterns), static-bespoke (needs deep framework knowledge).",
20
+ "enum": ["black-box", "static-portable", "static-bespoke"]
21
+ },
22
+ "enforcement": {
23
+ "description": "How the reference implementation enforces the rule today. At least one build-time entry; a 'runtime' entry records the fail-closed runtime half of the two-layer discipline, and a 'black-box' entry names the conformance-suite probe.",
24
+ "type": "array",
25
+ "minItems": 1,
26
+ "items": {
27
+ "type": "object",
28
+ "required": ["kind", "tool", "ref"],
29
+ "additionalProperties": false,
30
+ "properties": {
31
+ "kind": { "enum": ["build-time", "runtime", "black-box"] },
32
+ "tool": { "type": "string", "minLength": 1 },
33
+ "ref": { "type": "string", "minLength": 1 },
34
+ "reported_as": {
35
+ "type": "string",
36
+ "description": "Frontend build-time only: the ESLint rule id violations surface as. Several catalog rules share a core rule id, so conformance findings are attributed via the adapter's catalogRuleId mapping, never this field."
37
+ }
38
+ }
39
+ }
40
+ },
41
+ "runtime": {
42
+ "description": "The rule's runtime-applicability classification: whether the two-layer discipline pairs this build-time rule with a fail-closed runtime control. 'required' means the reference implementation owns such a control and the entry declares it (a kind 'runtime' enforcement entry whose ref must resolve — the parity test fails otherwise). 'not-applicable' means the invariant is a property of the authored artifact (source form, imports, markers, checked-in files) that is erased or already materialised by the time the app runs, so no runtime seam can enforce it — the rationale states why. 'deferred' means a runtime control would add independent fidelity on a seam the framework owns but has not shipped yet — an explicit, reviewed gap, never a silent one. Coherence is held by the spec suite: 'required' iff a kind 'runtime' enforcement entry exists; an exemption always carries a rationale.",
43
+ "type": "object",
44
+ "required": ["applicability"],
45
+ "additionalProperties": false,
46
+ "properties": {
47
+ "applicability": { "enum": ["required", "not-applicable", "deferred"] },
48
+ "rationale": {
49
+ "type": "string",
50
+ "description": "Why the rule is exempt from the runtime half (mandatory for 'not-applicable' and 'deferred'; optional for 'required', e.g. to record which vector the control covers).",
51
+ "minLength": 1
52
+ },
53
+ "tracking": {
54
+ "type": "string",
55
+ "description": "Where the deferral is tracked (mandatory for 'deferred', held by the spec suite): an issue URL, or the reference implementation's tracker document plus the named seam — so a deferred runtime control has a lifecycle instead of being an open-ended gap.",
56
+ "minLength": 1
57
+ }
58
+ }
59
+ },
60
+ "restricted_surface": {
61
+ "type": "array",
62
+ "description": "The restricted-surface.json keys this rule realises (frontend prohibition rules only). The structural half of the citation: the spec suite resolves each key against the refused surface and requires every key to be claimed by some rule, so the linkage never depends on parsing prose.",
63
+ "minItems": 1,
64
+ "items": {
65
+ "enum": [
66
+ "restrictedElements",
67
+ "restrictedAttributes",
68
+ "restrictedGlobals",
69
+ "restrictedMemberCalls",
70
+ "styleImportExtensions",
71
+ "deepImportPathSegments"
72
+ ]
73
+ }
74
+ },
75
+ "reference": {
76
+ "type": "string",
77
+ "description": "Reference-implementation metadata: the compliant realisation the reference stack offers (component / helper names). Not normative — the title and intent state the stack-neutral invariant; another stack ships its own realisation.",
78
+ "minLength": 1
79
+ },
80
+ "opt_out": {
81
+ "type": "string",
82
+ "description": "The reference realisation of the abstract escape-hatch contract: a justified inline marker naming the CATALOG RULE NAME (the <rule> half of the id — never a tool-internal rule id, mirroring findings attribution), counted against the app's checked-in budget ratchet. Another stack implements the same contract with its own comment syntax; only the semantics are normative. Omitted exactly by the escape-hatch governance rules themselves (the budget ratchet, the ungoverned-marker condition) — governance cannot be waived by the mechanism it governs. The spec suite holds both the derivation and the omissions.",
83
+ "minLength": 1
84
+ },
85
+ "guide_topic": {
86
+ "type": "string",
87
+ "description": "Reference-implementation metadata (backend only): the `terp guide` topic teaching the compliant pattern. Not normative for other stacks.",
88
+ "minLength": 1
89
+ },
90
+ "corpus": {
91
+ "type": "boolean",
92
+ "description": "Whether corpus/<id>/ cases exist (held to the directory truth by the parity test; a covered rule needs at least one violation-* AND one compliant-* case)."
93
+ }
94
+ }
95
+ }
@@ -1,4 +1,4 @@
1
- {
2
- "_comment": "The corpus-coverage ratchet: every catalog rule still WITHOUT corpus cases, exactly. Seeding a rule's corpus removes it here (the list only shrinks); a new rule without corpus must be added here explicitly, so uncovered rules stay visible and reviewed. Held to the catalog truth by tests/architecture/test_spec_catalog.py.",
3
- "pending": []
4
- }
1
+ {
2
+ "_comment": "The corpus-coverage ratchet: every catalog rule still WITHOUT corpus cases, exactly. Seeding a rule's corpus removes it here (the list only shrinks); a new rule without corpus must be added here explicitly, so uncovered rules stay visible and reviewed. Held to the catalog truth by tests/architecture/test_spec_catalog.py.",
3
+ "pending": []
4
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "_comment": "The detector-residual ratchet: the statically-erased or renamed forms DELIBERATELY outside the corpus contract, per rule — known limits of precise, low-false-positive detection, recorded as data so a second implementation neither over-fits nor under-claims. Governed like corpus/PENDING.json: the list only shrinks — closing a residual means seeding the corpus case that contracts it and deleting the entry here, never silently. Every key must be a catalog rule id; held to the catalog by tests/test_standard.py.",
3
- "residuals": {
4
- "backend/no_dynamic_sql": [
5
- "an alias-renamed symbol import (`from sqlalchemy import text as sql_text`) is not required to be resolved to `text`"
6
- ],
7
- "backend/no_raw_outbound_http": [
8
- "dynamic import (`importlib.import_module(\"httpx\")`) is not required to be seen as an import"
9
- ],
10
- "frontend/generated-client-only": [
11
- "egress through receivers other than `window`/`globalThis` (`self.fetch`) is not required"
12
- ],
13
- "frontend/no-dom-html-injection": [
14
- "a computed sink (`el[\"innerHTML\"] = ...`) is not required to be recognised"
15
- ],
16
- "frontend/no-eval": [
17
- "`Function(\"...\")` called without `new` is not required",
18
- "a computed global (`window[\"eval\"]`) is not required to be recognised"
19
- ]
20
- }
21
- }
1
+ {
2
+ "_comment": "The detector-residual ratchet: the statically-erased or renamed forms DELIBERATELY outside the corpus contract, per rule — known limits of precise, low-false-positive detection, recorded as data so a second implementation neither over-fits nor under-claims. Governed like corpus/PENDING.json: the list only shrinks — closing a residual means seeding the corpus case that contracts it and deleting the entry here, never silently. Every key must be a catalog rule id; held to the catalog by tests/test_standard.py.",
3
+ "residuals": {
4
+ "backend/no_dynamic_sql": [
5
+ "an alias-renamed symbol import (`from sqlalchemy import text as sql_text`) is not required to be resolved to `text`"
6
+ ],
7
+ "backend/no_raw_outbound_http": [
8
+ "dynamic import (`importlib.import_module(\"httpx\")`) is not required to be seen as an import"
9
+ ],
10
+ "frontend/generated-client-only": [
11
+ "egress through receivers other than `window`/`globalThis` (`self.fetch`) is not required"
12
+ ],
13
+ "frontend/no-dom-html-injection": [
14
+ "a computed sink (`el[\"innerHTML\"] = ...`) is not required to be recognised"
15
+ ],
16
+ "frontend/no-eval": [
17
+ "`Function(\"...\")` called without `new` is not required",
18
+ "a computed global (`window[\"eval\"]`) is not required to be recognised"
19
+ ]
20
+ }
21
+ }
@@ -1,6 +1,6 @@
1
- def upgrade():
2
- op.add_column("notes", column)
3
-
4
-
5
- def downgrade():
6
- op.drop_column("notes", "title")
1
+ def upgrade():
2
+ op.add_column("notes", column)
3
+
4
+
5
+ def downgrade():
6
+ op.drop_column("notes", "title")
@@ -1,6 +1,6 @@
1
- def upgrade():
2
- op.add_column("notes", column)
3
-
4
-
5
- def downgrade():
6
- pass
1
+ def upgrade():
2
+ op.add_column("notes", column)
3
+
4
+
5
+ def downgrade():
6
+ pass
@@ -1,8 +1,8 @@
1
- from terp.core import BaseService
2
-
3
-
4
- class NoteService(BaseService):
5
- model = Note
6
-
7
- def business_filters(self):
8
- return [Note.archived == False]
1
+ from terp.core import BaseService
2
+
3
+
4
+ class NoteService(BaseService):
5
+ model = Note
6
+
7
+ def business_filters(self):
8
+ return [Note.archived == False]
@@ -1,8 +1,8 @@
1
- from terp.core import BaseService
2
-
3
-
4
- class NoteService(BaseService):
5
- model = Note
6
-
7
- def business_filters(self):
8
- return [Note.archived == False]
1
+ from terp.core import BaseService
2
+
3
+
4
+ class NoteService(BaseService):
5
+ model = Note
6
+
7
+ def business_filters(self):
8
+ return [Note.archived == False]
@@ -1,8 +1,8 @@
1
- from terp.core import BaseService
2
-
3
-
4
- class NoteService(BaseService):
5
- model = Note
6
-
7
- def base_query(self):
8
- return super().base_query().where(Note.archived == False)
1
+ from terp.core import BaseService
2
+
3
+
4
+ class NoteService(BaseService):
5
+ model = Note
6
+
7
+ def base_query(self):
8
+ return super().base_query().where(Note.archived == False)
@@ -1,7 +1,7 @@
1
- [
2
- {
3
- "rule": "backend/base_query_not_overridden",
4
- "path": "modules/notes/service.py",
5
- "line": 7
6
- }
7
- ]
1
+ [
2
+ {
3
+ "rule": "backend/base_query_not_overridden",
4
+ "path": "modules/notes/service.py",
5
+ "line": 7
6
+ }
7
+ ]
@@ -1,8 +1,8 @@
1
- from terp.core import BaseService
2
-
3
-
4
- class NoteService(BaseService):
5
- model = Note
6
-
7
- async def base_query(self):
8
- return (await super().base_query()).where(Note.archived == False)
1
+ from terp.core import BaseService
2
+
3
+
4
+ class NoteService(BaseService):
5
+ model = Note
6
+
7
+ async def base_query(self):
8
+ return (await super().base_query()).where(Note.archived == False)
@@ -1,6 +1,6 @@
1
- from sqlmodel import Field
2
- from terp.core import BaseTable
3
-
4
-
5
- class Note(BaseTable, table=True):
6
- title: str = Field(max_length=200)
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class Note(BaseTable, table=True):
6
+ title: str = Field(max_length=200)
@@ -1,7 +1,7 @@
1
- from terp.core import ModuleSpec, Policy, Roles
2
-
3
- spec = ModuleSpec(
4
- name="notes",
5
- router=router,
6
- policy=Policy(read=Roles.VIEWER, write=Roles.EDITOR),
7
- )
1
+ from terp.core import ModuleSpec, Policy, Roles
2
+
3
+ spec = ModuleSpec(
4
+ name="notes",
5
+ router=router,
6
+ policy=Policy(read=Roles.VIEWER, write=Roles.EDITOR),
7
+ )
@@ -1,3 +1,3 @@
1
- @router.get('/', response_model=Page[NoteRead])
2
- def list_notes() -> Page[NoteRead]:
3
- return Page()
1
+ @router.get('/', response_model=Page[NoteRead])
2
+ def list_notes() -> Page[NoteRead]:
3
+ return Page()
@@ -1,10 +1,10 @@
1
- from sqlmodel import Field
2
- from terp.core import BaseSchema
3
-
4
-
5
- class NoteCreate(BaseSchema):
6
- title: str = Field(max_length=200)
7
-
8
-
9
- class NoteRead(BaseSchema):
10
- title: str
1
+ from sqlmodel import Field
2
+ from terp.core import BaseSchema
3
+
4
+
5
+ class NoteCreate(BaseSchema):
6
+ title: str = Field(max_length=200)
7
+
8
+
9
+ class NoteRead(BaseSchema):
10
+ title: str
@@ -1,5 +1,5 @@
1
- from terp.core import BaseService
2
-
3
-
4
- class NoteService(BaseService):
5
- model = Note
1
+ from terp.core import BaseService
2
+
3
+
4
+ class NoteService(BaseService):
5
+ model = Note
@@ -1,3 +1,3 @@
1
- @router.get('/', response_model=Page[NoteRead])
2
- def list_notes() -> Page[NoteRead]:
3
- return Page()
1
+ @router.get('/', response_model=Page[NoteRead])
2
+ def list_notes() -> Page[NoteRead]:
3
+ return Page()
@@ -1,12 +1,12 @@
1
- from datetime import datetime
2
-
3
- from sqlalchemy import DateTime
4
- from sqlmodel import Field
5
-
6
- from terp.core import BaseTable
7
-
8
-
9
- class Note(BaseTable, table=True):
10
- title: str = Field(max_length=200)
11
- # The column type pins the zone, so the stored moment stays unambiguous.
12
- due_at: datetime | None = Field(default=None, sa_type=DateTime(timezone=True))
1
+ from datetime import datetime
2
+
3
+ from sqlalchemy import DateTime
4
+ from sqlmodel import Field
5
+
6
+ from terp.core import BaseTable
7
+
8
+
9
+ class Note(BaseTable, table=True):
10
+ title: str = Field(max_length=200)
11
+ # The column type pins the zone, so the stored moment stays unambiguous.
12
+ due_at: datetime | None = Field(default=None, sa_type=DateTime(timezone=True))
@@ -1,12 +1,12 @@
1
- from datetime import datetime
2
-
3
- from sqlmodel import Field
4
-
5
- from terp.core import BaseTable
6
-
7
-
8
- class Note(BaseTable, table=True):
9
- title: str = Field(max_length=200)
10
- # No column type is named, so this maps to naive storage: the database
11
- # drops the zone of whatever aware value the service hands it.
12
- due_at: datetime | None = Field(default=None, index=True)
1
+ from datetime import datetime
2
+
3
+ from sqlmodel import Field
4
+
5
+ from terp.core import BaseTable
6
+
7
+
8
+ class Note(BaseTable, table=True):
9
+ title: str = Field(max_length=200)
10
+ # No column type is named, so this maps to naive storage: the database
11
+ # drops the zone of whatever aware value the service hands it.
12
+ due_at: datetime | None = Field(default=None, index=True)
@@ -1,13 +1,13 @@
1
- from datetime import datetime
2
-
3
- from sqlalchemy import DateTime
4
- from sqlmodel import Field
5
-
6
- from terp.core import BaseTable
7
-
8
-
9
- class Note(BaseTable, table=True):
10
- title: str = Field(max_length=200)
11
- # An explicit column type that is declared without a timezone is naive
12
- # storage just as thoroughly as naming no type at all.
13
- due_at: datetime = Field(sa_type=DateTime(timezone=False))
1
+ from datetime import datetime
2
+
3
+ from sqlalchemy import DateTime
4
+ from sqlmodel import Field
5
+
6
+ from terp.core import BaseTable
7
+
8
+
9
+ class Note(BaseTable, table=True):
10
+ title: str = Field(max_length=200)
11
+ # An explicit column type that is declared without a timezone is naive
12
+ # storage just as thoroughly as naming no type at all.
13
+ due_at: datetime = Field(sa_type=DateTime(timezone=False))
@@ -1,15 +1,15 @@
1
- from datetime import datetime
2
-
3
- from sqlmodel import Field
4
-
5
- from terp.core import BaseTable
6
-
7
-
8
- class ArchivedMixin:
9
- # Declaring the column one class up does not change where it lands: the
10
- # table still gets a naive timestamp column.
11
- archived_at: datetime | None = Field(default=None)
12
-
13
-
14
- class Note(ArchivedMixin, BaseTable, table=True):
15
- title: str = Field(max_length=200)
1
+ from datetime import datetime
2
+
3
+ from sqlmodel import Field
4
+
5
+ from terp.core import BaseTable
6
+
7
+
8
+ class ArchivedMixin:
9
+ # Declaring the column one class up does not change where it lands: the
10
+ # table still gets a naive timestamp column.
11
+ archived_at: datetime | None = Field(default=None)
12
+
13
+
14
+ class Note(ArchivedMixin, BaseTable, table=True):
15
+ title: str = Field(max_length=200)
@@ -1,3 +1,3 @@
1
- {
2
- "arch-allow-no-dynamic-sql": 1
3
- }
1
+ {
2
+ "arch-allow-no-dynamic-sql": 1
3
+ }
@@ -1,2 +1,2 @@
1
- def legacy_report(session, sql):
2
- return session.execute(text(sql)) # arch-allow-no-dynamic-sql: legacy reporting query, parameterised upstream
1
+ def legacy_report(session, sql):
2
+ return session.execute(text(sql)) # arch-allow-no-dynamic-sql: legacy reporting query, parameterised upstream
@@ -1,4 +1,4 @@
1
- def render_help():
2
- # Marker-shaped text inside a string is not counted by the budget ratchet:
3
- # with an empty budget this app is exactly in balance.
4
- return "suppress with '# arch-allow-no-dynamic-sql: <reason>' after review"
1
+ def render_help():
2
+ # Marker-shaped text inside a string is not counted by the budget ratchet:
3
+ # with an empty budget this app is exactly in balance.
4
+ return "suppress with '# arch-allow-no-dynamic-sql: <reason>' after review"
@@ -1,2 +1,2 @@
1
- def legacy_report(session, sql):
2
- return session.execute(text(sql)) # arch-allow-no-dynamic-sql: legacy reporting query, parameterised upstream
1
+ def legacy_report(session, sql):
2
+ return session.execute(text(sql)) # arch-allow-no-dynamic-sql: legacy reporting query, parameterised upstream
@@ -1,3 +1,3 @@
1
- {
2
- "arch-allow-made-up-rule": 1
3
- }
1
+ {
2
+ "arch-allow-made-up-rule": 1
3
+ }
@@ -1,4 +1,4 @@
1
- def legacy_report():
2
- # A marker (or budget key) that names no rule with a governed opt-out is
3
- # refused outright - it can never be budgeted into legitimacy.
4
- return None # arch-allow-made-up-rule: stale name
1
+ def legacy_report():
2
+ # A marker (or budget key) that names no rule with a governed opt-out is
3
+ # refused outright - it can never be budgeted into legitimacy.
4
+ return None # arch-allow-made-up-rule: stale name