@terpjs/spec 0.14.0 → 0.16.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
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/escape_hatch_budget",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Opt-out marker counts must exactly match the checked-in budget (a ratchet)",
|
|
5
|
-
"intent": "The budget is a checked-in per-app object mapping each opt-out marker to its count. Actual usage must equal it exactly: a marker that rose needs a justified budget bump in the same change; one that dropped must be lowered to lock in the win; an unbudgeted marker must be added with a justified count. This keeps every secure-by-default opt-out visible, greppable, and governed.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_escape_hatch_budget"
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"reference": "# arch-allow-<rule>: <reason> markers reconciled against the app's checked-in escape-hatch budget JSON (design §8). The rule carries no opt_out: governance cannot be waived by the mechanism it governs.",
|
|
15
|
-
"runtime": {
|
|
16
|
-
"applicability": "not-applicable",
|
|
17
|
-
"rationale": "The budget ratchet reconciles a checked-in governance file against source markers -- both sides are repository artifacts with no runtime counterpart. The controls the markers opt out of keep their own runtime halves regardless."
|
|
18
|
-
},
|
|
19
|
-
"guide_topic": "rules",
|
|
20
|
-
"corpus": true
|
|
21
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/escape_hatch_budget",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Opt-out marker counts must exactly match the checked-in budget (a ratchet)",
|
|
5
|
+
"intent": "The budget is a checked-in per-app object mapping each opt-out marker to its count. Actual usage must equal it exactly: a marker that rose needs a justified budget bump in the same change; one that dropped must be lowered to lock in the win; an unbudgeted marker must be added with a justified count. This keeps every secure-by-default opt-out visible, greppable, and governed.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_escape_hatch_budget"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"reference": "# arch-allow-<rule>: <reason> markers reconciled against the app's checked-in escape-hatch budget JSON (design §8). The rule carries no opt_out: governance cannot be waived by the mechanism it governs.",
|
|
15
|
+
"runtime": {
|
|
16
|
+
"applicability": "not-applicable",
|
|
17
|
+
"rationale": "The budget ratchet reconciles a checked-in governance file against source markers -- both sides are repository artifacts with no runtime counterpart. The controls the markers opt out of keep their own runtime halves regardless."
|
|
18
|
+
},
|
|
19
|
+
"guide_topic": "rules",
|
|
20
|
+
"corpus": true
|
|
21
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/events_reference_catalog",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Emitted / subscribed events are typed catalog constants, never bare strings",
|
|
5
|
-
"intent": "The event bus carries the same no-drift guarantee as the permission model: every event a module emits or subscribes to is a typed definition from the control-plane catalog. The rule forbids a bare string (or an inline, ad hoc definition) wherever an event is named — the event argument of an emit call, the argument of a subscribe decorator, the emits / subscribes lists of the module manifest, and the lifecycle event map — so an event name can never drift in outside the catalog.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_events_reference_catalog"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "emit"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"reference": "EventDefinition constants from the control-plane catalog, cited in emit(...) / subscribe(...) / ModuleSpec(emits=..., subscribes=...) / LifecycleEventMap(...); the runtime half is terp.core.emit.",
|
|
20
|
-
"opt_out": "# arch-allow-events-reference-catalog: <reason>",
|
|
21
|
-
"runtime": {
|
|
22
|
-
"applicability": "required",
|
|
23
|
-
"rationale": "emit() is the single producer chokepoint and fails closed on an event that is unknown to -- or shadows -- the registered EventCatalog entry; boot additionally refuses a ModuleSpec whose declared emits/subscribes miss the catalog (ControlPlane.validation_errors)."
|
|
24
|
-
},
|
|
25
|
-
"guide_topic": "events",
|
|
26
|
-
"corpus": true
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/events_reference_catalog",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Emitted / subscribed events are typed catalog constants, never bare strings",
|
|
5
|
+
"intent": "The event bus carries the same no-drift guarantee as the permission model: every event a module emits or subscribes to is a typed definition from the control-plane catalog. The rule forbids a bare string (or an inline, ad hoc definition) wherever an event is named — the event argument of an emit call, the argument of a subscribe decorator, the emits / subscribes lists of the module manifest, and the lifecycle event map — so an event name can never drift in outside the catalog.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_events_reference_catalog"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "emit"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"reference": "EventDefinition constants from the control-plane catalog, cited in emit(...) / subscribe(...) / ModuleSpec(emits=..., subscribes=...) / LifecycleEventMap(...); the runtime half is terp.core.emit.",
|
|
20
|
+
"opt_out": "# arch-allow-events-reference-catalog: <reason>",
|
|
21
|
+
"runtime": {
|
|
22
|
+
"applicability": "required",
|
|
23
|
+
"rationale": "emit() is the single producer chokepoint and fails closed on an event that is unknown to -- or shadows -- the registered EventCatalog entry; boot additionally refuses a ModuleSpec whose declared emits/subscribes miss the catalog (ControlPlane.validation_errors)."
|
|
24
|
+
},
|
|
25
|
+
"guide_topic": "events",
|
|
26
|
+
"corpus": true
|
|
27
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/input_schemas_exclude_managed_columns",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "No input schema declares a framework-managed column",
|
|
5
|
-
"intent": "An input schema is a *Create / *Update or any class used as a request body (a route handler's body parameter, or a generated CRUD router's create/update schema) — the same role-based definition the input-cap rule uses, so an off-convention DTO (UserProvision, LoginRequest) is covered too. The write chokepoint copies a schema's fields onto the model, so a client-settable id / version / tenant_id / created_by_id is an over-posting (mass-assignment) hole — a client could forge the primary key, defeat optimistic concurrency, or cross a tenant boundary. The framework assigns every managed column centrally; an input schema must never expose one. (The write chokepoint also strips the same set at runtime — this rule is the build-time half of that two-layer control.)",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_input_schemas_exclude_managed_columns"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "_without_managed_columns"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"reference": "BaseService.create/update strip the managed set via _without_managed_columns; build_crud_router create/update schemas are scanned too.",
|
|
20
|
-
"opt_out": "# arch-allow-input-schemas-exclude-managed-columns: <reason>",
|
|
21
|
-
"runtime": {
|
|
22
|
-
"applicability": "required",
|
|
23
|
-
"rationale": "BaseService.create/update strip every framework-managed column from the inbound payload at the write chokepoint (_without_managed_columns), so even an over-wide schema that ships cannot mass-assign id / version / scope / actor columns."
|
|
24
|
-
},
|
|
25
|
-
"guide_topic": "module",
|
|
26
|
-
"corpus": true
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/input_schemas_exclude_managed_columns",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "No input schema declares a framework-managed column",
|
|
5
|
+
"intent": "An input schema is a *Create / *Update or any class used as a request body (a route handler's body parameter, or a generated CRUD router's create/update schema) — the same role-based definition the input-cap rule uses, so an off-convention DTO (UserProvision, LoginRequest) is covered too. The write chokepoint copies a schema's fields onto the model, so a client-settable id / version / tenant_id / created_by_id is an over-posting (mass-assignment) hole — a client could forge the primary key, defeat optimistic concurrency, or cross a tenant boundary. The framework assigns every managed column centrally; an input schema must never expose one. (The write chokepoint also strips the same set at runtime — this rule is the build-time half of that two-layer control.)",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_input_schemas_exclude_managed_columns"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "_without_managed_columns"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"reference": "BaseService.create/update strip the managed set via _without_managed_columns; build_crud_router create/update schemas are scanned too.",
|
|
20
|
+
"opt_out": "# arch-allow-input-schemas-exclude-managed-columns: <reason>",
|
|
21
|
+
"runtime": {
|
|
22
|
+
"applicability": "required",
|
|
23
|
+
"rationale": "BaseService.create/update strip every framework-managed column from the inbound payload at the write chokepoint (_without_managed_columns), so even an over-wide schema that ships cannot mass-assign id / version / scope / actor columns."
|
|
24
|
+
},
|
|
25
|
+
"guide_topic": "module",
|
|
26
|
+
"corpus": true
|
|
27
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/input_str_fields_have_max_length",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Every string a client can supply caps its length",
|
|
5
|
-
"intent": "A field is client-supplied when it lives on a table model, on a *Create / *Update schema, or on any class used as a request body (a route handler's body parameter, or a generated CRUD router's create/update schema) — so an input DTO named off-convention (LoginRequest, UserProvision) is capped too, not only the *Create / *Update ones. Plain strings, optional strings, and sequence containers of strings all count; an uncapped one is an unbounded-input (DoS / abuse) hole.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_input_str_fields_have_max_length"
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"reference": "str / str | None / list[str] fields declare max_length; build_crud_router create/update schemas are scanned too.",
|
|
15
|
-
"opt_out": "# arch-allow-input-str-fields-have-max-length: <reason>",
|
|
16
|
-
"runtime": {
|
|
17
|
-
"applicability": "not-applicable",
|
|
18
|
-
"rationale": "The rule's job is to force the cap into the authored schema -- and a declared cap is then enforced at request time by validation itself, so compliance creates its own runtime control. No shipped seam can supply a missing per-field cap; the per-module request-size middleware bounds the payload wholesale as a distinct Tier-A control."
|
|
19
|
-
},
|
|
20
|
-
"guide_topic": "module",
|
|
21
|
-
"corpus": true
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/input_str_fields_have_max_length",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Every string a client can supply caps its length",
|
|
5
|
+
"intent": "A field is client-supplied when it lives on a table model, on a *Create / *Update schema, or on any class used as a request body (a route handler's body parameter, or a generated CRUD router's create/update schema) — so an input DTO named off-convention (LoginRequest, UserProvision) is capped too, not only the *Create / *Update ones. Plain strings, optional strings, and sequence containers of strings all count; an uncapped one is an unbounded-input (DoS / abuse) hole.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_input_str_fields_have_max_length"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"reference": "str / str | None / list[str] fields declare max_length; build_crud_router create/update schemas are scanned too.",
|
|
15
|
+
"opt_out": "# arch-allow-input-str-fields-have-max-length: <reason>",
|
|
16
|
+
"runtime": {
|
|
17
|
+
"applicability": "not-applicable",
|
|
18
|
+
"rationale": "The rule's job is to force the cap into the authored schema -- and a declared cap is then enforced at request time by validation itself, so compliance creates its own runtime control. No shipped seam can supply a missing per-field cap; the per-module request-size middleware bounds the payload wholesale as a distinct Tier-A control."
|
|
19
|
+
},
|
|
20
|
+
"guide_topic": "module",
|
|
21
|
+
"corpus": true
|
|
22
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/jobs_reference_catalog",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Enqueued / declared jobs are typed catalog constants, never bare strings",
|
|
5
|
-
"intent": "Background work carries the same no-drift guarantee as the event bus: every job a module enqueues or declares is a typed definition from the control-plane catalog. The rule forbids a bare string (or an inline, ad hoc definition) wherever a job is named — the job argument of an enqueue call and the jobs list of the module manifest — so a job name can never drift in outside the catalog. The runtime half is the enqueue chokepoint, which rejects a job not registered in the active catalog.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_jobs_reference_catalog"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "enqueue"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"reference": "JobDefinition constants from the control-plane catalog, cited in enqueue(job=...) / ModuleSpec(jobs=[...]); the runtime half is terp.core.enqueue.",
|
|
20
|
-
"opt_out": "# arch-allow-jobs-reference-catalog: <reason>",
|
|
21
|
-
"runtime": {
|
|
22
|
-
"applicability": "required",
|
|
23
|
-
"rationale": "enqueue is the single producer chokepoint and fails closed on a job the active JobCatalog does not register (the job analogue of emit); execution re-checks (run_job raises JobError on a stale envelope), and boot refuses a ModuleSpec whose declared jobs miss the catalog (ControlPlane.validation_errors)."
|
|
24
|
-
},
|
|
25
|
-
"guide_topic": "jobs",
|
|
26
|
-
"corpus": true
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/jobs_reference_catalog",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Enqueued / declared jobs are typed catalog constants, never bare strings",
|
|
5
|
+
"intent": "Background work carries the same no-drift guarantee as the event bus: every job a module enqueues or declares is a typed definition from the control-plane catalog. The rule forbids a bare string (or an inline, ad hoc definition) wherever a job is named — the job argument of an enqueue call and the jobs list of the module manifest — so a job name can never drift in outside the catalog. The runtime half is the enqueue chokepoint, which rejects a job not registered in the active catalog.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_jobs_reference_catalog"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "enqueue"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"reference": "JobDefinition constants from the control-plane catalog, cited in enqueue(job=...) / ModuleSpec(jobs=[...]); the runtime half is terp.core.enqueue.",
|
|
20
|
+
"opt_out": "# arch-allow-jobs-reference-catalog: <reason>",
|
|
21
|
+
"runtime": {
|
|
22
|
+
"applicability": "required",
|
|
23
|
+
"rationale": "enqueue is the single producer chokepoint and fails closed on a job the active JobCatalog does not register (the job analogue of emit); execution re-checks (run_job raises JobError on a stale envelope), and boot refuses a ModuleSpec whose declared jobs miss the catalog (ControlPlane.validation_errors)."
|
|
24
|
+
},
|
|
25
|
+
"guide_topic": "jobs",
|
|
26
|
+
"corpus": true
|
|
27
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/list_routes_paginate",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "A list route returns a capped page envelope, never a bare unbounded collection",
|
|
5
|
-
"intent": "Pagination is a mandatory cross-cutting control: a route whose declared response type is a bare collection serializes an unbounded result — a resource-exhaustion and over-exposure footgun on a large table, and a page-envelope guarantee that was previously only a convention. Wrap the read DTO in the framework's capped page envelope so every list is bounded and uniformly shaped. A single-object response is unaffected; both decorator routes and imperative route registration are checked.",
|
|
6
|
-
"layer": "black-box",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_list_routes_paginate"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "black-box",
|
|
15
|
-
"tool": "@
|
|
16
|
-
"ref": "standard: list routes return a capped Page envelope"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"kind": "runtime",
|
|
20
|
-
"tool": "terp.core",
|
|
21
|
-
"ref": "_validate_list_routes_paginate"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"reference": "response_model=Page[ReadDTO] returned via Page.of(...) with PaginationDep (ADR 0006, Tier A); bare list[...] / Sequence[...] response models are refused.",
|
|
25
|
-
"opt_out": "# arch-allow-list-routes-paginate: <reason>",
|
|
26
|
-
"runtime": {
|
|
27
|
-
"applicability": "required",
|
|
28
|
-
"rationale": "At boot, the route scan on the composition seam (the same seam as _validate_router_response_models) walks every composed route \u2014 decorator, imperative add_api_route, and nested included routers alike \u2014 and raises a BootError naming this rule and the route when a route declares a bare list[...] / Sequence[...] response_model instead of Page[T]. PaginationDep's fail-closed skip/limit caps and the @
|
|
29
|
-
},
|
|
30
|
-
"guide_topic": "service",
|
|
31
|
-
"corpus": true
|
|
32
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/list_routes_paginate",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "A list route returns a capped page envelope, never a bare unbounded collection",
|
|
5
|
+
"intent": "Pagination is a mandatory cross-cutting control: a route whose declared response type is a bare collection serializes an unbounded result — a resource-exhaustion and over-exposure footgun on a large table, and a page-envelope guarantee that was previously only a convention. Wrap the read DTO in the framework's capped page envelope so every list is bounded and uniformly shaped. A single-object response is unaffected; both decorator routes and imperative route registration are checked.",
|
|
6
|
+
"layer": "black-box",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_list_routes_paginate"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "black-box",
|
|
15
|
+
"tool": "@terpjs/conformance",
|
|
16
|
+
"ref": "standard: list routes return a capped Page envelope"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"kind": "runtime",
|
|
20
|
+
"tool": "terp.core",
|
|
21
|
+
"ref": "_validate_list_routes_paginate"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"reference": "response_model=Page[ReadDTO] returned via Page.of(...) with PaginationDep (ADR 0006, Tier A); bare list[...] / Sequence[...] response models are refused.",
|
|
25
|
+
"opt_out": "# arch-allow-list-routes-paginate: <reason>",
|
|
26
|
+
"runtime": {
|
|
27
|
+
"applicability": "required",
|
|
28
|
+
"rationale": "At boot, the route scan on the composition seam (the same seam as _validate_router_response_models) walks every composed route \u2014 decorator, imperative add_api_route, and nested included routers alike \u2014 and raises a BootError naming this rule and the route when a route declares a bare list[...] / Sequence[...] response_model instead of Page[T]. PaginationDep's fail-closed skip/limit caps and the @terpjs/conformance Page-envelope probe remain in place on top of this in-app refusal."
|
|
29
|
+
},
|
|
30
|
+
"guide_topic": "service",
|
|
31
|
+
"corpus": true
|
|
32
|
+
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/modules_declare_policy",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Every module manifest declares an access policy",
|
|
5
|
-
"intent": "The deny-by-default posture hangs on the declaration: composition mounts a module's routes behind the guard its declared policy configures, so a module without one has no stated authority model at all — there is nothing to authorize against, and the framework refuses to guess. Requiring the declaration in the manifest keeps every module's access decision explicit, reviewable, and in one predictable place.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_modules_declare_policy"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "build_guard"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"runtime": {
|
|
20
|
-
"applicability": "required"
|
|
21
|
-
},
|
|
22
|
-
"reference": "modules/<name>/module.py declares ModuleSpec(policy=Policy(...)); create_app mounts the router behind the guard built from it (build_guard).",
|
|
23
|
-
"opt_out": "# arch-allow-modules-declare-policy: <reason>",
|
|
24
|
-
"guide_topic": "policy",
|
|
25
|
-
"corpus": true
|
|
26
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/modules_declare_policy",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Every module manifest declares an access policy",
|
|
5
|
+
"intent": "The deny-by-default posture hangs on the declaration: composition mounts a module's routes behind the guard its declared policy configures, so a module without one has no stated authority model at all — there is nothing to authorize against, and the framework refuses to guess. Requiring the declaration in the manifest keeps every module's access decision explicit, reviewable, and in one predictable place.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_modules_declare_policy"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "build_guard"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"runtime": {
|
|
20
|
+
"applicability": "required"
|
|
21
|
+
},
|
|
22
|
+
"reference": "modules/<name>/module.py declares ModuleSpec(policy=Policy(...)); create_app mounts the router behind the guard built from it (build_guard).",
|
|
23
|
+
"opt_out": "# arch-allow-modules-declare-policy: <reason>",
|
|
24
|
+
"guide_topic": "policy",
|
|
25
|
+
"corpus": true
|
|
26
|
+
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/mutations_emit_audit",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Modules never write to the session directly; mutations go through the audited chokepoint",
|
|
5
|
-
"intent": "Audit is auto-emitted from the single audited write chokepoint of the model's service, inside the write's transaction. A module that calls the session's persistence methods itself — or smuggles a write through a session-executed data-modification statement — bypasses that chokepoint and would persist a mutation with no audit trail. The receiver is recognised by the conventional session names and by any parameter annotated with the session type (so renaming the variable does not evade the rule). Routing every write through the service keeps the trail structural — a method call on the model's service is fine; a raw session write is not.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_mutations_emit_audit"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "WriteGuardedSession"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"reference": "BaseService.create/update/delete (_save/_remove) is the audited chokepoint; session.add/delete/merge/commit/flush, bulk_* helpers, and DML via session.execute/exec are refused, on conventional names and Session/SessionDep-annotated parameters alike.",
|
|
20
|
-
"opt_out": "# arch-allow-mutations-emit-audit: <reason>",
|
|
21
|
-
"runtime": {
|
|
22
|
-
"applicability": "required"
|
|
23
|
-
},
|
|
24
|
-
"guide_topic": "service",
|
|
25
|
-
"corpus": true
|
|
26
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/mutations_emit_audit",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Modules never write to the session directly; mutations go through the audited chokepoint",
|
|
5
|
+
"intent": "Audit is auto-emitted from the single audited write chokepoint of the model's service, inside the write's transaction. A module that calls the session's persistence methods itself — or smuggles a write through a session-executed data-modification statement — bypasses that chokepoint and would persist a mutation with no audit trail. The receiver is recognised by the conventional session names and by any parameter annotated with the session type (so renaming the variable does not evade the rule). Routing every write through the service keeps the trail structural — a method call on the model's service is fine; a raw session write is not.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_mutations_emit_audit"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "WriteGuardedSession"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"reference": "BaseService.create/update/delete (_save/_remove) is the audited chokepoint; session.add/delete/merge/commit/flush, bulk_* helpers, and DML via session.execute/exec are refused, on conventional names and Session/SessionDep-annotated parameters alike.",
|
|
20
|
+
"opt_out": "# arch-allow-mutations-emit-audit: <reason>",
|
|
21
|
+
"runtime": {
|
|
22
|
+
"applicability": "required"
|
|
23
|
+
},
|
|
24
|
+
"guide_topic": "service",
|
|
25
|
+
"corpus": true
|
|
26
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/mutations_require_write_role",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "A module with a mutating route must not gate writes below its read tier",
|
|
5
|
-
"intent": "A module that exposes a mutating route is a write surface, so its declared policy must gate writes at or above the read tier — otherwise anyone who can read can also mutate (privilege inversion). Two shapes are caught statically: the write tier set to the read floor, and a default-ladder inversion where the write rank is below the read rank (including a raised read tier whose write tier is left at the lower default). The framework's default policy is the safe shape. A custom role ladder's ranks are not knowable from a source scan, so those are enforced by the boot-time check — this rule is the early-warning build-time half. A public module is governed by public_modules_are_read_only instead. The check is tied to the policy bound in the module's manifest.",
|
|
6
|
-
"layer": "static-bespoke",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_mutations_require_write_role"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "build_guard"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"kind": "runtime",
|
|
20
|
-
"tool": "terp.core",
|
|
21
|
-
"ref": "_validate_policy_write_tiers"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"runtime": {
|
|
25
|
-
"applicability": "required",
|
|
26
|
-
"rationale": "Two runtime halves: the request guard authorizes every mutating method at the policy's write tier (build_guard), and boot refuses a Policy whose write rank sits below its read rank for any role ladder, including custom ranks a source scan cannot know (_validate_policy_write_tiers)."
|
|
27
|
-
},
|
|
28
|
-
"opt_out": "# arch-allow-mutations-require-write-role: <reason>",
|
|
29
|
-
"reference": "Policy(write=Roles.VIEWER), Policy.tiers(write=...) at the read floor, and default-ladder inversions like Policy(read=Roles.ADMIN, write=Roles.EDITOR) are refused; Policy.default() is the safe shape, and create_app -> _validate_policy_write_tiers covers custom ladders at boot.",
|
|
30
|
-
"guide_topic": "policy",
|
|
31
|
-
"corpus": true
|
|
32
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/mutations_require_write_role",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "A module with a mutating route must not gate writes below its read tier",
|
|
5
|
+
"intent": "A module that exposes a mutating route is a write surface, so its declared policy must gate writes at or above the read tier — otherwise anyone who can read can also mutate (privilege inversion). Two shapes are caught statically: the write tier set to the read floor, and a default-ladder inversion where the write rank is below the read rank (including a raised read tier whose write tier is left at the lower default). The framework's default policy is the safe shape. A custom role ladder's ranks are not knowable from a source scan, so those are enforced by the boot-time check — this rule is the early-warning build-time half. A public module is governed by public_modules_are_read_only instead. The check is tied to the policy bound in the module's manifest.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_mutations_require_write_role"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "build_guard"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"kind": "runtime",
|
|
20
|
+
"tool": "terp.core",
|
|
21
|
+
"ref": "_validate_policy_write_tiers"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"runtime": {
|
|
25
|
+
"applicability": "required",
|
|
26
|
+
"rationale": "Two runtime halves: the request guard authorizes every mutating method at the policy's write tier (build_guard), and boot refuses a Policy whose write rank sits below its read rank for any role ladder, including custom ranks a source scan cannot know (_validate_policy_write_tiers)."
|
|
27
|
+
},
|
|
28
|
+
"opt_out": "# arch-allow-mutations-require-write-role: <reason>",
|
|
29
|
+
"reference": "Policy(write=Roles.VIEWER), Policy.tiers(write=...) at the read floor, and default-ladder inversions like Policy(read=Roles.ADMIN, write=Roles.EDITOR) are refused; Policy.default() is the safe shape, and create_app -> _validate_policy_write_tiers covers custom ladders at boot.",
|
|
30
|
+
"guide_topic": "policy",
|
|
31
|
+
"corpus": true
|
|
32
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/no_adhoc_background_runtime",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "App modules don't import a background engine / runtime directly \u2014 only adapter caps do",
|
|
5
|
-
"intent": "Background work (a scheduled sync, an export, a webhook) goes through the typed enqueue chokepoint and the context-binding kernel runner, so the engine that actually runs it stays a composition-root choice wired into an opt-in adapter capability, never an import baked into domain code. The rule forbids importing broker / scheduler engines (and a raw thread- or process-execution construct, or a bare import that can reach one) anywhere in an app module; an explicit synchronization primitive is a correctness tool, not background execution, and stays allowed. The constructive counterpart is the jobs seam itself: every job runs through the enqueue chokepoint and the active queue, so an adapter swap never touches a call site. An adapter capability legitimately imports its engine under a budgeted opt-out marker.",
|
|
6
|
-
"layer": "static-portable",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_no_adhoc_background_runtime"
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"reference": "terp.core.enqueue + the active JobQueue are the sanctioned seam; Celery / Azure Service Bus / Redis / APScheduler imports and threading/multiprocessing execution constructs (Thread, Process, pools) are refused, while primitives like RLock stay allowed.",
|
|
15
|
-
"opt_out": "# arch-allow-no-adhoc-background-runtime: <reason>",
|
|
16
|
-
"runtime": {
|
|
17
|
-
"applicability": "not-applicable",
|
|
18
|
-
"rationale": "Import-form rule: which package imported an engine is a source fact, and at runtime the sanctioned adapter capability legitimately holds the same engine in the same process, so engine presence cannot be attributed to an app module. The constructive counterpart is the jobs seam itself (enqueue + the active JobQueue) -- it makes ad hoc runtimes unnecessary, but nothing can refuse a hand-spawned thread fail-closed."
|
|
19
|
-
},
|
|
20
|
-
"guide_topic": "jobs",
|
|
21
|
-
"corpus": true
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/no_adhoc_background_runtime",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "App modules don't import a background engine / runtime directly \u2014 only adapter caps do",
|
|
5
|
+
"intent": "Background work (a scheduled sync, an export, a webhook) goes through the typed enqueue chokepoint and the context-binding kernel runner, so the engine that actually runs it stays a composition-root choice wired into an opt-in adapter capability, never an import baked into domain code. The rule forbids importing broker / scheduler engines (and a raw thread- or process-execution construct, or a bare import that can reach one) anywhere in an app module; an explicit synchronization primitive is a correctness tool, not background execution, and stays allowed. The constructive counterpart is the jobs seam itself: every job runs through the enqueue chokepoint and the active queue, so an adapter swap never touches a call site. An adapter capability legitimately imports its engine under a budgeted opt-out marker.",
|
|
6
|
+
"layer": "static-portable",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_no_adhoc_background_runtime"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"reference": "terp.core.enqueue + the active JobQueue are the sanctioned seam; Celery / Azure Service Bus / Redis / APScheduler imports and threading/multiprocessing execution constructs (Thread, Process, pools) are refused, while primitives like RLock stay allowed.",
|
|
15
|
+
"opt_out": "# arch-allow-no-adhoc-background-runtime: <reason>",
|
|
16
|
+
"runtime": {
|
|
17
|
+
"applicability": "not-applicable",
|
|
18
|
+
"rationale": "Import-form rule: which package imported an engine is a source fact, and at runtime the sanctioned adapter capability legitimately holds the same engine in the same process, so engine presence cannot be attributed to an app module. The constructive counterpart is the jobs seam itself (enqueue + the active JobQueue) -- it makes ad hoc runtimes unnecessary, but nothing can refuse a hand-spawned thread fail-closed."
|
|
19
|
+
},
|
|
20
|
+
"guide_topic": "jobs",
|
|
21
|
+
"corpus": true
|
|
22
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "backend/no_adhoc_config_decrypt",
|
|
3
|
-
"surface": "backend",
|
|
4
|
-
"title": "Sealed config is never decrypted ad hoc; one budgeted call site only",
|
|
5
|
-
"intent": "A sealed configuration value stays opaque in app code: a module renders the masked form and never unseals a value itself. The single sanctioned decrypt site carries a justified opt-out marker counted against the app's escape-hatch budget. The runtime half is the decrypt chokepoint itself, which fails closed unless called from the one registered call site.",
|
|
6
|
-
"layer": "static-portable",
|
|
7
|
-
"enforcement": [
|
|
8
|
-
{
|
|
9
|
-
"kind": "build-time",
|
|
10
|
-
"tool": "terp.arch",
|
|
11
|
-
"ref": "check_no_adhoc_config_decrypt"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"kind": "runtime",
|
|
15
|
-
"tool": "terp.core",
|
|
16
|
-
"ref": "decrypt_config"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"reference": "mask_config renders sealed values (enc:v1:...); terp.core.secrets.decrypt_config fails closed unless called from the site registered via register_decrypt_call_site (design §5.4).",
|
|
20
|
-
"opt_out": "# arch-allow-no-adhoc-config-decrypt: <reason>",
|
|
21
|
-
"runtime": {
|
|
22
|
-
"applicability": "required",
|
|
23
|
-
"rationale": "decrypt_config itself fails closed on every path unless invoked from the single call site registered via register_decrypt_call_site -- an ad hoc decrypt raises SecretsError at runtime; the budgeted marker governs which site that is."
|
|
24
|
-
},
|
|
25
|
-
"guide_topic": "capability",
|
|
26
|
-
"corpus": true
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/no_adhoc_config_decrypt",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "Sealed config is never decrypted ad hoc; one budgeted call site only",
|
|
5
|
+
"intent": "A sealed configuration value stays opaque in app code: a module renders the masked form and never unseals a value itself. The single sanctioned decrypt site carries a justified opt-out marker counted against the app's escape-hatch budget. The runtime half is the decrypt chokepoint itself, which fails closed unless called from the one registered call site.",
|
|
6
|
+
"layer": "static-portable",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_no_adhoc_config_decrypt"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "decrypt_config"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"reference": "mask_config renders sealed values (enc:v1:...); terp.core.secrets.decrypt_config fails closed unless called from the site registered via register_decrypt_call_site (design §5.4).",
|
|
20
|
+
"opt_out": "# arch-allow-no-adhoc-config-decrypt: <reason>",
|
|
21
|
+
"runtime": {
|
|
22
|
+
"applicability": "required",
|
|
23
|
+
"rationale": "decrypt_config itself fails closed on every path unless invoked from the single call site registered via register_decrypt_call_site -- an ad hoc decrypt raises SecretsError at runtime; the budgeted marker governs which site that is."
|
|
24
|
+
},
|
|
25
|
+
"guide_topic": "capability",
|
|
26
|
+
"corpus": true
|
|
27
|
+
}
|