@terpjs/spec 0.14.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 -0
  2. package/README.md +417 -0
  3. package/VERSION +1 -0
  4. package/app-check-report.schema.json +122 -0
  5. package/assurance-profile.schema.json +70 -0
  6. package/catalog/backend/alembic_downgrades_not_empty.json +22 -0
  7. package/catalog/backend/base_query_not_overridden.json +27 -0
  8. package/catalog/backend/canonical_module_shape.json +22 -0
  9. package/catalog/backend/datetime_columns_are_timezone_aware.json +23 -0
  10. package/catalog/backend/escape_hatch_budget.json +21 -0
  11. package/catalog/backend/events_reference_catalog.json +27 -0
  12. package/catalog/backend/input_schemas_exclude_managed_columns.json +27 -0
  13. package/catalog/backend/input_str_fields_have_max_length.json +22 -0
  14. package/catalog/backend/jobs_reference_catalog.json +27 -0
  15. package/catalog/backend/list_routes_paginate.json +32 -0
  16. package/catalog/backend/modules_declare_policy.json +26 -0
  17. package/catalog/backend/mutations_emit_audit.json +26 -0
  18. package/catalog/backend/mutations_require_write_role.json +32 -0
  19. package/catalog/backend/no_adhoc_background_runtime.json +22 -0
  20. package/catalog/backend/no_adhoc_config_decrypt.json +27 -0
  21. package/catalog/backend/no_adhoc_logging_config.json +22 -0
  22. package/catalog/backend/no_adhoc_middleware.json +27 -0
  23. package/catalog/backend/no_adhoc_permission_literals.json +27 -0
  24. package/catalog/backend/no_app_instantiation.json +22 -0
  25. package/catalog/backend/no_blocking_sleep.json +22 -0
  26. package/catalog/backend/no_cross_module_imports.json +21 -0
  27. package/catalog/backend/no_dependency_overrides.json +27 -0
  28. package/catalog/backend/no_destructive_migrations.json +22 -0
  29. package/catalog/backend/no_dynamic_sql.json +22 -0
  30. package/catalog/backend/no_empty_tests.json +22 -0
  31. package/catalog/backend/no_eval_or_exec.json +22 -0
  32. package/catalog/backend/no_hardcoded_credentials.json +21 -0
  33. package/catalog/backend/no_internal_imports.json +22 -0
  34. package/catalog/backend/no_manual_actor_stamping.json +27 -0
  35. package/catalog/backend/no_manual_ownership_checks.json +32 -0
  36. package/catalog/backend/no_manual_scope_filtering.json +27 -0
  37. package/catalog/backend/no_manual_table_schema.json +22 -0
  38. package/catalog/backend/no_manual_version_assignment.json +22 -0
  39. package/catalog/backend/no_mutable_default_args.json +22 -0
  40. package/catalog/backend/no_naive_datetime.json +22 -0
  41. package/catalog/backend/no_oversized_python_files.json +22 -0
  42. package/catalog/backend/no_print.json +22 -0
  43. package/catalog/backend/no_raw_app_routes.json +26 -0
  44. package/catalog/backend/no_raw_connection_access.json +27 -0
  45. package/catalog/backend/no_raw_file_references.json +27 -0
  46. package/catalog/backend/no_raw_outbound_http.json +22 -0
  47. package/catalog/backend/no_raw_session_construction.json +22 -0
  48. package/catalog/backend/no_star_imports.json +22 -0
  49. package/catalog/backend/no_todo_fixme.json +22 -0
  50. package/catalog/backend/no_unique_columns_on_soft_delete_models.json +22 -0
  51. package/catalog/backend/offset_queries_declare_ordering.json +22 -0
  52. package/catalog/backend/path_id_params_are_uuid.json +22 -0
  53. package/catalog/backend/policy_refs_resolve.json +27 -0
  54. package/catalog/backend/public_modules_are_read_only.json +26 -0
  55. package/catalog/backend/reads_use_base_query.json +27 -0
  56. package/catalog/backend/response_model_not_table_model.json +27 -0
  57. package/catalog/backend/routes_declare_response_model.json +27 -0
  58. package/catalog/backend/safe_methods_are_read_only.json +31 -0
  59. package/catalog/backend/schemas_exclude_sensitive_fields.json +32 -0
  60. package/catalog/backend/session_imported_from_sqlmodel.json +22 -0
  61. package/catalog/backend/table_models_use_base_table.json +22 -0
  62. package/catalog/backend/tables_have_migrations.json +27 -0
  63. package/catalog/backend/tenant_scoped_models_use_scoped_service.json +22 -0
  64. package/catalog/backend/ungoverned_escape_hatch.json +21 -0
  65. package/catalog/backend/update_schemas_inherit_base_update_schema.json +32 -0
  66. package/catalog/frontend/escape-hatch.json +21 -0
  67. package/catalog/frontend/generated-client-only.json +26 -0
  68. package/catalog/frontend/layout-contract.json +26 -0
  69. package/catalog/frontend/no-cross-module-imports.json +21 -0
  70. package/catalog/frontend/no-deep-imports.json +25 -0
  71. package/catalog/frontend/no-dom-html-injection.json +22 -0
  72. package/catalog/frontend/no-eval.json +21 -0
  73. package/catalog/frontend/no-inline-styling.json +25 -0
  74. package/catalog/frontend/no-style-imports.json +24 -0
  75. package/catalog/frontend/no-unsafe-href.json +21 -0
  76. package/catalog/frontend/no-unsafe-target-blank.json +21 -0
  77. package/catalog/frontend/router-links.json +22 -0
  78. package/catalog/frontend/token-styled-elements.json +25 -0
  79. package/catalog/schema.json +95 -0
  80. package/corpus/PENDING.json +4 -0
  81. package/corpus/RESIDUALS.json +21 -0
  82. package/corpus/backend/alembic_downgrades_not_empty/compliant-01/modules/notes/migrations/versions/0001_change.py +6 -0
  83. package/corpus/backend/alembic_downgrades_not_empty/violation-01/modules/notes/migrations/versions/0001_change.py +6 -0
  84. package/corpus/backend/base_query_not_overridden/compliant-01/modules/notes/service.py +8 -0
  85. package/corpus/backend/base_query_not_overridden/compliant-02/modules/notes/service.py +8 -0
  86. package/corpus/backend/base_query_not_overridden/violation-01/modules/notes/service.py +8 -0
  87. package/corpus/backend/base_query_not_overridden/violation-02/expected-findings.json +7 -0
  88. package/corpus/backend/base_query_not_overridden/violation-02/modules/notes/service.py +8 -0
  89. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/models.py +6 -0
  90. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/module.py +7 -0
  91. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/router.py +3 -0
  92. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/schemas.py +10 -0
  93. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/service.py +5 -0
  94. package/corpus/backend/canonical_module_shape/violation-01/modules/notes/router.py +3 -0
  95. package/corpus/backend/datetime_columns_are_timezone_aware/compliant-01/modules/notes/models.py +12 -0
  96. package/corpus/backend/datetime_columns_are_timezone_aware/violation-01/modules/notes/models.py +12 -0
  97. package/corpus/backend/datetime_columns_are_timezone_aware/violation-02/modules/notes/models.py +13 -0
  98. package/corpus/backend/datetime_columns_are_timezone_aware/violation-03/modules/notes/models.py +15 -0
  99. package/corpus/backend/escape_hatch_budget/compliant-01/escape-hatch-budget.json +3 -0
  100. package/corpus/backend/escape_hatch_budget/compliant-01/modules/notes/service.py +2 -0
  101. package/corpus/backend/escape_hatch_budget/compliant-02/escape-hatch-budget.json +1 -0
  102. package/corpus/backend/escape_hatch_budget/compliant-02/modules/notes/service.py +4 -0
  103. package/corpus/backend/escape_hatch_budget/violation-01/escape-hatch-budget.json +1 -0
  104. package/corpus/backend/escape_hatch_budget/violation-01/modules/notes/service.py +2 -0
  105. package/corpus/backend/escape_hatch_budget/violation-02/escape-hatch-budget.json +3 -0
  106. package/corpus/backend/escape_hatch_budget/violation-02/modules/notes/service.py +4 -0
  107. package/corpus/backend/events_reference_catalog/compliant-01/modules/notes/module.py +9 -0
  108. package/corpus/backend/events_reference_catalog/violation-01/modules/notes/module.py +8 -0
  109. package/corpus/backend/input_schemas_exclude_managed_columns/compliant-01/modules/notes/schemas.py +6 -0
  110. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/expected-findings.json +7 -0
  111. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/modules/notes/schemas.py +9 -0
  112. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/expected-findings.json +12 -0
  113. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/modules/notes/router.py +14 -0
  114. package/corpus/backend/input_str_fields_have_max_length/compliant-01/modules/notes/schemas.py +4 -0
  115. package/corpus/backend/input_str_fields_have_max_length/violation-01/modules/notes/schemas.py +2 -0
  116. package/corpus/backend/jobs_reference_catalog/compliant-01/modules/notes/service.py +5 -0
  117. package/corpus/backend/jobs_reference_catalog/violation-01/modules/notes/service.py +2 -0
  118. package/corpus/backend/list_routes_paginate/compliant-01/modules/notes/router.py +6 -0
  119. package/corpus/backend/list_routes_paginate/violation-01/modules/notes/router.py +3 -0
  120. package/corpus/backend/modules_declare_policy/compliant-01/modules/notes/module.py +7 -0
  121. package/corpus/backend/modules_declare_policy/violation-01/modules/notes/module.py +6 -0
  122. package/corpus/backend/mutations_emit_audit/compliant-01/modules/notes/service.py +9 -0
  123. package/corpus/backend/mutations_emit_audit/violation-01/modules/notes/service.py +4 -0
  124. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/module.py +7 -0
  125. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/router.py +3 -0
  126. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/module.py +7 -0
  127. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/router.py +3 -0
  128. package/corpus/backend/mutations_require_write_role/violation-01/expected-findings.json +7 -0
  129. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/module.py +7 -0
  130. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/router.py +3 -0
  131. package/corpus/backend/mutations_require_write_role/violation-02/expected-findings.json +7 -0
  132. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/module.py +7 -0
  133. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/router.py +5 -0
  134. package/corpus/backend/mutations_require_write_role/violation-03/expected-findings.json +7 -0
  135. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/module.py +10 -0
  136. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/router.py +3 -0
  137. package/corpus/backend/no_adhoc_background_runtime/compliant-01/modules/notes/service.py +9 -0
  138. package/corpus/backend/no_adhoc_background_runtime/violation-01/modules/notes/service.py +5 -0
  139. package/corpus/backend/no_adhoc_background_runtime/violation-02/modules/notes/service.py +3 -0
  140. package/corpus/backend/no_adhoc_config_decrypt/compliant-01/modules/billing/service.py +2 -0
  141. package/corpus/backend/no_adhoc_config_decrypt/violation-01/modules/billing/service.py +2 -0
  142. package/corpus/backend/no_adhoc_logging_config/compliant-01/modules/notes/service.py +3 -0
  143. package/corpus/backend/no_adhoc_logging_config/violation-01/modules/notes/service.py +3 -0
  144. package/corpus/backend/no_adhoc_middleware/compliant-01/main.py +3 -0
  145. package/corpus/backend/no_adhoc_middleware/violation-01/main.py +4 -0
  146. package/corpus/backend/no_adhoc_middleware/violation-02/main.py +5 -0
  147. package/corpus/backend/no_adhoc_permission_literals/compliant-01/modules/notes/module.py +3 -0
  148. package/corpus/backend/no_adhoc_permission_literals/compliant-02/modules/notes/router.py +5 -0
  149. package/corpus/backend/no_adhoc_permission_literals/violation-01/expected-findings.json +7 -0
  150. package/corpus/backend/no_adhoc_permission_literals/violation-01/modules/notes/module.py +1 -0
  151. package/corpus/backend/no_adhoc_permission_literals/violation-02/expected-findings.json +7 -0
  152. package/corpus/backend/no_adhoc_permission_literals/violation-02/modules/notes/router.py +2 -0
  153. package/corpus/backend/no_adhoc_permission_literals/violation-03/expected-findings.json +12 -0
  154. package/corpus/backend/no_adhoc_permission_literals/violation-03/modules/notes/module.py +10 -0
  155. package/corpus/backend/no_app_instantiation/compliant-01/main.py +3 -0
  156. package/corpus/backend/no_app_instantiation/violation-01/main.py +3 -0
  157. package/corpus/backend/no_blocking_sleep/compliant-01/modules/notes/service.py +2 -0
  158. package/corpus/backend/no_blocking_sleep/violation-01/modules/notes/service.py +5 -0
  159. package/corpus/backend/no_cross_module_imports/compliant-01/modules/a/service.py +1 -0
  160. package/corpus/backend/no_cross_module_imports/violation-01/modules/a/service.py +1 -0
  161. package/corpus/backend/no_cross_module_imports/violation-02/modules/a/service.py +1 -0
  162. package/corpus/backend/no_dependency_overrides/compliant-01/main.py +12 -0
  163. package/corpus/backend/no_dependency_overrides/violation-01/main.py +11 -0
  164. package/corpus/backend/no_destructive_migrations/compliant-01/modules/notes/migrations/versions/0001_change.py +3 -0
  165. package/corpus/backend/no_destructive_migrations/compliant-02/modules/notes/migrations/versions/0001_change.py +8 -0
  166. package/corpus/backend/no_destructive_migrations/violation-01/expected-findings.json +7 -0
  167. package/corpus/backend/no_destructive_migrations/violation-01/modules/notes/migrations/versions/0001_change.py +2 -0
  168. package/corpus/backend/no_destructive_migrations/violation-02/expected-findings.json +7 -0
  169. package/corpus/backend/no_destructive_migrations/violation-02/modules/notes/migrations/versions/0001_change.py +2 -0
  170. package/corpus/backend/no_destructive_migrations/violation-03/expected-findings.json +7 -0
  171. package/corpus/backend/no_destructive_migrations/violation-03/modules/notes/migrations/versions/0001_change.py +3 -0
  172. package/corpus/backend/no_destructive_migrations/violation-04/expected-findings.json +12 -0
  173. package/corpus/backend/no_destructive_migrations/violation-04/modules/notes/migrations/versions/0001_change.py +6 -0
  174. package/corpus/backend/no_destructive_migrations/violation-05/expected-findings.json +12 -0
  175. package/corpus/backend/no_destructive_migrations/violation-05/modules/notes/migrations/versions/0001_change.py +3 -0
  176. package/corpus/backend/no_dynamic_sql/compliant-01/modules/notes/service.py +1 -0
  177. package/corpus/backend/no_dynamic_sql/compliant-02/modules/notes/service.py +20 -0
  178. package/corpus/backend/no_dynamic_sql/violation-01/modules/notes/service.py +2 -0
  179. package/corpus/backend/no_dynamic_sql/violation-02/modules/notes/service.py +2 -0
  180. package/corpus/backend/no_dynamic_sql/violation-03/modules/notes/service.py +7 -0
  181. package/corpus/backend/no_dynamic_sql/violation-04/modules/notes/service.py +16 -0
  182. package/corpus/backend/no_empty_tests/compliant-01/tests/test_notes.py +2 -0
  183. package/corpus/backend/no_empty_tests/violation-01/tests/test_notes.py +2 -0
  184. package/corpus/backend/no_eval_or_exec/compliant-01/modules/notes/service.py +2 -0
  185. package/corpus/backend/no_eval_or_exec/violation-01/modules/notes/service.py +2 -0
  186. package/corpus/backend/no_hardcoded_credentials/compliant-01/modules/billing/service.py +3 -0
  187. package/corpus/backend/no_hardcoded_credentials/compliant-02/modules/billing/service.py +18 -0
  188. package/corpus/backend/no_hardcoded_credentials/violation-01/expected-findings.json +7 -0
  189. package/corpus/backend/no_hardcoded_credentials/violation-01/modules/billing/service.py +2 -0
  190. package/corpus/backend/no_hardcoded_credentials/violation-02/expected-findings.json +7 -0
  191. package/corpus/backend/no_hardcoded_credentials/violation-02/modules/billing/service.py +1 -0
  192. package/corpus/backend/no_hardcoded_credentials/violation-03/expected-findings.json +22 -0
  193. package/corpus/backend/no_hardcoded_credentials/violation-03/modules/billing/service.py +11 -0
  194. package/corpus/backend/no_hardcoded_credentials/violation-04/expected-findings.json +12 -0
  195. package/corpus/backend/no_hardcoded_credentials/violation-04/modules/billing/service.py +8 -0
  196. package/corpus/backend/no_hardcoded_credentials/violation-05/expected-findings.json +12 -0
  197. package/corpus/backend/no_hardcoded_credentials/violation-05/modules/billing/service.py +4 -0
  198. package/corpus/backend/no_internal_imports/compliant-01/modules/notes/service.py +1 -0
  199. package/corpus/backend/no_internal_imports/violation-01/modules/notes/service.py +1 -0
  200. package/corpus/backend/no_manual_actor_stamping/compliant-01/modules/notes/service.py +5 -0
  201. package/corpus/backend/no_manual_actor_stamping/compliant-02/modules/notes/schemas.py +12 -0
  202. package/corpus/backend/no_manual_actor_stamping/violation-01/modules/notes/service.py +3 -0
  203. package/corpus/backend/no_manual_actor_stamping/violation-02/expected-findings.json +7 -0
  204. package/corpus/backend/no_manual_actor_stamping/violation-02/modules/notes/service.py +4 -0
  205. package/corpus/backend/no_manual_ownership_checks/compliant-01/modules/journals/service.py +3 -0
  206. package/corpus/backend/no_manual_ownership_checks/compliant-02/modules/journals/schemas.py +11 -0
  207. package/corpus/backend/no_manual_ownership_checks/violation-01/modules/journals/service.py +3 -0
  208. package/corpus/backend/no_manual_ownership_checks/violation-02/expected-findings.json +7 -0
  209. package/corpus/backend/no_manual_ownership_checks/violation-02/modules/journals/service.py +9 -0
  210. package/corpus/backend/no_manual_ownership_checks/violation-03/expected-findings.json +7 -0
  211. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/jobs.py +1 -0
  212. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/models.py +7 -0
  213. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/module.py +11 -0
  214. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/service.py +7 -0
  215. package/corpus/backend/no_manual_scope_filtering/compliant-01/modules/notes/service.py +8 -0
  216. package/corpus/backend/no_manual_scope_filtering/compliant-02/modules/notes/schemas.py +12 -0
  217. package/corpus/backend/no_manual_scope_filtering/violation-01/modules/notes/service.py +2 -0
  218. package/corpus/backend/no_manual_scope_filtering/violation-02/modules/notes/service.py +2 -0
  219. package/corpus/backend/no_manual_scope_filtering/violation-03/expected-findings.json +7 -0
  220. package/corpus/backend/no_manual_scope_filtering/violation-03/modules/notes/service.py +7 -0
  221. package/corpus/backend/no_manual_table_schema/compliant-01/modules/notes/models.py +6 -0
  222. package/corpus/backend/no_manual_table_schema/violation-01/modules/notes/models.py +8 -0
  223. package/corpus/backend/no_manual_version_assignment/compliant-01/modules/notes/service.py +5 -0
  224. package/corpus/backend/no_manual_version_assignment/violation-01/modules/notes/service.py +3 -0
  225. package/corpus/backend/no_manual_version_assignment/violation-02/modules/notes/service.py +3 -0
  226. package/corpus/backend/no_manual_version_assignment/violation-03/modules/notes/service.py +3 -0
  227. package/corpus/backend/no_mutable_default_args/compliant-01/modules/notes/service.py +3 -0
  228. package/corpus/backend/no_mutable_default_args/violation-01/modules/notes/service.py +2 -0
  229. package/corpus/backend/no_naive_datetime/compliant-01/modules/notes/service.py +2 -0
  230. package/corpus/backend/no_naive_datetime/violation-01/modules/notes/service.py +2 -0
  231. package/corpus/backend/no_naive_datetime/violation-02/modules/notes/service.py +2 -0
  232. package/corpus/backend/no_oversized_python_files/compliant-01/modules/notes/service.py +9 -0
  233. package/corpus/backend/no_oversized_python_files/violation-01/modules/notes/service.py +530 -0
  234. package/corpus/backend/no_print/compliant-01/modules/notes/service.py +7 -0
  235. package/corpus/backend/no_print/violation-01/modules/notes/service.py +2 -0
  236. package/corpus/backend/no_raw_app_routes/compliant-01/main.py +12 -0
  237. package/corpus/backend/no_raw_app_routes/violation-01/main.py +10 -0
  238. package/corpus/backend/no_raw_app_routes/violation-02/main.py +16 -0
  239. package/corpus/backend/no_raw_app_routes/violation-03/main.py +12 -0
  240. package/corpus/backend/no_raw_app_routes/violation-04/main.py +10 -0
  241. package/corpus/backend/no_raw_app_routes/violation-05/main.py +12 -0
  242. package/corpus/backend/no_raw_app_routes/violation-06/main.py +10 -0
  243. package/corpus/backend/no_raw_connection_access/compliant-01/modules/notes/service.py +5 -0
  244. package/corpus/backend/no_raw_connection_access/violation-01/modules/notes/service.py +2 -0
  245. package/corpus/backend/no_raw_connection_access/violation-02/modules/notes/service.py +2 -0
  246. package/corpus/backend/no_raw_file_references/compliant-01/modules/notes/models.py +10 -0
  247. package/corpus/backend/no_raw_file_references/violation-01/modules/notes/models.py +9 -0
  248. package/corpus/backend/no_raw_outbound_http/compliant-01/modules/notes/service.py +2 -0
  249. package/corpus/backend/no_raw_outbound_http/compliant-03/modules/notes/service.py +12 -0
  250. package/corpus/backend/no_raw_outbound_http/violation-01/expected-findings.json +7 -0
  251. package/corpus/backend/no_raw_outbound_http/violation-01/modules/notes/service.py +1 -0
  252. package/corpus/backend/no_raw_outbound_http/violation-02/expected-findings.json +7 -0
  253. package/corpus/backend/no_raw_outbound_http/violation-02/modules/notes/service.py +1 -0
  254. package/corpus/backend/no_raw_outbound_http/violation-03/expected-findings.json +7 -0
  255. package/corpus/backend/no_raw_outbound_http/violation-03/modules/notes/service.py +1 -0
  256. package/corpus/backend/no_raw_outbound_http/violation-04/expected-findings.json +12 -0
  257. package/corpus/backend/no_raw_outbound_http/violation-04/modules/notes/service.py +7 -0
  258. package/corpus/backend/no_raw_outbound_http/violation-05/expected-findings.json +12 -0
  259. package/corpus/backend/no_raw_outbound_http/violation-05/modules/notes/service.py +5 -0
  260. package/corpus/backend/no_raw_outbound_http/violation-06/expected-findings.json +12 -0
  261. package/corpus/backend/no_raw_outbound_http/violation-06/modules/notes/service.py +2 -0
  262. package/corpus/backend/no_raw_session_construction/compliant-01/modules/notes/service.py +3 -0
  263. package/corpus/backend/no_raw_session_construction/violation-01/modules/notes/service.py +3 -0
  264. package/corpus/backend/no_star_imports/compliant-01/modules/notes/service.py +1 -0
  265. package/corpus/backend/no_star_imports/violation-01/modules/notes/service.py +1 -0
  266. package/corpus/backend/no_todo_fixme/compliant-01/modules/notes/service.py +3 -0
  267. package/corpus/backend/no_todo_fixme/violation-01/modules/notes/service.py +3 -0
  268. package/corpus/backend/no_unique_columns_on_soft_delete_models/compliant-01/modules/notes/models.py +17 -0
  269. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/expected-findings.json +7 -0
  270. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/modules/notes/models.py +6 -0
  271. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/expected-findings.json +7 -0
  272. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/modules/notes/models.py +13 -0
  273. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/expected-findings.json +7 -0
  274. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/modules/notes/models.py +16 -0
  275. package/corpus/backend/offset_queries_declare_ordering/compliant-01/modules/notes/service.py +4 -0
  276. package/corpus/backend/offset_queries_declare_ordering/violation-01/modules/notes/service.py +2 -0
  277. package/corpus/backend/path_id_params_are_uuid/compliant-01/modules/notes/router.py +6 -0
  278. package/corpus/backend/path_id_params_are_uuid/violation-01/modules/notes/router.py +6 -0
  279. package/corpus/backend/policy_refs_resolve/compliant-01/modules/notes/module.py +7 -0
  280. package/corpus/backend/policy_refs_resolve/violation-01/modules/notes/module.py +8 -0
  281. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/module.py +7 -0
  282. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/router.py +3 -0
  283. package/corpus/backend/public_modules_are_read_only/violation-01/expected-findings.json +7 -0
  284. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/module.py +7 -0
  285. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/router.py +3 -0
  286. package/corpus/backend/public_modules_are_read_only/violation-02/expected-findings.json +7 -0
  287. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/module.py +7 -0
  288. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/router.py +5 -0
  289. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/models.py +6 -0
  290. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/service.py +8 -0
  291. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/models.py +9 -0
  292. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/service.py +9 -0
  293. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/models.py +6 -0
  294. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/service.py +9 -0
  295. package/corpus/backend/reads_use_base_query/violation-02/expected-findings.json +7 -0
  296. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/models.py +6 -0
  297. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/service.py +8 -0
  298. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/models.py +6 -0
  299. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/router.py +3 -0
  300. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/schemas.py +5 -0
  301. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/models.py +6 -0
  302. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/router.py +3 -0
  303. package/corpus/backend/routes_declare_response_model/compliant-01/modules/notes/router.py +8 -0
  304. package/corpus/backend/routes_declare_response_model/violation-01/modules/notes/router.py +3 -0
  305. package/corpus/backend/safe_methods_are_read_only/compliant-01/modules/notes/router.py +6 -0
  306. package/corpus/backend/safe_methods_are_read_only/compliant-02/modules/notes/router.py +5 -0
  307. package/corpus/backend/safe_methods_are_read_only/violation-01/expected-findings.json +7 -0
  308. package/corpus/backend/safe_methods_are_read_only/violation-01/modules/notes/router.py +3 -0
  309. package/corpus/backend/safe_methods_are_read_only/violation-02/expected-findings.json +7 -0
  310. package/corpus/backend/safe_methods_are_read_only/violation-02/modules/notes/router.py +5 -0
  311. package/corpus/backend/safe_methods_are_read_only/violation-03/expected-findings.json +7 -0
  312. package/corpus/backend/safe_methods_are_read_only/violation-03/modules/notes/router.py +3 -0
  313. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-01/modules/users/schemas.py +5 -0
  314. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-02/modules/accounts/schemas.py +12 -0
  315. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/expected-findings.json +7 -0
  316. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/modules/users/schemas.py +3 -0
  317. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/expected-findings.json +7 -0
  318. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/modules/connectors/router.py +8 -0
  319. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/expected-findings.json +17 -0
  320. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/modules/integrations/schemas.py +7 -0
  321. package/corpus/backend/session_imported_from_sqlmodel/compliant-01/modules/notes/service.py +5 -0
  322. package/corpus/backend/session_imported_from_sqlmodel/violation-01/modules/notes/service.py +5 -0
  323. package/corpus/backend/table_models_use_base_table/compliant-01/modules/notes/models.py +6 -0
  324. package/corpus/backend/table_models_use_base_table/violation-01/modules/notes/models.py +5 -0
  325. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/migrations/versions/0a1b2c3d4e5f_create_notes_tables.py +22 -0
  326. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/models.py +6 -0
  327. package/corpus/backend/tables_have_migrations/compliant-02/capabilities/ledger/models.py +6 -0
  328. package/corpus/backend/tables_have_migrations/violation-01/expected-findings.json +7 -0
  329. package/corpus/backend/tables_have_migrations/violation-01/modules/notes/models.py +6 -0
  330. package/corpus/backend/tables_have_migrations/violation-02/expected-findings.json +7 -0
  331. package/corpus/backend/tables_have_migrations/violation-02/modules/notes/models.py +10 -0
  332. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/models.py +6 -0
  333. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/service.py +5 -0
  334. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/models.py +8 -0
  335. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/service.py +5 -0
  336. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/models.py +6 -0
  337. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/service.py +5 -0
  338. package/corpus/backend/ungoverned_escape_hatch/compliant-01/modules/notes/service.py +5 -0
  339. package/corpus/backend/ungoverned_escape_hatch/compliant-02/modules/notes/service.py +4 -0
  340. package/corpus/backend/ungoverned_escape_hatch/violation-01/modules/notes/service.py +2 -0
  341. package/corpus/backend/update_schemas_inherit_base_update_schema/compliant-01/modules/notes/schemas.py +2 -0
  342. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-01/modules/notes/schemas.py +2 -0
  343. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-02/modules/notes/schemas.py +5 -0
  344. package/corpus/frontend/escape-hatch/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  345. package/corpus/frontend/escape-hatch/violation-01/src/modules/widgets/Widget.tsx +4 -0
  346. package/corpus/frontend/escape-hatch/violation-02/src/modules/widgets/Widget.tsx +7 -0
  347. package/corpus/frontend/escape-hatch/violation-03/src/modules/widgets/Widget.tsx +7 -0
  348. package/corpus/frontend/generated-client-only/compliant-01/src/modules/widgets/Widget.tsx +6 -0
  349. package/corpus/frontend/generated-client-only/compliant-02/src/modules/widgets/Widget.tsx +21 -0
  350. package/corpus/frontend/generated-client-only/compliant-03/src/modules/widgets/Widget.tsx +7 -0
  351. package/corpus/frontend/generated-client-only/compliant-04/src/modules/widgets/Widget.tsx +17 -0
  352. package/corpus/frontend/generated-client-only/violation-01/src/modules/widgets/Widget.tsx +3 -0
  353. package/corpus/frontend/generated-client-only/violation-02/src/modules/widgets/Widget.tsx +9 -0
  354. package/corpus/frontend/generated-client-only/violation-03/src/modules/widgets/Widget.tsx +14 -0
  355. package/corpus/frontend/generated-client-only/violation-04/src/modules/widgets/Widget.tsx +6 -0
  356. package/corpus/frontend/layout-contract/compliant-01/layout-contract.json +3 -0
  357. package/corpus/frontend/layout-contract/compliant-01/src/modules/widgets/Widget.tsx +8 -0
  358. package/corpus/frontend/layout-contract/violation-01/layout-contract.json +3 -0
  359. package/corpus/frontend/layout-contract/violation-01/src/modules/widgets/Widget.tsx +8 -0
  360. package/corpus/frontend/no-cross-module-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  361. package/corpus/frontend/no-cross-module-imports/violation-01/src/modules/widgets/Widget.tsx +3 -0
  362. package/corpus/frontend/no-deep-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  363. package/corpus/frontend/no-deep-imports/violation-01/src/modules/widgets/Widget.tsx +2 -0
  364. package/corpus/frontend/no-dom-html-injection/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  365. package/corpus/frontend/no-dom-html-injection/compliant-02/src/modules/widgets/Widget.tsx +11 -0
  366. package/corpus/frontend/no-dom-html-injection/violation-01/src/modules/widgets/Widget.tsx +3 -0
  367. package/corpus/frontend/no-dom-html-injection/violation-02/src/modules/widgets/Widget.tsx +7 -0
  368. package/corpus/frontend/no-dom-html-injection/violation-03/src/modules/widgets/Widget.tsx +12 -0
  369. package/corpus/frontend/no-eval/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  370. package/corpus/frontend/no-eval/compliant-02/src/modules/widgets/Widget.tsx +11 -0
  371. package/corpus/frontend/no-eval/violation-01/src/modules/widgets/Widget.tsx +3 -0
  372. package/corpus/frontend/no-eval/violation-02/src/modules/widgets/Widget.tsx +7 -0
  373. package/corpus/frontend/no-eval/violation-03/src/modules/widgets/Widget.tsx +10 -0
  374. package/corpus/frontend/no-eval/violation-04/src/modules/widgets/Widget.tsx +6 -0
  375. package/corpus/frontend/no-inline-styling/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  376. package/corpus/frontend/no-inline-styling/violation-01/src/modules/widgets/Widget.tsx +3 -0
  377. package/corpus/frontend/no-inline-styling/violation-02/src/modules/widgets/Widget.tsx +6 -0
  378. package/corpus/frontend/no-style-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  379. package/corpus/frontend/no-style-imports/violation-01/src/modules/widgets/Widget.tsx +2 -0
  380. package/corpus/frontend/no-unsafe-href/compliant-01/src/modules/widgets/Widget.tsx +3 -0
  381. package/corpus/frontend/no-unsafe-href/violation-01/src/modules/widgets/Widget.tsx +3 -0
  382. package/corpus/frontend/no-unsafe-target-blank/compliant-01/src/modules/widgets/Widget.tsx +3 -0
  383. package/corpus/frontend/no-unsafe-target-blank/violation-01/src/modules/widgets/Widget.tsx +3 -0
  384. package/corpus/frontend/router-links/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  385. package/corpus/frontend/router-links/violation-01/src/modules/widgets/Widget.tsx +3 -0
  386. package/corpus/frontend/token-styled-elements/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  387. package/corpus/frontend/token-styled-elements/compliant-02/src/modules/widgets/Widget.tsx +4 -0
  388. package/corpus/frontend/token-styled-elements/violation-01/src/modules/widgets/Widget.tsx +3 -0
  389. package/findings.schema.json +40 -0
  390. package/package.json +23 -0
  391. package/restricted-surface.json +10 -0
  392. package/scorecard.schema.json +52 -0
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,417 @@
1
+ # The Terp Standard — rule catalog + violation corpus
2
+
3
+ This repository is the **stack-neutral specification** of Terp's
4
+ secure-by-default rules (ADR 0080, made consumable by ADR 0081). The framework
5
+ ([terp-framework](https://github.com/AITT-NL/terp-framework)) is the
6
+ **reference implementation**; this spec is what any other stack would
7
+ implement and be verified against. It is deliberately self-contained (no
8
+ `terp.*` imports, plain JSON + sample files) and consumed as a **package**,
9
+ never a repo path (ADR 0082).
10
+
11
+ ## The package seam (ADR 0082)
12
+
13
+ The directory doubles as two thin distributions over one data set:
14
+
15
+ - **`terp-spec`** (Python, `pyproject.toml`): a dependency-free `terp_spec`
16
+ accessor — `spec_dir()` returns the on-disk spec root, `spec_version()` the
17
+ `VERSION` semver. A uv workspace member; the framework's certification tests
18
+ locate the spec only through it.
19
+ - **`@terpjs/spec`** (npm, `package.json`): data only; consumers resolve the
20
+ spec root via `require.resolve("@terpjs/spec/package.json")`. An npm workspace
21
+ member; the ESLint adapter's corpus/surface tests depend on it.
22
+
23
+ Both are **published from the tag workflow** (ADR 0086) — `terp-spec` on PyPI,
24
+ `@terpjs/spec` on npm, via Trusted Publishing (OIDC), so a consumer pins an
25
+ ordinary version. Pinning the git tag instead keeps working.
26
+
27
+ Both manifests carry the **spec version** (`VERSION`) — independent of the
28
+ platform's lockstep release version, per ADR 0081's certification model; the
29
+ standalone suite holds the three declarations equal.
30
+
31
+ The spec-only validations (versioning, schema validity, the refused surface's
32
+ shape, the corpus ratchet and directory discipline) live in `spec/tests/` and
33
+ run standalone (`python -m pytest` from `spec/`; CI: the path-filtered
34
+ `spec.yml` workflow). The framework gate keeps the *parity* half — everything
35
+ that needs the live implementations. Because the framework consumes a **pinned
36
+ release**, this repository's CI additionally runs `certify-against-reference`:
37
+ it checks out the reference framework, substitutes the candidate spec for the
38
+ pinned `terp-spec` / `@terpjs/spec`, and runs the framework's parity + corpus
39
+ certification — so a catalog or corpus change is proven against the live
40
+ implementations *before* release, and the framework's later pin bump re-proves
41
+ it in the framework's own gate. Splitting the spec out is then purely a
42
+ manifest change: move `spec/` + `spec.yml`, repin `terp-spec` / `@terpjs/spec`
43
+ from workspace sources to a git tag or registry release
44
+ (`tests/architecture/test_repo_split_readiness.py` fails the build if code
45
+ re-couples the units by path).
46
+
47
+ The artifacts:
48
+
49
+ 1. **`VERSION`** — the semver of the standard. A checker certified against the
50
+ corpus records the spec version it was certified for.
51
+ 2. **The catalog** (`catalog/`) — one JSON document per rule (validated by
52
+ `catalog/schema.json`): what the rule is, why it exists, and how it is
53
+ enforced today.
54
+ 3. **The corpus** (`corpus/`) — violating and compliant code samples per rule:
55
+ the executable meaning of the rule. `corpus/PENDING.json` is the coverage
56
+ ratchet — the explicit list of rules still without cases, which only shrinks.
57
+ 4. **The finding format** (`findings.schema.json`) — what a conformant checker
58
+ emits, so checkers are interoperable and a corpus harness can be generic.
59
+ 5. **The check-report format** (`app-check-report.schema.json`) — the complete
60
+ result of one checker invocation over one application tree: the finding
61
+ format plus the run's own evaluated-rule inventory, spec version, checker
62
+ identity and verdict, so a driving tool can join per-rule verdicts to the
63
+ catalog fail-closed.
64
+ 6. **The refused surface** (`restricted-surface.json`) — the stack-neutral,
65
+ normative declaration of the raw frontend primitives an app module must not
66
+ author (elements, attributes, egress globals/member calls, stylesheet
67
+ extensions, deep-import segments). The portable prohibition rules cite it
68
+ structurally (the `restricted_surface` catalog field); which sanctioned
69
+ component answers each primitive is per-stack configuration.
70
+ 7. **The residual ratchet** (`corpus/RESIDUALS.json`) — the statically-erased
71
+ or renamed forms deliberately outside the corpus contract, per rule, as
72
+ shrink-only data (see "Detector boundaries" below).
73
+ 8. **The scorecard format** (`scorecard.schema.json`) — the machine-readable
74
+ certification summary a conformant checker emits (spec version, per-rule
75
+ verdicts over the corpus, residuals claimed), so "certified against spec
76
+ X.Y.Z" is a verifiable artifact instead of a claim.
77
+ 9. **The changelog** (`CHANGELOG.md`) — the change history keyed to `VERSION`
78
+ (the top entry must match, held by the spec suite), so a checker certified
79
+ against an earlier version can see exactly what changed since.
80
+ 10. **The rule pages** (`docs/rules/`) — plain-language documentation generated
81
+ from the catalog (`tools/generate_rule_docs.py`; regenerate-and-compare
82
+ parity in the spec suite, so the pages cannot drift from the data).
83
+
84
+ Everything is locked to the live implementations by build-time parity tests
85
+ (`tests/architecture/test_spec_catalog.py`, `tests/architecture/test_spec_corpus.py`,
86
+ `packages/frontend/eslint-boundaries/src/corpus.test.js` and
87
+ `packages/frontend/eslint-boundaries/src/surface.test.js` — the latter holds the
88
+ reference adapter to `restricted-surface.json` both structurally and
89
+ behaviourally), following the
90
+ same "docs can't lie" discipline as the rest of the gate: a rule cannot ship
91
+ without a catalog entry, a catalog entry cannot outlive its rule, and every
92
+ enforcement reference must resolve to real code.
93
+
94
+ ## Scope: what the standard claims — and delegates
95
+
96
+ The catalog is **Terp-specific secure architecture, not complete application
97
+ security**. A rule is admitted only when Terp provides a *privileged seam* or
98
+ a *more precise invariant* than a generic analyzer can state — a framework
99
+ chokepoint to pair with (`runtime.applicability: required`), a
100
+ refused-surface entry, a trait/registry the rule holds code to. Generic
101
+ vulnerability classes that stock security analyzers already detect well
102
+ (command injection, unsafe deserialization, weak security randomness, …) are
103
+ **delegated, never duplicated**: a conformant toolchain
104
+ runs a generic security baseline for its language *next to* the Terp rules
105
+ (the reference stack pins ruff's bandit-derived `S` rules, wired into the
106
+ platform repo, the client template, and each generated project's own gate —
107
+ platform ADR 0085). Classes no stock analyzer detects well — path traversal,
108
+ secrets in logs, browser-storage auth material — are addressed
109
+ **constructively** by the reference framework (streamed storage behind
110
+ declared references, central log redaction, the session/refresh model) and
111
+ earn a detective catalog rule only through the same admission bar, never as a
112
+ checkbox. Baseline findings are **not** Terp findings: the finding
113
+ format's `rule` pattern admits only catalog ids, so a generic finding can
114
+ never masquerade as (or dilute) a Terp verdict — it travels as the baseline
115
+ tool's own output (or the envelope's `unattributed` bucket, ADR 0083).
116
+
117
+ Catalog entries deliberately carry **no CWE/OWASP mappings**: no governance
118
+ consumer exists for them, and the delegated baseline's own documentation
119
+ already maps the generic classes. Metadata joins the catalog when something
120
+ machine-consumes it, not before.
121
+
122
+ ## Catalog format
123
+
124
+ `catalog/<surface>/<rule>.json`, validated against `catalog/schema.json` (the
125
+ schema is normative and travels with the spec). The normative statement of a
126
+ rule is its `title` + `intent`, and that prose is **stack-neutral by
127
+ construction**: plain prose, no docstring markup, no reference-implementation
128
+ symbols, package paths, marker spellings, or repo-internal pointers (sibling
129
+ rules are cited by catalog rule name). The reference realisation lives in the
130
+ non-normative fields (`enforcement`, `reference`, `opt_out`, `guide_topic`).
131
+ The standalone suite enforces the split
132
+ (`test_normative_prose_is_stack_neutral`).
133
+
134
+ | Field | Meaning |
135
+ |---|---|
136
+ | `id` | `<surface>/<rule>` — `backend/<snake_case>` (a `terp.arch` rule) or `frontend/<kebab-case>` (a boundary rule). **Findings are attributed to this id**, never to a tool-internal rule id. |
137
+ | `surface` | `backend` or `frontend`. |
138
+ | `title` | One-line statement of the invariant. |
139
+ | `intent` | Why the rule exists — the drift or threat it prevents. |
140
+ | `layer` | Cheapest faithful verification for a *new* stack: see below. |
141
+ | `enforcement` | How the reference implementation enforces it (first entry: the `build-time` check). A `runtime` entry names the fail-closed runtime control pairing with it (the two-layer discipline a Level 3 stack must reproduce for the rules that require it); a `black-box` entry names the `@terp/conformance` probe title. For frontend rules, `reported_as` is the ESLint rule id violations surface as — several catalog rules share one core rule id, so the adapter publishes a `catalogRuleId()` mapping and findings are attributed through it. |
142
+ | `runtime` | **Mandatory.** The rule's runtime-applicability classification (`required` / `not-applicable` / `deferred`) plus a `rationale` (mandatory for exemptions) and, for `deferred`, a `tracking` reference naming where the deferral is tracked — see “Runtime applicability” below. |
143
+ | `restricted_surface` | Frontend prohibition rules only: the `restricted-surface.json` keys the rule realises — the structural citation the spec suite resolves (every key must be claimed by some rule; a prose mention in `intent` must agree with the field). |
144
+ | `opt_out` | The *reference realisation* of the abstract escape-hatch contract (see below). |
145
+ | `reference` | Optional 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. |
146
+ | `guide_topic` | Reference-implementation metadata (backend only): the `terp guide` topic teaching the compliant pattern. Not normative for other stacks. |
147
+ | `corpus` | Whether `corpus/<id>/` cases exist yet (the parity test holds this flag to the directory truth; a covered rule needs at least one `violation-*` **and** one `compliant-*` case). |
148
+
149
+ ### Enforcement layers
150
+
151
+ - **`black-box`** — the invariant is observable by probing a *running* app
152
+ (no source access needed). Portable to any stack via a conformance suite;
153
+ each such rule names its probe in a `black-box` enforcement entry
154
+ (`packages/frontend/conformance/tests/standard.spec.ts`).
155
+ - **`static-portable`** — expressible as source patterns a generic engine
156
+ (Semgrep/ast-grep-class) can realise per language from this spec.
157
+ - **`static-bespoke`** — needs deep framework knowledge (traits, `ModuleSpec`,
158
+ archetype slots); built per officially certified stack only.
159
+
160
+ The classification is a judgment about *porting cost*, not a limit on the
161
+ reference implementation — today every rule is enforced by `terp.arch` or
162
+ `@terp/eslint-boundaries` regardless of layer.
163
+
164
+ ### Runtime applicability (the two-layer discipline, per rule)
165
+
166
+ Whether a rule *additionally* pairs with a fail-closed runtime control is not a
167
+ blanket claim — it is recorded per rule in the mandatory `runtime` block and
168
+ held coherent by the spec suite:
169
+
170
+ - **`required`** — the invariant is observable in the running system and the
171
+ reference implementation owns a fail-closed control for it; the entry **must**
172
+ declare that control as a `kind: "runtime"` enforcement entry (whose `ref`
173
+ must resolve to a real symbol — the framework's parity test fails otherwise).
174
+ - **`not-applicable`** — the invariant is a property of the authored artifact
175
+ (source form, imports, justification markers, checked-in files) that is
176
+ erased or already materialised by the time the app runs, so no runtime seam
177
+ can enforce it. The mandatory `rationale` states why; the build-time check is
178
+ the control, by recorded decision.
179
+ - **`deferred`** — a runtime control would add independent fidelity on a seam
180
+ the reference framework owns, but has not shipped yet: an explicit, reviewed
181
+ gap, never a silent one. The mandatory `rationale` names the seam, and the
182
+ mandatory `tracking` reference names where the deferral is tracked (an issue
183
+ URL or the reference implementation's tracker document plus the seam) — so a
184
+ deferral has a lifecycle instead of being open-ended.
185
+
186
+ Fail-closed consistency (spec suite): `required` iff a `runtime` enforcement
187
+ entry exists; an exemption always carries a non-empty rationale; a deferral
188
+ always carries a tracking reference.
189
+
190
+ ### The escape-hatch contract
191
+
192
+ Every rule has **at most one** governed opt-out, and its *semantics* are the
193
+ normative part: a **justified inline marker** on (or immediately above) the
194
+ violating line that names the **catalog rule name** (the `<rule>` half of the
195
+ id — never a tool-internal rule id, mirroring findings attribution) and states
196
+ a reason; an unjustified marker is itself a violation; marker counts must
197
+ exactly match a checked-in per-app budget that can only shrink (the ratchet).
198
+ The `opt_out` field records the reference realisation's concrete spelling
199
+ (`# arch-allow-<rule>: <reason>` in Python, `// terp-allow-<rule>: <reason>`
200
+ in TypeScript) — the spelling is derived from the rule id, and the spec suite
201
+ holds the derivation. The escape-hatch **governance rules themselves** (the
202
+ budget ratchet, the ungoverned-marker condition) carry no `opt_out`:
203
+ governance cannot be waived by the mechanism it governs. Another stack
204
+ implements the same contract with its own comment syntax.
205
+
206
+ The `<reason>` is free text, and MAY carry structured metadata tokens so a
207
+ long-lived exception stays visible and auditable rather than eternal:
208
+ `owner:<who>` (who answers for the exception), `ticket:<ref>` (where its
209
+ removal is tracked), and `review-by:<YYYY-MM-DD>` (when it must be re-justified).
210
+ The tokens are a convention, not a gate — the marker's semantics are unchanged,
211
+ and a checker MUST NOT reject a reason without them — but a toolchain SHOULD
212
+ surface expired `review-by:` dates in its reporting.
213
+
214
+ ## Finding format
215
+
216
+ A conformant checker emits an array of findings per checked tree, shaped by
217
+ `findings.schema.json`: each finding names the **catalog rule id** it realises
218
+ (`rule`), the file `path` relative to the checked tree's root, and optionally a
219
+ `line`, a directive `message`, a `fix_hint` (the compliant construct, for agent
220
+ consumers acting on findings without re-reading the catalog), and a
221
+ `fingerprint` (a stable, checker-chosen per-instance identifier so a finding
222
+ can be tracked across line-shifting edits). Attribution is always to the
223
+ stack-neutral catalog id — the reference ESLint adapter, whose core rule ids
224
+ are shared between several catalog rules, publishes this mapping as
225
+ `catalogRuleId()` in `@terp/eslint-boundaries`.
226
+
227
+ ## Check-report format
228
+
229
+ Findings alone cannot support a per-rule verdict: a rule with zero findings is
230
+ only *passing* if the run actually evaluated it, and the consumer must never
231
+ supply that knowledge itself — its own catalog copy can be newer or older than
232
+ the checked app's pinned toolchain (version skew), and some enforcement is
233
+ conditional (an escape-hatch budget only when one is checked in, a layout
234
+ contract only when the app opts in). So a conformant checker reports a whole
235
+ run as one **application check report** (`app-check-report.schema.json`): a
236
+ format marker (`terp_check_report: 1`), the **spec version** its rule ids
237
+ resolve against, the **checker identity** (the same identity its certification
238
+ scorecard carries), the run **verdict** (`ok`, or an explicit `error` for a
239
+ run that failed to complete — an erroring run never claims ok and never claims
240
+ rules it did not finish evaluating), the **evaluated-rule inventory**
241
+ (`rules`), the opt-in rules published as **`not_applicable`** (their own
242
+ state — never passing, never unknown), the **findings** (exactly the finding
243
+ format's item shape — the spec suite holds the two identical), and
244
+ **`unattributed`** messages (diagnostics outside the standard, surfaced rather
245
+ than dropped). A consumer joins per-rule verdicts to the catalog exclusively
246
+ through the report's inventory, fail closed: pass = evaluated with no
247
+ attributed finding; a rule the run did not publish renders unknown, never
248
+ green. A multi-surface toolchain emits one report per checker run and a
249
+ consumer merges reports through their inventories.
250
+
251
+ ## Scorecard format
252
+
253
+ A checker claiming certification emits a scorecard (`scorecard.schema.json`):
254
+ the spec version it certified against, the checker's identity, and one entry
255
+ per claimed rule with its pass/fail verdict over the corpus and the residuals
256
+ it relies on (which must be a subset of `corpus/RESIDUALS.json` for the rule —
257
+ claiming an unrecorded residual is a conformance failure). A consumer can
258
+ re-run the corpus and reproduce the scorecard, making the certification claim
259
+ verifiable.
260
+
261
+ ## Assurance profile
262
+
263
+ Conformance to the rule catalog proves the *Terp-specific* half of release
264
+ readiness. The **assurance profile** (`assurance-profile.schema.json`) is the
265
+ machine-readable composition of that with the generic evidence lanes a release
266
+ also stands on — one document a toolchain emits from its release verification
267
+ profile, so "this build is releasable" becomes a checkable artifact instead of
268
+ a habit.
269
+
270
+ The lane vocabulary and each lane's requirement level are **normative** and
271
+ fixed here — an emitter cannot demote a required lane, which is why the
272
+ requirement level is deliberately not a field of the document:
273
+
274
+ | Lane | Requirement | Evidence |
275
+ |---|---|---|
276
+ | `terp-standard` | **required** | The standard's own enforcement surfaces ran and passed: the architecture gate and the frontend boundary lint, publishing their evaluated-rule inventories (check reports). |
277
+ | `appsec-baseline` | **required** | The delegated generic AppSec baseline passed (the reference realisation: `ruff` with the flake8-bandit `S` rules). |
278
+ | `dependency-audit` | **required** | Dependency trees were audited against known-vulnerability databases (the reference realisation: `pip-audit` and `npm audit`). |
279
+ | `a11y` | recommended | Automated accessibility checks over the running app (e.g. axe). |
280
+ | `blackbox-conformance` | recommended | The black-box behavioural conformance suite over the running workbench. |
281
+
282
+ The claim (`ok`) is true exactly when every **required** lane passed;
283
+ recommended lanes inform the reader but never carry the claim. Every lane of
284
+ the vocabulary appears exactly once — a lane the toolchain does not realise is
285
+ reported `not-run` (with no composing checks), never dropped and never counted
286
+ as passed. Each realised lane names the verification-check ids whose verdicts
287
+ compose it, so a consumer can trace the claim into the toolchain's own
288
+ verification envelope.
289
+
290
+ ## Corpus format
291
+
292
+ ```text
293
+ corpus/backend/<rule>/violation-01/ # files rooted at the app package root
294
+ corpus/backend/<rule>/compliant-01/ # e.g. modules/notes/service.py
295
+ corpus/frontend/<rule>/violation-01/ # files rooted at the frontend app root
296
+ corpus/frontend/<rule>/compliant-01/ # e.g. src/modules/widgets/Widget.tsx
297
+ ```
298
+
299
+ A case may ship an extra checker input at its root when the rule takes one:
300
+ the `backend/escape_hatch_budget` cases carry the `escape-hatch-budget.json`
301
+ the ratchet is verified against, and a frontend layout-contract case activates
302
+ via a `layout-contract.json` at its root.
303
+
304
+ A violation case MAY additionally ship an **`expected-findings.json`** at its
305
+ root: the exact findings (rule + path + line, shaped by
306
+ `findings.schema.json`) a maximally-precise checker emits for the rule under
307
+ test. A harness may assert the manifest exactly — hardening the per-case
308
+ contract from "flags something" to "flags the right line" — while the loose
309
+ contract below remains the floor for cases without one (and the ceiling a
310
+ checker must reach to conform). The spec suite holds every checked-in manifest
311
+ to the finding shape and to the case it sits in.
312
+
313
+ The contract for a checker claiming conformance for a rule is stated **per
314
+ rule**, over the finding format above:
315
+
316
+ - every `violation-*` case produces at least one finding attributed to that
317
+ rule's catalog id, and
318
+ - every `compliant-*` case produces **no** findings for that rule.
319
+
320
+ A compliant case is minimal, not a full application — it may legitimately be
321
+ incomplete with respect to *other* rules, so a multi-rule checker scopes the
322
+ corpus run to the rule under test. (The reference frontend harness happens to
323
+ hold its compliant cases fully clean across all boundary rules — a stricter
324
+ bar than the contract requires.)
325
+
326
+ ### Detector boundaries (what the corpus deliberately does not require)
327
+
328
+ The executable corpus **is** the interoperability contract: a checker is
329
+ conformant for a rule when it flags every `violation-*` case and stays silent
330
+ on every `compliant-*` case — nothing more. The violation cases include the
331
+ evasion shapes the rules are expected to see through (qualified/attribute
332
+ calls, multiline constructs, values routed through a local variable or
333
+ `.format()`/`%` building, aliased and parenthesized imports, computed
334
+ `window["fetch"]`-style member access for the egress family), and the
335
+ compliant cases pin the near-misses that must **not** fire (adjacent-literal
336
+ SQL that merely looks concatenated, credential-shaped names with dynamic
337
+ values, `*_mock`/`socketserver`-style name cousins, member calls like
338
+ `repo.fetch(...)` / `interpreter.eval(...)` on local objects, forbidden syntax
339
+ quoted inside comments and strings).
340
+
341
+ Some statically-erased or renamed forms are **deliberately outside** the
342
+ contract — known limits of precise, low-false-positive detection, kept out of
343
+ the corpus so a second implementation neither over-fits nor under-claims.
344
+ These residuals are recorded per rule in **`corpus/RESIDUALS.json`** — a
345
+ machine-readable ratchet governed like `corpus/PENDING.json`: the list only
346
+ shrinks, and closing a residual means seeding the corpus case that contracts
347
+ it and deleting the entry, never silently. Today it records:
348
+
349
+ - an alias-renamed symbol import (`from sqlalchemy import text as sql_text`)
350
+ is not required to be resolved to `text`;
351
+ - dynamic import (`importlib.import_module("httpx")`) is not required to be
352
+ seen as an import;
353
+ - a computed sink or global (`el["innerHTML"] = …`, `window["eval"]`) is not
354
+ required to be recognised for the *eval/DOM-sink* rules (the egress family
355
+ **does** contract the computed forms — its cases include them);
356
+ - `Function("…")` called without `new`, and egress through receivers other
357
+ than `window`/`globalThis` (`self.fetch`), are not required.
358
+
359
+ These residuals are governed the usual way: the escape-hatch contract makes
360
+ sanctioned exceptions visible, and the paired runtime controls (where
361
+ `runtime.applicability` is `required`) hold the invariant regardless of
362
+ spelling. Widening a detector past the contract is always allowed — the
363
+ corpus states the floor, not the ceiling.
364
+
365
+ The reference implementations are themselves held to this contract in CI:
366
+ `test_spec_corpus.py` runs each catalogued `terp.arch` rule over the backend
367
+ corpus, and `corpus.test.js` runs the ESLint adapter over the frontend corpus,
368
+ attributing findings via `catalogRuleId()`. Corpus sample files are
369
+ intentionally violating code — they are excluded from repo-wide lint
370
+ (`ruff` `extend-exclude`) and are never imported or executed.
371
+
372
+ Coverage is governed by **`corpus/PENDING.json`**: the exact list of rules
373
+ still without cases. Seeding a rule's corpus removes it from the list; a new
374
+ rule shipped without corpus must be listed there explicitly — so uncovered
375
+ rules stay visible and the list only shrinks. Every `static-portable` backend
376
+ rule must have cases (enforced by the parity test); today every backend and
377
+ frontend rule has cases and the pending list is empty — it exists so a *new*
378
+ rule can ship with its gap explicit and reviewed.
379
+
380
+ ## Conformance levels
381
+
382
+ - **Level 1 — black-box:** the app passes the runnable conformance suite
383
+ (`@terp/conformance`) for the capabilities it claims, including the
384
+ `standard:` probes the `black-box` catalog entries name.
385
+ - **Level 2 — static rule pack:** additionally, a checker validated against
386
+ this corpus enforces the `static-portable` rules for the app's language(s),
387
+ emitting findings per `findings.schema.json`.
388
+ - **Level 3 — full harness:** additionally, the `static-bespoke` rules, the
389
+ paired `runtime` controls of every rule whose `runtime.applicability` is
390
+ `required`, and the governed escape-hatch budget ratchet are enforced (today:
391
+ the `terp.arch` + `@terp/eslint-boundaries` reference harness on top of
392
+ `terp.core` / `@terp/react-core`).
393
+
394
+ ## Growing the spec
395
+
396
+ - **New rule** → ship the rule and its catalog entry together (the parity test
397
+ fails otherwise); classify its runtime applicability deliberately (`required`
398
+ with the declared control, or an exemption with its rationale — a deferral
399
+ also names its `tracking` reference); seed corpus cases when the rule is
400
+ portable, or list it in `corpus/PENDING.json` explicitly; regenerate the
401
+ rule pages (`python tools/generate_rule_docs.py`).
402
+ - **New corpus cases** → add `violation-*`/`compliant-*` directories, flip the
403
+ entry's `corpus` flag to `true`, and drop the rule from `corpus/PENDING.json`;
404
+ the harness picks the cases up by convention. Ship an `expected-findings.json`
405
+ when the violating lines are pinned. Closing a documented detector residual
406
+ also deletes its `corpus/RESIDUALS.json` entry.
407
+ - **New stack** → implement the `static-portable` rules however you like; the
408
+ corpus is the acceptance test, findings must attribute to catalog ids, and
409
+ the certification summary is a scorecard (`scorecard.schema.json`).
410
+ - **Format change** → bump `VERSION` and add the matching `CHANGELOG.md` entry
411
+ (the suite holds the top entry to the version). Pre-1.0, a **changed
412
+ contract** — a new mandatory catalog field (0.5.0's `runtime` block), a
413
+ changed finding shape —
414
+ bumps the **minor** (the strongest signal 0.x semver carries; a 0.x major
415
+ would claim a stability this spec does not yet promise); purely additive
416
+ fields and new rules also bump the minor; prose bumps the patch. From 1.0.0
417
+ a changed contract bumps the **major**.
package/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.14.0