@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.
- package/LICENSE +201 -201
- package/README.md +417 -417
- package/VERSION +1 -1
- package/app-check-report.schema.json +122 -122
- package/assurance-profile.schema.json +70 -70
- package/catalog/backend/alembic_downgrades_not_empty.json +22 -22
- package/catalog/backend/base_query_not_overridden.json +27 -27
- package/catalog/backend/canonical_module_shape.json +22 -22
- package/catalog/backend/datetime_columns_are_timezone_aware.json +23 -23
- package/catalog/backend/escape_hatch_budget.json +21 -21
- package/catalog/backend/events_reference_catalog.json +27 -27
- package/catalog/backend/input_schemas_exclude_managed_columns.json +27 -27
- package/catalog/backend/input_str_fields_have_max_length.json +22 -22
- package/catalog/backend/jobs_reference_catalog.json +27 -27
- package/catalog/backend/list_routes_paginate.json +32 -32
- package/catalog/backend/modules_declare_policy.json +26 -26
- package/catalog/backend/mutations_emit_audit.json +26 -26
- package/catalog/backend/mutations_require_write_role.json +32 -32
- package/catalog/backend/no_adhoc_background_runtime.json +22 -22
- package/catalog/backend/no_adhoc_config_decrypt.json +27 -27
- package/catalog/backend/no_adhoc_logging_config.json +22 -22
- package/catalog/backend/no_adhoc_middleware.json +27 -27
- package/catalog/backend/no_adhoc_permission_literals.json +27 -27
- package/catalog/backend/no_app_instantiation.json +22 -22
- package/catalog/backend/no_blocking_sleep.json +22 -22
- package/catalog/backend/no_cross_module_imports.json +21 -21
- package/catalog/backend/no_dependency_overrides.json +27 -27
- package/catalog/backend/no_destructive_migrations.json +22 -22
- package/catalog/backend/no_dynamic_sql.json +22 -22
- package/catalog/backend/no_empty_tests.json +22 -22
- package/catalog/backend/no_eval_or_exec.json +22 -22
- package/catalog/backend/no_hardcoded_credentials.json +21 -21
- package/catalog/backend/no_internal_imports.json +22 -22
- package/catalog/backend/no_manual_actor_stamping.json +27 -27
- package/catalog/backend/no_manual_ownership_checks.json +32 -32
- package/catalog/backend/no_manual_scope_filtering.json +27 -27
- package/catalog/backend/no_manual_table_schema.json +22 -22
- package/catalog/backend/no_manual_version_assignment.json +22 -22
- package/catalog/backend/no_mutable_default_args.json +22 -22
- package/catalog/backend/no_naive_datetime.json +22 -22
- package/catalog/backend/no_oversized_python_files.json +22 -22
- package/catalog/backend/no_print.json +22 -22
- package/catalog/backend/no_raw_app_routes.json +26 -26
- package/catalog/backend/no_raw_connection_access.json +27 -27
- package/catalog/backend/no_raw_file_references.json +27 -27
- package/catalog/backend/no_raw_outbound_http.json +22 -22
- package/catalog/backend/no_raw_session_construction.json +22 -22
- package/catalog/backend/no_star_imports.json +22 -22
- package/catalog/backend/no_todo_fixme.json +22 -22
- package/catalog/backend/no_unique_columns_on_soft_delete_models.json +22 -22
- package/catalog/backend/offset_queries_declare_ordering.json +22 -22
- package/catalog/backend/path_id_params_are_uuid.json +22 -22
- package/catalog/backend/policy_refs_resolve.json +27 -27
- package/catalog/backend/public_modules_are_read_only.json +26 -26
- package/catalog/backend/reads_use_base_query.json +27 -27
- package/catalog/backend/response_model_not_table_model.json +27 -27
- package/catalog/backend/routes_declare_response_model.json +27 -27
- package/catalog/backend/safe_methods_are_read_only.json +31 -31
- package/catalog/backend/schemas_exclude_sensitive_fields.json +32 -32
- package/catalog/backend/session_imported_from_sqlmodel.json +22 -22
- package/catalog/backend/table_models_use_base_table.json +22 -22
- package/catalog/backend/tables_have_migrations.json +27 -27
- package/catalog/backend/tenant_scoped_models_use_scoped_service.json +22 -22
- package/catalog/backend/ungoverned_escape_hatch.json +21 -21
- package/catalog/backend/update_schemas_inherit_base_update_schema.json +32 -32
- package/catalog/frontend/escape-hatch.json +21 -21
- package/catalog/frontend/generated-client-only.json +26 -26
- package/catalog/frontend/layout-contract.json +26 -26
- package/catalog/frontend/no-cross-module-imports.json +21 -21
- package/catalog/frontend/no-deep-imports.json +25 -25
- package/catalog/frontend/no-dom-html-injection.json +22 -22
- package/catalog/frontend/no-eval.json +21 -21
- package/catalog/frontend/no-inline-styling.json +25 -25
- package/catalog/frontend/no-style-imports.json +24 -24
- package/catalog/frontend/no-unsafe-href.json +21 -21
- package/catalog/frontend/no-unsafe-target-blank.json +21 -21
- package/catalog/frontend/router-links.json +22 -22
- package/catalog/frontend/token-styled-elements.json +25 -25
- package/catalog/schema.json +95 -95
- package/corpus/PENDING.json +4 -4
- package/corpus/RESIDUALS.json +21 -21
- package/corpus/backend/alembic_downgrades_not_empty/compliant-01/modules/notes/migrations/versions/0001_change.py +6 -6
- package/corpus/backend/alembic_downgrades_not_empty/violation-01/modules/notes/migrations/versions/0001_change.py +6 -6
- package/corpus/backend/base_query_not_overridden/compliant-01/modules/notes/service.py +8 -8
- package/corpus/backend/base_query_not_overridden/compliant-02/modules/notes/service.py +8 -8
- package/corpus/backend/base_query_not_overridden/violation-01/modules/notes/service.py +8 -8
- package/corpus/backend/base_query_not_overridden/violation-02/expected-findings.json +7 -7
- package/corpus/backend/base_query_not_overridden/violation-02/modules/notes/service.py +8 -8
- package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/models.py +6 -6
- package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/module.py +7 -7
- package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/router.py +3 -3
- package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/schemas.py +10 -10
- package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/canonical_module_shape/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/datetime_columns_are_timezone_aware/compliant-01/modules/notes/models.py +12 -12
- package/corpus/backend/datetime_columns_are_timezone_aware/violation-01/modules/notes/models.py +12 -12
- package/corpus/backend/datetime_columns_are_timezone_aware/violation-02/modules/notes/models.py +13 -13
- package/corpus/backend/datetime_columns_are_timezone_aware/violation-03/modules/notes/models.py +15 -15
- package/corpus/backend/escape_hatch_budget/compliant-01/escape-hatch-budget.json +3 -3
- package/corpus/backend/escape_hatch_budget/compliant-01/modules/notes/service.py +2 -2
- package/corpus/backend/escape_hatch_budget/compliant-02/escape-hatch-budget.json +1 -1
- package/corpus/backend/escape_hatch_budget/compliant-02/modules/notes/service.py +4 -4
- package/corpus/backend/escape_hatch_budget/violation-01/escape-hatch-budget.json +1 -1
- package/corpus/backend/escape_hatch_budget/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/escape_hatch_budget/violation-02/escape-hatch-budget.json +3 -3
- package/corpus/backend/escape_hatch_budget/violation-02/modules/notes/service.py +4 -4
- package/corpus/backend/events_reference_catalog/compliant-01/modules/notes/module.py +9 -9
- package/corpus/backend/events_reference_catalog/violation-01/modules/notes/module.py +8 -8
- package/corpus/backend/input_schemas_exclude_managed_columns/compliant-01/modules/notes/schemas.py +6 -6
- package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/expected-findings.json +7 -7
- package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/modules/notes/schemas.py +9 -9
- package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/expected-findings.json +12 -12
- package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/modules/notes/router.py +14 -14
- package/corpus/backend/input_str_fields_have_max_length/compliant-01/modules/notes/schemas.py +4 -4
- package/corpus/backend/input_str_fields_have_max_length/violation-01/modules/notes/schemas.py +2 -2
- package/corpus/backend/jobs_reference_catalog/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/jobs_reference_catalog/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/list_routes_paginate/compliant-01/modules/notes/router.py +6 -6
- package/corpus/backend/list_routes_paginate/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/modules_declare_policy/compliant-01/modules/notes/module.py +7 -7
- package/corpus/backend/modules_declare_policy/violation-01/modules/notes/module.py +6 -6
- package/corpus/backend/mutations_emit_audit/compliant-01/modules/notes/service.py +9 -9
- package/corpus/backend/mutations_emit_audit/violation-01/modules/notes/service.py +4 -4
- package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/module.py +7 -7
- package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/router.py +3 -3
- package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/module.py +7 -7
- package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/router.py +3 -3
- package/corpus/backend/mutations_require_write_role/violation-01/expected-findings.json +7 -7
- package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/module.py +7 -7
- package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/mutations_require_write_role/violation-02/expected-findings.json +7 -7
- package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/module.py +7 -7
- package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/router.py +5 -5
- package/corpus/backend/mutations_require_write_role/violation-03/expected-findings.json +7 -7
- package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/module.py +10 -10
- package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/router.py +3 -3
- package/corpus/backend/no_adhoc_background_runtime/compliant-01/modules/notes/service.py +9 -9
- package/corpus/backend/no_adhoc_background_runtime/violation-01/modules/notes/service.py +5 -5
- package/corpus/backend/no_adhoc_background_runtime/violation-02/modules/notes/service.py +3 -3
- package/corpus/backend/no_adhoc_config_decrypt/compliant-01/modules/billing/service.py +2 -2
- package/corpus/backend/no_adhoc_config_decrypt/violation-01/modules/billing/service.py +2 -2
- package/corpus/backend/no_adhoc_logging_config/compliant-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_adhoc_logging_config/violation-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_adhoc_middleware/compliant-01/main.py +3 -3
- package/corpus/backend/no_adhoc_middleware/violation-01/main.py +4 -4
- package/corpus/backend/no_adhoc_middleware/violation-02/main.py +5 -5
- package/corpus/backend/no_adhoc_permission_literals/compliant-01/modules/notes/module.py +3 -3
- package/corpus/backend/no_adhoc_permission_literals/compliant-02/modules/notes/router.py +5 -5
- package/corpus/backend/no_adhoc_permission_literals/violation-01/expected-findings.json +7 -7
- package/corpus/backend/no_adhoc_permission_literals/violation-01/modules/notes/module.py +1 -1
- package/corpus/backend/no_adhoc_permission_literals/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_adhoc_permission_literals/violation-02/modules/notes/router.py +2 -2
- package/corpus/backend/no_adhoc_permission_literals/violation-03/expected-findings.json +12 -12
- package/corpus/backend/no_adhoc_permission_literals/violation-03/modules/notes/module.py +10 -10
- package/corpus/backend/no_app_instantiation/compliant-01/main.py +3 -3
- package/corpus/backend/no_app_instantiation/violation-01/main.py +3 -3
- package/corpus/backend/no_blocking_sleep/compliant-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_blocking_sleep/violation-01/modules/notes/service.py +5 -5
- package/corpus/backend/no_cross_module_imports/compliant-01/modules/a/service.py +1 -1
- package/corpus/backend/no_cross_module_imports/violation-01/modules/a/service.py +1 -1
- package/corpus/backend/no_cross_module_imports/violation-02/modules/a/service.py +1 -1
- package/corpus/backend/no_dependency_overrides/compliant-01/main.py +12 -12
- package/corpus/backend/no_dependency_overrides/violation-01/main.py +11 -11
- package/corpus/backend/no_destructive_migrations/compliant-01/modules/notes/migrations/versions/0001_change.py +3 -3
- package/corpus/backend/no_destructive_migrations/compliant-02/modules/notes/migrations/versions/0001_change.py +8 -8
- package/corpus/backend/no_destructive_migrations/violation-01/expected-findings.json +7 -7
- package/corpus/backend/no_destructive_migrations/violation-01/modules/notes/migrations/versions/0001_change.py +2 -2
- package/corpus/backend/no_destructive_migrations/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_destructive_migrations/violation-02/modules/notes/migrations/versions/0001_change.py +2 -2
- package/corpus/backend/no_destructive_migrations/violation-03/expected-findings.json +7 -7
- package/corpus/backend/no_destructive_migrations/violation-03/modules/notes/migrations/versions/0001_change.py +3 -3
- package/corpus/backend/no_destructive_migrations/violation-04/expected-findings.json +12 -12
- package/corpus/backend/no_destructive_migrations/violation-04/modules/notes/migrations/versions/0001_change.py +6 -6
- package/corpus/backend/no_destructive_migrations/violation-05/expected-findings.json +12 -12
- package/corpus/backend/no_destructive_migrations/violation-05/modules/notes/migrations/versions/0001_change.py +3 -3
- package/corpus/backend/no_dynamic_sql/compliant-01/modules/notes/service.py +1 -1
- package/corpus/backend/no_dynamic_sql/compliant-02/modules/notes/service.py +20 -20
- package/corpus/backend/no_dynamic_sql/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_dynamic_sql/violation-02/modules/notes/service.py +2 -2
- package/corpus/backend/no_dynamic_sql/violation-03/modules/notes/service.py +7 -7
- package/corpus/backend/no_dynamic_sql/violation-04/modules/notes/service.py +16 -16
- package/corpus/backend/no_empty_tests/compliant-01/tests/test_notes.py +2 -2
- package/corpus/backend/no_empty_tests/violation-01/tests/test_notes.py +2 -2
- package/corpus/backend/no_eval_or_exec/compliant-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_eval_or_exec/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_hardcoded_credentials/compliant-01/modules/billing/service.py +3 -3
- package/corpus/backend/no_hardcoded_credentials/compliant-02/modules/billing/service.py +18 -18
- package/corpus/backend/no_hardcoded_credentials/violation-01/expected-findings.json +7 -7
- package/corpus/backend/no_hardcoded_credentials/violation-01/modules/billing/service.py +2 -2
- package/corpus/backend/no_hardcoded_credentials/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_hardcoded_credentials/violation-02/modules/billing/service.py +1 -1
- package/corpus/backend/no_hardcoded_credentials/violation-03/expected-findings.json +22 -22
- package/corpus/backend/no_hardcoded_credentials/violation-03/modules/billing/service.py +11 -11
- package/corpus/backend/no_hardcoded_credentials/violation-04/expected-findings.json +12 -12
- package/corpus/backend/no_hardcoded_credentials/violation-04/modules/billing/service.py +8 -8
- package/corpus/backend/no_hardcoded_credentials/violation-05/expected-findings.json +12 -12
- package/corpus/backend/no_hardcoded_credentials/violation-05/modules/billing/service.py +4 -4
- package/corpus/backend/no_internal_imports/compliant-01/modules/notes/service.py +1 -1
- package/corpus/backend/no_internal_imports/violation-01/modules/notes/service.py +1 -1
- package/corpus/backend/no_manual_actor_stamping/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/no_manual_actor_stamping/compliant-02/modules/notes/schemas.py +12 -12
- package/corpus/backend/no_manual_actor_stamping/violation-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_manual_actor_stamping/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_manual_actor_stamping/violation-02/modules/notes/service.py +4 -4
- package/corpus/backend/no_manual_ownership_checks/compliant-01/modules/journals/service.py +3 -3
- package/corpus/backend/no_manual_ownership_checks/compliant-02/modules/journals/schemas.py +11 -11
- package/corpus/backend/no_manual_ownership_checks/violation-01/modules/journals/service.py +3 -3
- package/corpus/backend/no_manual_ownership_checks/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_manual_ownership_checks/violation-02/modules/journals/service.py +9 -9
- package/corpus/backend/no_manual_ownership_checks/violation-03/expected-findings.json +7 -7
- package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/jobs.py +1 -1
- package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/models.py +7 -7
- package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/module.py +11 -11
- package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/service.py +7 -7
- package/corpus/backend/no_manual_scope_filtering/compliant-01/modules/notes/service.py +8 -8
- package/corpus/backend/no_manual_scope_filtering/compliant-02/modules/notes/schemas.py +12 -12
- package/corpus/backend/no_manual_scope_filtering/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_manual_scope_filtering/violation-02/modules/notes/service.py +2 -2
- package/corpus/backend/no_manual_scope_filtering/violation-03/expected-findings.json +7 -7
- package/corpus/backend/no_manual_scope_filtering/violation-03/modules/notes/service.py +7 -7
- package/corpus/backend/no_manual_table_schema/compliant-01/modules/notes/models.py +6 -6
- package/corpus/backend/no_manual_table_schema/violation-01/modules/notes/models.py +8 -8
- package/corpus/backend/no_manual_version_assignment/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/no_manual_version_assignment/violation-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_manual_version_assignment/violation-02/modules/notes/service.py +3 -3
- package/corpus/backend/no_manual_version_assignment/violation-03/modules/notes/service.py +3 -3
- package/corpus/backend/no_mutable_default_args/compliant-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_mutable_default_args/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_naive_datetime/compliant-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_naive_datetime/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_naive_datetime/violation-02/modules/notes/service.py +2 -2
- package/corpus/backend/no_oversized_python_files/compliant-01/modules/notes/service.py +9 -9
- package/corpus/backend/no_oversized_python_files/violation-01/modules/notes/service.py +530 -530
- package/corpus/backend/no_print/compliant-01/modules/notes/service.py +7 -7
- package/corpus/backend/no_print/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_raw_app_routes/compliant-01/main.py +12 -12
- package/corpus/backend/no_raw_app_routes/violation-01/main.py +10 -10
- package/corpus/backend/no_raw_app_routes/violation-02/main.py +16 -16
- package/corpus/backend/no_raw_app_routes/violation-03/main.py +12 -12
- package/corpus/backend/no_raw_app_routes/violation-04/main.py +10 -10
- package/corpus/backend/no_raw_app_routes/violation-05/main.py +12 -12
- package/corpus/backend/no_raw_app_routes/violation-06/main.py +10 -10
- package/corpus/backend/no_raw_connection_access/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/no_raw_connection_access/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_raw_connection_access/violation-02/modules/notes/service.py +2 -2
- package/corpus/backend/no_raw_file_references/compliant-01/modules/notes/models.py +10 -10
- package/corpus/backend/no_raw_file_references/violation-01/modules/notes/models.py +9 -9
- package/corpus/backend/no_raw_outbound_http/compliant-01/modules/notes/service.py +2 -2
- package/corpus/backend/no_raw_outbound_http/compliant-03/modules/notes/service.py +12 -12
- package/corpus/backend/no_raw_outbound_http/violation-01/expected-findings.json +7 -7
- package/corpus/backend/no_raw_outbound_http/violation-01/modules/notes/service.py +1 -1
- package/corpus/backend/no_raw_outbound_http/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_raw_outbound_http/violation-02/modules/notes/service.py +1 -1
- package/corpus/backend/no_raw_outbound_http/violation-03/expected-findings.json +7 -7
- package/corpus/backend/no_raw_outbound_http/violation-03/modules/notes/service.py +1 -1
- package/corpus/backend/no_raw_outbound_http/violation-04/expected-findings.json +12 -12
- package/corpus/backend/no_raw_outbound_http/violation-04/modules/notes/service.py +7 -7
- package/corpus/backend/no_raw_outbound_http/violation-05/expected-findings.json +12 -12
- package/corpus/backend/no_raw_outbound_http/violation-05/modules/notes/service.py +5 -5
- package/corpus/backend/no_raw_outbound_http/violation-06/expected-findings.json +12 -12
- package/corpus/backend/no_raw_outbound_http/violation-06/modules/notes/service.py +2 -2
- package/corpus/backend/no_raw_session_construction/compliant-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_raw_session_construction/violation-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_star_imports/compliant-01/modules/notes/service.py +1 -1
- package/corpus/backend/no_star_imports/violation-01/modules/notes/service.py +1 -1
- package/corpus/backend/no_todo_fixme/compliant-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_todo_fixme/violation-01/modules/notes/service.py +3 -3
- package/corpus/backend/no_unique_columns_on_soft_delete_models/compliant-01/modules/notes/models.py +17 -17
- package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/expected-findings.json +7 -7
- package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/modules/notes/models.py +6 -6
- package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/expected-findings.json +7 -7
- package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/modules/notes/models.py +13 -13
- package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/expected-findings.json +7 -7
- package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/modules/notes/models.py +16 -16
- package/corpus/backend/offset_queries_declare_ordering/compliant-01/modules/notes/service.py +4 -4
- package/corpus/backend/offset_queries_declare_ordering/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/path_id_params_are_uuid/compliant-01/modules/notes/router.py +6 -6
- package/corpus/backend/path_id_params_are_uuid/violation-01/modules/notes/router.py +6 -6
- package/corpus/backend/policy_refs_resolve/compliant-01/modules/notes/module.py +7 -7
- package/corpus/backend/policy_refs_resolve/violation-01/modules/notes/module.py +8 -8
- package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/module.py +7 -7
- package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/router.py +3 -3
- package/corpus/backend/public_modules_are_read_only/violation-01/expected-findings.json +7 -7
- package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/module.py +7 -7
- package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/public_modules_are_read_only/violation-02/expected-findings.json +7 -7
- package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/module.py +7 -7
- package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/router.py +5 -5
- package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/models.py +6 -6
- package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/service.py +8 -8
- package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/models.py +9 -9
- package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/service.py +9 -9
- package/corpus/backend/reads_use_base_query/violation-01/modules/notes/models.py +6 -6
- package/corpus/backend/reads_use_base_query/violation-01/modules/notes/service.py +9 -9
- package/corpus/backend/reads_use_base_query/violation-02/expected-findings.json +7 -7
- package/corpus/backend/reads_use_base_query/violation-02/modules/notes/models.py +6 -6
- package/corpus/backend/reads_use_base_query/violation-02/modules/notes/service.py +8 -8
- package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/models.py +6 -6
- package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/router.py +3 -3
- package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/schemas.py +5 -5
- package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/models.py +6 -6
- package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/routes_declare_response_model/compliant-01/modules/notes/router.py +8 -8
- package/corpus/backend/routes_declare_response_model/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/safe_methods_are_read_only/compliant-01/modules/notes/router.py +6 -6
- package/corpus/backend/safe_methods_are_read_only/compliant-02/modules/notes/router.py +5 -5
- package/corpus/backend/safe_methods_are_read_only/violation-01/expected-findings.json +7 -7
- package/corpus/backend/safe_methods_are_read_only/violation-01/modules/notes/router.py +3 -3
- package/corpus/backend/safe_methods_are_read_only/violation-02/expected-findings.json +7 -7
- package/corpus/backend/safe_methods_are_read_only/violation-02/modules/notes/router.py +5 -5
- package/corpus/backend/safe_methods_are_read_only/violation-03/expected-findings.json +7 -7
- package/corpus/backend/safe_methods_are_read_only/violation-03/modules/notes/router.py +3 -3
- package/corpus/backend/schemas_exclude_sensitive_fields/compliant-01/modules/users/schemas.py +5 -5
- package/corpus/backend/schemas_exclude_sensitive_fields/compliant-02/modules/accounts/schemas.py +12 -12
- package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/expected-findings.json +7 -7
- package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/modules/users/schemas.py +3 -3
- package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/expected-findings.json +7 -7
- package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/modules/connectors/router.py +8 -8
- package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/expected-findings.json +17 -17
- package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/modules/integrations/schemas.py +7 -7
- package/corpus/backend/session_imported_from_sqlmodel/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/session_imported_from_sqlmodel/violation-01/modules/notes/service.py +5 -5
- package/corpus/backend/table_models_use_base_table/compliant-01/modules/notes/models.py +6 -6
- package/corpus/backend/table_models_use_base_table/violation-01/modules/notes/models.py +5 -5
- package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/migrations/versions/0a1b2c3d4e5f_create_notes_tables.py +22 -22
- package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/models.py +6 -6
- package/corpus/backend/tables_have_migrations/compliant-02/capabilities/ledger/models.py +6 -6
- package/corpus/backend/tables_have_migrations/violation-01/expected-findings.json +7 -7
- package/corpus/backend/tables_have_migrations/violation-01/modules/notes/models.py +6 -6
- package/corpus/backend/tables_have_migrations/violation-02/expected-findings.json +7 -7
- package/corpus/backend/tables_have_migrations/violation-02/modules/notes/models.py +10 -10
- package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/models.py +6 -6
- package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/service.py +5 -5
- package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/models.py +8 -8
- package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/service.py +5 -5
- package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/models.py +6 -6
- package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/service.py +5 -5
- package/corpus/backend/ungoverned_escape_hatch/compliant-01/modules/notes/service.py +5 -5
- package/corpus/backend/ungoverned_escape_hatch/compliant-02/modules/notes/service.py +4 -4
- package/corpus/backend/ungoverned_escape_hatch/violation-01/modules/notes/service.py +2 -2
- package/corpus/backend/update_schemas_inherit_base_update_schema/compliant-01/modules/notes/schemas.py +2 -2
- package/corpus/backend/update_schemas_inherit_base_update_schema/violation-01/modules/notes/schemas.py +2 -2
- package/corpus/backend/update_schemas_inherit_base_update_schema/violation-02/modules/notes/schemas.py +5 -5
- package/corpus/frontend/escape-hatch/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/escape-hatch/violation-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/escape-hatch/violation-02/src/modules/widgets/Widget.tsx +7 -7
- package/corpus/frontend/escape-hatch/violation-03/src/modules/widgets/Widget.tsx +7 -7
- package/corpus/frontend/generated-client-only/compliant-01/src/modules/widgets/Widget.tsx +6 -6
- package/corpus/frontend/generated-client-only/compliant-02/src/modules/widgets/Widget.tsx +21 -21
- package/corpus/frontend/generated-client-only/compliant-03/src/modules/widgets/Widget.tsx +7 -7
- package/corpus/frontend/generated-client-only/compliant-04/src/modules/widgets/Widget.tsx +17 -17
- package/corpus/frontend/generated-client-only/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/generated-client-only/violation-02/src/modules/widgets/Widget.tsx +9 -9
- package/corpus/frontend/generated-client-only/violation-03/src/modules/widgets/Widget.tsx +14 -14
- package/corpus/frontend/generated-client-only/violation-04/src/modules/widgets/Widget.tsx +6 -6
- package/corpus/frontend/layout-contract/compliant-01/layout-contract.json +3 -3
- package/corpus/frontend/layout-contract/compliant-01/src/modules/widgets/Widget.tsx +8 -8
- package/corpus/frontend/layout-contract/violation-01/layout-contract.json +3 -3
- package/corpus/frontend/layout-contract/violation-01/src/modules/widgets/Widget.tsx +8 -8
- package/corpus/frontend/no-cross-module-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/no-cross-module-imports/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-deep-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/no-deep-imports/violation-01/src/modules/widgets/Widget.tsx +2 -2
- package/corpus/frontend/no-dom-html-injection/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/no-dom-html-injection/compliant-02/src/modules/widgets/Widget.tsx +11 -11
- package/corpus/frontend/no-dom-html-injection/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-dom-html-injection/violation-02/src/modules/widgets/Widget.tsx +7 -7
- package/corpus/frontend/no-dom-html-injection/violation-03/src/modules/widgets/Widget.tsx +12 -12
- package/corpus/frontend/no-eval/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/no-eval/compliant-02/src/modules/widgets/Widget.tsx +11 -11
- package/corpus/frontend/no-eval/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-eval/violation-02/src/modules/widgets/Widget.tsx +7 -7
- package/corpus/frontend/no-eval/violation-03/src/modules/widgets/Widget.tsx +10 -10
- package/corpus/frontend/no-eval/violation-04/src/modules/widgets/Widget.tsx +6 -6
- package/corpus/frontend/no-inline-styling/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/no-inline-styling/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-inline-styling/violation-02/src/modules/widgets/Widget.tsx +6 -6
- package/corpus/frontend/no-style-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/no-style-imports/violation-01/src/modules/widgets/Widget.tsx +2 -2
- package/corpus/frontend/no-unsafe-href/compliant-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-unsafe-href/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-unsafe-target-blank/compliant-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/no-unsafe-target-blank/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/router-links/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/router-links/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/corpus/frontend/token-styled-elements/compliant-01/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/token-styled-elements/compliant-02/src/modules/widgets/Widget.tsx +4 -4
- package/corpus/frontend/token-styled-elements/violation-01/src/modules/widgets/Widget.tsx +3 -3
- package/findings.schema.json +40 -40
- package/package.json +23 -23
- package/restricted-surface.json +10 -10
- package/scorecard.schema.json +52 -52
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.15.0
|
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://terp.dev/spec/app-check-report.schema.json",
|
|
4
|
-
"title": "Terp Standard application check report",
|
|
5
|
-
"description": "The complete, self-describing result of ONE checker invocation over ONE application tree. Findings alone cannot support per-rule verdicts: a rule with zero findings is only passing if the run actually evaluated it, and a consumer must never supply that knowledge itself — its own catalog copy can be newer or older than the checked app's pinned toolchain. So the report carries its own evaluated-rule inventory, the spec version its rule ids resolve against, and the checker's identity: everything a driving tool needs to join verdicts to the catalog fail-closed. A rule absent from `rules` and `not_applicable` was not evaluated and can never render as passing. A multi-surface toolchain emits one report per checker run; a consumer merges reports through their inventories.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["terp_check_report", "spec_version", "checker", "ok", "rules", "findings"],
|
|
8
|
-
"additionalProperties": false,
|
|
9
|
-
"properties": {
|
|
10
|
-
"terp_check_report": {
|
|
11
|
-
"type": "integer",
|
|
12
|
-
"enum": [1],
|
|
13
|
-
"description": "Format marker and major version of this envelope. A consumer refuses an unknown marker value rather than guessing the shape."
|
|
14
|
-
},
|
|
15
|
-
"spec_version": {
|
|
16
|
-
"type": "string",
|
|
17
|
-
"description": "The spec semver (VERSION) whose catalog the rule ids in this report resolve against — the checked app's own pinned standard, which may differ from any consumer-side catalog copy.",
|
|
18
|
-
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
19
|
-
},
|
|
20
|
-
"checker": {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"description": "The checker's identity: tool name and its own version — the same identity the checker's certification scorecard (scorecard.schema.json) carries, so a consumer can tie a report to a verifiable certification claim.",
|
|
23
|
-
"required": ["tool", "version"],
|
|
24
|
-
"additionalProperties": false,
|
|
25
|
-
"properties": {
|
|
26
|
-
"tool": { "type": "string", "minLength": 1 },
|
|
27
|
-
"version": { "type": "string", "minLength": 1 }
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
"ok": {
|
|
31
|
-
"type": "boolean",
|
|
32
|
-
"description": "The run's overall verdict: true only when the checker completed and attributed no findings. Overall only — per-rule verdicts are always derived from `rules` + `findings`, never from this flag."
|
|
33
|
-
},
|
|
34
|
-
"error": {
|
|
35
|
-
"type": "string",
|
|
36
|
-
"minLength": 1,
|
|
37
|
-
"description": "Present when the run failed to complete (a crash, a timeout, an unreadable tree): what went wrong, for humans. An erroring report is never ok, and it must not claim rules it did not fully evaluate — fail visible, never a silent pass."
|
|
38
|
-
},
|
|
39
|
-
"rules": {
|
|
40
|
-
"type": "array",
|
|
41
|
-
"description": "The evaluated-rule inventory: every catalog rule id this run actually held the tree to. Zero findings for a listed rule means pass; an unlisted rule has NO verdict from this run (a consumer renders it unknown, never green). A finding's rule is normally listed here; a consumer treats an attributed finding as a failure regardless.",
|
|
42
|
-
"items": {
|
|
43
|
-
"type": "string",
|
|
44
|
-
"pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"not_applicable": {
|
|
48
|
-
"type": "array",
|
|
49
|
-
"description": "Opt-in rules the checked tree has not enabled (for example a slot-typed layout contract without its checked-in configuration): published as their own state so a consumer renders them as not applicable — never as passing, never as unknown.",
|
|
50
|
-
"items": {
|
|
51
|
-
"type": "string",
|
|
52
|
-
"pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
"findings": {
|
|
56
|
-
"type": "array",
|
|
57
|
-
"description": "The findings this run attributed to catalog rule ids. Each item is exactly the finding format's shape (findings.schema.json items — the spec suite holds the two identical), so a findings consumer and a report consumer read one shape.",
|
|
58
|
-
"items": {
|
|
59
|
-
"type": "object",
|
|
60
|
-
"required": ["rule", "path"],
|
|
61
|
-
"additionalProperties": false,
|
|
62
|
-
"properties": {
|
|
63
|
-
"rule": {
|
|
64
|
-
"type": "string",
|
|
65
|
-
"description": "The Terp Standard catalog id the finding realises (catalog/<surface>/<rule>.json).",
|
|
66
|
-
"pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
|
|
67
|
-
},
|
|
68
|
-
"path": {
|
|
69
|
-
"type": "string",
|
|
70
|
-
"description": "File path relative to the checked tree's root (the corpus case root, or the app root), with forward slashes."
|
|
71
|
-
},
|
|
72
|
-
"line": {
|
|
73
|
-
"type": "integer",
|
|
74
|
-
"description": "1-based line of the violation, when the checker can locate it.",
|
|
75
|
-
"minimum": 1
|
|
76
|
-
},
|
|
77
|
-
"message": {
|
|
78
|
-
"type": "string",
|
|
79
|
-
"description": "Human-readable explanation; ideally directive (states the compliant fix)."
|
|
80
|
-
},
|
|
81
|
-
"fix_hint": {
|
|
82
|
-
"type": "string",
|
|
83
|
-
"description": "Agent-oriented remediation metadata: the compliant construct that replaces the violating one, sourced from the catalog entry's non-normative `reference` field (or the checker's own knowledge of the sanctioned pattern). Optional — a consumer must not require it."
|
|
84
|
-
},
|
|
85
|
-
"fingerprint": {
|
|
86
|
-
"type": "string",
|
|
87
|
-
"description": "A stable, checker-chosen identifier for THIS violation instance (e.g. a hash over rule + path + violating source construct), so consumers can track a finding across line-shifting edits. Optional; the fingerprint scheme is checker-internal — only stability across reformatting is expected."
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
"unattributed": {
|
|
93
|
-
"type": "array",
|
|
94
|
-
"description": "Messages the run produced OUTSIDE the standard's rules (another configured tool's diagnostics sharing the run, a parse error): surfaced, never dropped — and never attributable to a catalog id, which the finding format's rule pattern refuses by construction.",
|
|
95
|
-
"items": {
|
|
96
|
-
"type": "object",
|
|
97
|
-
"required": ["path", "message"],
|
|
98
|
-
"additionalProperties": false,
|
|
99
|
-
"properties": {
|
|
100
|
-
"path": {
|
|
101
|
-
"type": "string",
|
|
102
|
-
"description": "File path relative to the checked tree's root, with forward slashes."
|
|
103
|
-
},
|
|
104
|
-
"line": {
|
|
105
|
-
"type": "integer",
|
|
106
|
-
"description": "1-based line of the message, when the producer located one.",
|
|
107
|
-
"minimum": 1
|
|
108
|
-
},
|
|
109
|
-
"message": {
|
|
110
|
-
"type": "string",
|
|
111
|
-
"description": "The diagnostic text, verbatim."
|
|
112
|
-
},
|
|
113
|
-
"reported_as": {
|
|
114
|
-
"type": "string",
|
|
115
|
-
"minLength": 1,
|
|
116
|
-
"description": "The tool-internal rule id the message surfaced as, when one exists — omitted (never null) otherwise."
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://terp.dev/spec/app-check-report.schema.json",
|
|
4
|
+
"title": "Terp Standard application check report",
|
|
5
|
+
"description": "The complete, self-describing result of ONE checker invocation over ONE application tree. Findings alone cannot support per-rule verdicts: a rule with zero findings is only passing if the run actually evaluated it, and a consumer must never supply that knowledge itself — its own catalog copy can be newer or older than the checked app's pinned toolchain. So the report carries its own evaluated-rule inventory, the spec version its rule ids resolve against, and the checker's identity: everything a driving tool needs to join verdicts to the catalog fail-closed. A rule absent from `rules` and `not_applicable` was not evaluated and can never render as passing. A multi-surface toolchain emits one report per checker run; a consumer merges reports through their inventories.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["terp_check_report", "spec_version", "checker", "ok", "rules", "findings"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"terp_check_report": {
|
|
11
|
+
"type": "integer",
|
|
12
|
+
"enum": [1],
|
|
13
|
+
"description": "Format marker and major version of this envelope. A consumer refuses an unknown marker value rather than guessing the shape."
|
|
14
|
+
},
|
|
15
|
+
"spec_version": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "The spec semver (VERSION) whose catalog the rule ids in this report resolve against — the checked app's own pinned standard, which may differ from any consumer-side catalog copy.",
|
|
18
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
19
|
+
},
|
|
20
|
+
"checker": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"description": "The checker's identity: tool name and its own version — the same identity the checker's certification scorecard (scorecard.schema.json) carries, so a consumer can tie a report to a verifiable certification claim.",
|
|
23
|
+
"required": ["tool", "version"],
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"properties": {
|
|
26
|
+
"tool": { "type": "string", "minLength": 1 },
|
|
27
|
+
"version": { "type": "string", "minLength": 1 }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"ok": {
|
|
31
|
+
"type": "boolean",
|
|
32
|
+
"description": "The run's overall verdict: true only when the checker completed and attributed no findings. Overall only — per-rule verdicts are always derived from `rules` + `findings`, never from this flag."
|
|
33
|
+
},
|
|
34
|
+
"error": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"minLength": 1,
|
|
37
|
+
"description": "Present when the run failed to complete (a crash, a timeout, an unreadable tree): what went wrong, for humans. An erroring report is never ok, and it must not claim rules it did not fully evaluate — fail visible, never a silent pass."
|
|
38
|
+
},
|
|
39
|
+
"rules": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"description": "The evaluated-rule inventory: every catalog rule id this run actually held the tree to. Zero findings for a listed rule means pass; an unlisted rule has NO verdict from this run (a consumer renders it unknown, never green). A finding's rule is normally listed here; a consumer treats an attributed finding as a failure regardless.",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"not_applicable": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"description": "Opt-in rules the checked tree has not enabled (for example a slot-typed layout contract without its checked-in configuration): published as their own state so a consumer renders them as not applicable — never as passing, never as unknown.",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"findings": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"description": "The findings this run attributed to catalog rule ids. Each item is exactly the finding format's shape (findings.schema.json items — the spec suite holds the two identical), so a findings consumer and a report consumer read one shape.",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"required": ["rule", "path"],
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"properties": {
|
|
63
|
+
"rule": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": "The Terp Standard catalog id the finding realises (catalog/<surface>/<rule>.json).",
|
|
66
|
+
"pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
|
|
67
|
+
},
|
|
68
|
+
"path": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "File path relative to the checked tree's root (the corpus case root, or the app root), with forward slashes."
|
|
71
|
+
},
|
|
72
|
+
"line": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"description": "1-based line of the violation, when the checker can locate it.",
|
|
75
|
+
"minimum": 1
|
|
76
|
+
},
|
|
77
|
+
"message": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Human-readable explanation; ideally directive (states the compliant fix)."
|
|
80
|
+
},
|
|
81
|
+
"fix_hint": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "Agent-oriented remediation metadata: the compliant construct that replaces the violating one, sourced from the catalog entry's non-normative `reference` field (or the checker's own knowledge of the sanctioned pattern). Optional — a consumer must not require it."
|
|
84
|
+
},
|
|
85
|
+
"fingerprint": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"description": "A stable, checker-chosen identifier for THIS violation instance (e.g. a hash over rule + path + violating source construct), so consumers can track a finding across line-shifting edits. Optional; the fingerprint scheme is checker-internal — only stability across reformatting is expected."
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"unattributed": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"description": "Messages the run produced OUTSIDE the standard's rules (another configured tool's diagnostics sharing the run, a parse error): surfaced, never dropped — and never attributable to a catalog id, which the finding format's rule pattern refuses by construction.",
|
|
95
|
+
"items": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"required": ["path", "message"],
|
|
98
|
+
"additionalProperties": false,
|
|
99
|
+
"properties": {
|
|
100
|
+
"path": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"description": "File path relative to the checked tree's root, with forward slashes."
|
|
103
|
+
},
|
|
104
|
+
"line": {
|
|
105
|
+
"type": "integer",
|
|
106
|
+
"description": "1-based line of the message, when the producer located one.",
|
|
107
|
+
"minimum": 1
|
|
108
|
+
},
|
|
109
|
+
"message": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"description": "The diagnostic text, verbatim."
|
|
112
|
+
},
|
|
113
|
+
"reported_as": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"minLength": 1,
|
|
116
|
+
"description": "The tool-internal rule id the message surfaced as, when one exists — omitted (never null) otherwise."
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://terp.dev/spec/assurance-profile.schema.json",
|
|
4
|
-
"title": "Terp assurance profile",
|
|
5
|
-
"description": "The machine-readable release-assurance claim a Terp toolchain emits after running its release verification profile: one document composing the named evidence lanes of the standard. The lane vocabulary and each lane's requirement level are normative and fixed by the spec (see the README's assurance-profile section): terp-standard, appsec-baseline and dependency-audit are REQUIRED - the claim ('ok') is true only when every required lane passed; a11y and blackbox-conformance are RECOMMENDED - they inform the reader but never carry the claim. Requirement levels are deliberately NOT a field of the document: an emitter cannot demote a required lane, and a consumer joins the document to the spec (via spec_version) for the canonical mapping. Every lane of the vocabulary must appear exactly once - omitting a lane would hide it. A lane's verdict is composed from the named verification checks ('checks', the check ids of the toolchain's release profile); a lane the toolchain does not realise yet is reported 'not-run' with no checks, never silently dropped and never counted as passed.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["terp_assurance", "spec_version", "toolchain", "profile", "ok", "lanes"],
|
|
8
|
-
"additionalProperties": false,
|
|
9
|
-
"properties": {
|
|
10
|
-
"terp_assurance": {
|
|
11
|
-
"description": "The assurance-profile document format version.",
|
|
12
|
-
"enum": [1]
|
|
13
|
-
},
|
|
14
|
-
"spec_version": {
|
|
15
|
-
"type": "string",
|
|
16
|
-
"description": "The spec semver (VERSION) whose lane vocabulary and requirement mapping this document claims against.",
|
|
17
|
-
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
18
|
-
},
|
|
19
|
-
"toolchain": {
|
|
20
|
-
"type": "object",
|
|
21
|
-
"description": "The emitting toolchain's identity: tool name and its own version.",
|
|
22
|
-
"required": ["tool", "version"],
|
|
23
|
-
"additionalProperties": false,
|
|
24
|
-
"properties": {
|
|
25
|
-
"tool": { "type": "string", "minLength": 1 },
|
|
26
|
-
"version": { "type": "string", "minLength": 1 }
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"profile": {
|
|
30
|
-
"type": "string",
|
|
31
|
-
"description": "The verification profile the toolchain realised the lanes from (the reference toolchain: 'release').",
|
|
32
|
-
"minLength": 1
|
|
33
|
-
},
|
|
34
|
-
"ok": {
|
|
35
|
-
"type": "boolean",
|
|
36
|
-
"description": "The claim: true exactly when every REQUIRED lane has status 'passed'. Recommended lanes never affect it."
|
|
37
|
-
},
|
|
38
|
-
"lanes": {
|
|
39
|
-
"type": "array",
|
|
40
|
-
"description": "One entry per lane of the spec's vocabulary, each exactly once.",
|
|
41
|
-
"minItems": 1,
|
|
42
|
-
"items": {
|
|
43
|
-
"type": "object",
|
|
44
|
-
"required": ["id", "status", "checks"],
|
|
45
|
-
"additionalProperties": false,
|
|
46
|
-
"properties": {
|
|
47
|
-
"id": {
|
|
48
|
-
"description": "The lane, from the spec's fixed vocabulary.",
|
|
49
|
-
"enum": [
|
|
50
|
-
"terp-standard",
|
|
51
|
-
"appsec-baseline",
|
|
52
|
-
"dependency-audit",
|
|
53
|
-
"a11y",
|
|
54
|
-
"blackbox-conformance"
|
|
55
|
-
]
|
|
56
|
-
},
|
|
57
|
-
"status": {
|
|
58
|
-
"description": "'passed' (every composing check green), 'failed' (any composing check red), or 'not-run' (the toolchain does not realise the lane, or its precondition was unmet). 'not-run' is never a pass.",
|
|
59
|
-
"enum": ["passed", "failed", "not-run"]
|
|
60
|
-
},
|
|
61
|
-
"checks": {
|
|
62
|
-
"type": "array",
|
|
63
|
-
"description": "The verification-check ids whose verdicts compose this lane (evidence pointers into the toolchain's verification envelope). Non-empty for 'passed'/'failed'; empty for 'not-run' - a verdict without evidence is not a verdict.",
|
|
64
|
-
"items": { "type": "string", "minLength": 1 }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://terp.dev/spec/assurance-profile.schema.json",
|
|
4
|
+
"title": "Terp assurance profile",
|
|
5
|
+
"description": "The machine-readable release-assurance claim a Terp toolchain emits after running its release verification profile: one document composing the named evidence lanes of the standard. The lane vocabulary and each lane's requirement level are normative and fixed by the spec (see the README's assurance-profile section): terp-standard, appsec-baseline and dependency-audit are REQUIRED - the claim ('ok') is true only when every required lane passed; a11y and blackbox-conformance are RECOMMENDED - they inform the reader but never carry the claim. Requirement levels are deliberately NOT a field of the document: an emitter cannot demote a required lane, and a consumer joins the document to the spec (via spec_version) for the canonical mapping. Every lane of the vocabulary must appear exactly once - omitting a lane would hide it. A lane's verdict is composed from the named verification checks ('checks', the check ids of the toolchain's release profile); a lane the toolchain does not realise yet is reported 'not-run' with no checks, never silently dropped and never counted as passed.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["terp_assurance", "spec_version", "toolchain", "profile", "ok", "lanes"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"terp_assurance": {
|
|
11
|
+
"description": "The assurance-profile document format version.",
|
|
12
|
+
"enum": [1]
|
|
13
|
+
},
|
|
14
|
+
"spec_version": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "The spec semver (VERSION) whose lane vocabulary and requirement mapping this document claims against.",
|
|
17
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
18
|
+
},
|
|
19
|
+
"toolchain": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "The emitting toolchain's identity: tool name and its own version.",
|
|
22
|
+
"required": ["tool", "version"],
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"properties": {
|
|
25
|
+
"tool": { "type": "string", "minLength": 1 },
|
|
26
|
+
"version": { "type": "string", "minLength": 1 }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"profile": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "The verification profile the toolchain realised the lanes from (the reference toolchain: 'release').",
|
|
32
|
+
"minLength": 1
|
|
33
|
+
},
|
|
34
|
+
"ok": {
|
|
35
|
+
"type": "boolean",
|
|
36
|
+
"description": "The claim: true exactly when every REQUIRED lane has status 'passed'. Recommended lanes never affect it."
|
|
37
|
+
},
|
|
38
|
+
"lanes": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"description": "One entry per lane of the spec's vocabulary, each exactly once.",
|
|
41
|
+
"minItems": 1,
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"required": ["id", "status", "checks"],
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"properties": {
|
|
47
|
+
"id": {
|
|
48
|
+
"description": "The lane, from the spec's fixed vocabulary.",
|
|
49
|
+
"enum": [
|
|
50
|
+
"terp-standard",
|
|
51
|
+
"appsec-baseline",
|
|
52
|
+
"dependency-audit",
|
|
53
|
+
"a11y",
|
|
54
|
+
"blackbox-conformance"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"status": {
|
|
58
|
+
"description": "'passed' (every composing check green), 'failed' (any composing check red), or 'not-run' (the toolchain does not realise the lane, or its precondition was unmet). 'not-run' is never a pass.",
|
|
59
|
+
"enum": ["passed", "failed", "not-run"]
|
|
60
|
+
},
|
|
61
|
+
"checks": {
|
|
62
|
+
"type": "array",
|
|
63
|
+
"description": "The verification-check ids whose verdicts compose this lane (evidence pointers into the toolchain's verification envelope). Non-empty for 'passed'/'failed'; empty for 'not-run' - a verdict without evidence is not a verdict.",
|
|
64
|
+
"items": { "type": "string", "minLength": 1 }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/alembic_downgrades_not_empty",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "A migration's downgrade must reverse the change, not be an empty stub",
|
|
5
|
-
"intent": "A migration that leaves its downgrade step empty (a bare no-op with no operations) cannot be rolled back: reversing the revision silently leaves the schema mismatched instead of restoring the previous state. Each migration's downgrade must either perform the reverse operations or, for a deliberately irreversible step, carry a comment explaining why the no-op is intentional.",
|
|
6
|
-
"layer": "static-portable",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_alembic_downgrades_not_empty"
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"reference": "In each revision file under migrations/versions, a downgrade function whose body (after any docstring) is a lone pass / ellipsis or is empty, with no explanatory comment, is flagged; a reverse operation or an in-body '#' comment clears it.",
|
|
15
|
-
"opt_out": "# arch-allow-alembic-downgrades-not-empty: <reason>",
|
|
16
|
-
"runtime": {
|
|
17
|
-
"applicability": "not-applicable",
|
|
18
|
-
"rationale": "An empty downgrade is a property of the authored revision file; a forward deploy never executes downgrade, so the emptiness is invisible at runtime until a rollback is attempted, and the reversibility guarantee exists only in source."
|
|
19
|
-
},
|
|
20
|
-
"guide_topic": "migrations",
|
|
21
|
-
"corpus": true
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/alembic_downgrades_not_empty",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "A migration's downgrade must reverse the change, not be an empty stub",
|
|
5
|
+
"intent": "A migration that leaves its downgrade step empty (a bare no-op with no operations) cannot be rolled back: reversing the revision silently leaves the schema mismatched instead of restoring the previous state. Each migration's downgrade must either perform the reverse operations or, for a deliberately irreversible step, carry a comment explaining why the no-op is intentional.",
|
|
6
|
+
"layer": "static-portable",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_alembic_downgrades_not_empty"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"reference": "In each revision file under migrations/versions, a downgrade function whose body (after any docstring) is a lone pass / ellipsis or is empty, with no explanatory comment, is flagged; a reverse operation or an in-body '#' comment clears it.",
|
|
15
|
+
"opt_out": "# arch-allow-alembic-downgrades-not-empty: <reason>",
|
|
16
|
+
"runtime": {
|
|
17
|
+
"applicability": "not-applicable",
|
|
18
|
+
"rationale": "An empty downgrade is a property of the authored revision file; a forward deploy never executes downgrade, so the emptiness is invisible at runtime until a rollback is attempted, and the reversibility guarantee exists only in source."
|
|
19
|
+
},
|
|
20
|
+
"guide_topic": "migrations",
|
|
21
|
+
"corpus": true
|
|
22
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/base_query_not_overridden",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "A service never overrides the scoped base read query — read filters compose through the declared filter seam",
|
|
5
|
-
"intent": "The base read query composes the non-droppable row scope (soft-delete plus every registered capability predicate, e.g. tenancy) with the service's declared business filters. Overriding that composition point — the old, footgun-y seam — can silently drop soft-delete or tenant scoping the moment the override forgets to delegate up the chain, leaking soft-deleted or cross-tenant rows. Add static read conditions through the declared filter seam (it returns conditions, not a query, so scope cannot be dropped); a per-call filter belongs in a bespoke read built on the scoped base query.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_base_query_not_overridden"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "apply_row_scope"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"reference": "BaseService.base_query composes the scope; add filters via business_filters(), or build bespoke reads on base_query().where(...) (ADR 0017).",
|
|
20
|
-
"opt_out": "# arch-allow-base-query-not-overridden: <reason>",
|
|
21
|
-
"runtime": {
|
|
22
|
-
"applicability": "required",
|
|
23
|
-
"rationale": "The request session re-scopes every user-facing ORM read idempotently (apply_row_scope in the write-guarded session), so even a query built by an override that dropped super().base_query() is filtered again before execution."
|
|
24
|
-
},
|
|
25
|
-
"guide_topic": "service",
|
|
26
|
-
"corpus": true
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/base_query_not_overridden",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "A service never overrides the scoped base read query — read filters compose through the declared filter seam",
|
|
5
|
+
"intent": "The base read query composes the non-droppable row scope (soft-delete plus every registered capability predicate, e.g. tenancy) with the service's declared business filters. Overriding that composition point — the old, footgun-y seam — can silently drop soft-delete or tenant scoping the moment the override forgets to delegate up the chain, leaking soft-deleted or cross-tenant rows. Add static read conditions through the declared filter seam (it returns conditions, not a query, so scope cannot be dropped); a per-call filter belongs in a bespoke read built on the scoped base query.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_base_query_not_overridden"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "apply_row_scope"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"reference": "BaseService.base_query composes the scope; add filters via business_filters(), or build bespoke reads on base_query().where(...) (ADR 0017).",
|
|
20
|
+
"opt_out": "# arch-allow-base-query-not-overridden: <reason>",
|
|
21
|
+
"runtime": {
|
|
22
|
+
"applicability": "required",
|
|
23
|
+
"rationale": "The request session re-scopes every user-facing ORM read idempotently (apply_row_scope in the write-guarded session), so even a query built by an override that dropped super().base_query() is filtered again before execution."
|
|
24
|
+
},
|
|
25
|
+
"guide_topic": "service",
|
|
26
|
+
"corpus": true
|
|
27
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/canonical_module_shape",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Every wired module directory carries the canonical parts: models, schemas, service, router, manifest",
|
|
5
|
-
"intent": "Terp modules are uniform on purpose: the table lives in models, the DTOs in schemas, the logic in service, the routes in router, and the manifest declares the module — so the shape is predictable to discover and the other rules (response models, input caps, audited writes, the declared policy) have the surface they scan. A directory is treated as a module once it ships a manifest or a mounted router; it must then carry all of the canonical parts, and the rule names each missing one. Requiring the manifest is deliberate: a directory that ships a router with no manifest would otherwise be invisible to this rule and to modules_declare_policy (which only scans manifests), so it could mount a router with no declared policy unnoticed. A directory with neither signal (a partial or a shared-asset / helper directory) is left alone.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_canonical_module_shape"
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"reference": "modules/<name>/ carries models.py, schemas.py, service.py, router.py and module.py (the ModuleSpec manifest).",
|
|
15
|
-
"opt_out": "# arch-allow-canonical-module-shape: <reason>",
|
|
16
|
-
"runtime": {
|
|
17
|
-
"applicability": "not-applicable",
|
|
18
|
-
"rationale": "Repository-layout governance: the module directory's file shape exists only in the source tree. Composition consumes the ModuleSpec object, which carries no memory of which files declared it, so no runtime seam can observe the layout."
|
|
19
|
-
},
|
|
20
|
-
"guide_topic": "module",
|
|
21
|
-
"corpus": true
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/canonical_module_shape",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Every wired module directory carries the canonical parts: models, schemas, service, router, manifest",
|
|
5
|
+
"intent": "Terp modules are uniform on purpose: the table lives in models, the DTOs in schemas, the logic in service, the routes in router, and the manifest declares the module — so the shape is predictable to discover and the other rules (response models, input caps, audited writes, the declared policy) have the surface they scan. A directory is treated as a module once it ships a manifest or a mounted router; it must then carry all of the canonical parts, and the rule names each missing one. Requiring the manifest is deliberate: a directory that ships a router with no manifest would otherwise be invisible to this rule and to modules_declare_policy (which only scans manifests), so it could mount a router with no declared policy unnoticed. A directory with neither signal (a partial or a shared-asset / helper directory) is left alone.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_canonical_module_shape"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"reference": "modules/<name>/ carries models.py, schemas.py, service.py, router.py and module.py (the ModuleSpec manifest).",
|
|
15
|
+
"opt_out": "# arch-allow-canonical-module-shape: <reason>",
|
|
16
|
+
"runtime": {
|
|
17
|
+
"applicability": "not-applicable",
|
|
18
|
+
"rationale": "Repository-layout governance: the module directory's file shape exists only in the source tree. Composition consumes the ModuleSpec object, which carries no memory of which files declared it, so no runtime seam can observe the layout."
|
|
19
|
+
},
|
|
20
|
+
"guide_topic": "module",
|
|
21
|
+
"corpus": true
|
|
22
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/datetime_columns_are_timezone_aware",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Stored timestamp columns must keep their timezone",
|
|
5
|
-
"intent": "A timestamp column declared without an explicit timezone maps to a naive database type, which discards the zone of even a correctly-built aware value on the way in — so the moment the row records is ambiguous, and ordering or comparison across zones is silently wrong on read. This is the storage half of the hole whose in-memory half the naive-timestamp rule closes: capturing an aware value is worth nothing if the column cannot keep it. Every persisted timestamp column must declare timezone-aware storage.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_datetime_columns_are_timezone_aware"
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"reference": "On a table model, a datetime (or datetime | None) field is refused unless its declaration pins a timezone-aware column type — Field(sa_type=DateTime(timezone=True)) or Field(sa_column=Column(DateTime(timezone=True), ...)); the TIMESTAMP spelling and a qualified name (sa.DateTime) count equally. A bare annotation, a Field() that never names the column type, and an explicit timezone=False are all naive storage. Fields a table inherits from a mixin declared in the same file are in scope, since the column lands on the table either way. Classes no table inherits (DTOs, unrelated payloads) declare no columns and are out of scope.",
|
|
15
|
-
"opt_out": "# arch-allow-datetime-columns-are-timezone-aware: <reason>",
|
|
16
|
-
"runtime": {
|
|
17
|
-
"applicability": "deferred",
|
|
18
|
-
"rationale": "Unlike the in-memory naive-timestamp rule — where the zone is already lost at the point the value is built, so no seam downstream can recover it — this invariant survives into the running system: the mapped column type is inspectable on the ORM metadata the framework already collects at boot, and a naive timestamp column is decidable there without ambiguity. A fail-closed boot check over the collected table metadata would therefore add independent fidelity, catching a column whose naive type arrives from a path the source scan cannot follow: a dynamically built column, a mixin imported from another package, or a table contributed by an installed capability. The framework owns that seam (its create_app boot validations) but has not yet shipped this control.",
|
|
19
|
-
"tracking": "terp-framework reference implementation: the create_app boot-validation seam in terp.core.app (alongside the existing _validate_* boot checks) — to walk the collected table metadata and refuse a timestamp column whose mapped type is not timezone-aware."
|
|
20
|
-
},
|
|
21
|
-
"guide_topic": "module",
|
|
22
|
-
"corpus": true
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/datetime_columns_are_timezone_aware",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Stored timestamp columns must keep their timezone",
|
|
5
|
+
"intent": "A timestamp column declared without an explicit timezone maps to a naive database type, which discards the zone of even a correctly-built aware value on the way in — so the moment the row records is ambiguous, and ordering or comparison across zones is silently wrong on read. This is the storage half of the hole whose in-memory half the naive-timestamp rule closes: capturing an aware value is worth nothing if the column cannot keep it. Every persisted timestamp column must declare timezone-aware storage.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_datetime_columns_are_timezone_aware"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"reference": "On a table model, a datetime (or datetime | None) field is refused unless its declaration pins a timezone-aware column type — Field(sa_type=DateTime(timezone=True)) or Field(sa_column=Column(DateTime(timezone=True), ...)); the TIMESTAMP spelling and a qualified name (sa.DateTime) count equally. A bare annotation, a Field() that never names the column type, and an explicit timezone=False are all naive storage. Fields a table inherits from a mixin declared in the same file are in scope, since the column lands on the table either way. Classes no table inherits (DTOs, unrelated payloads) declare no columns and are out of scope.",
|
|
15
|
+
"opt_out": "# arch-allow-datetime-columns-are-timezone-aware: <reason>",
|
|
16
|
+
"runtime": {
|
|
17
|
+
"applicability": "deferred",
|
|
18
|
+
"rationale": "Unlike the in-memory naive-timestamp rule — where the zone is already lost at the point the value is built, so no seam downstream can recover it — this invariant survives into the running system: the mapped column type is inspectable on the ORM metadata the framework already collects at boot, and a naive timestamp column is decidable there without ambiguity. A fail-closed boot check over the collected table metadata would therefore add independent fidelity, catching a column whose naive type arrives from a path the source scan cannot follow: a dynamically built column, a mixin imported from another package, or a table contributed by an installed capability. The framework owns that seam (its create_app boot validations) but has not yet shipped this control.",
|
|
19
|
+
"tracking": "terp-framework reference implementation: the create_app boot-validation seam in terp.core.app (alongside the existing _validate_* boot checks) — to walk the collected table metadata and refuse a timestamp column whose mapped type is not timezone-aware."
|
|
20
|
+
},
|
|
21
|
+
"guide_topic": "module",
|
|
22
|
+
"corpus": true
|
|
23
|
+
}
|