atlan-application-sdk-conformance 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. atlan_application_sdk_conformance-0.2.0.dist-info/METADATA +64 -0
  2. atlan_application_sdk_conformance-0.2.0.dist-info/RECORD +49 -0
  3. atlan_application_sdk_conformance-0.2.0.dist-info/WHEEL +4 -0
  4. atlan_application_sdk_conformance-0.2.0.dist-info/entry_points.txt +2 -0
  5. conformance/__init__.py +7 -0
  6. conformance/cli.py +126 -0
  7. conformance/docs/rules/ci.md +33 -0
  8. conformance/docs/rules/error-handling.md +283 -0
  9. conformance/docs/rules/logging.md +277 -0
  10. conformance/docs/schema-contract.md +255 -0
  11. conformance/package-lock.json +1760 -0
  12. conformance/package.json +18 -0
  13. conformance/programs/areas/ci.prose.md +47 -0
  14. conformance/programs/areas/error-handling.prose.md +55 -0
  15. conformance/programs/areas/logging.prose.md +47 -0
  16. conformance/programs/conformance-remediation.prose.md +85 -0
  17. conformance/programs/functions/detect-violations.prose.md +99 -0
  18. conformance/programs/functions/orthogonal-gate.prose.md +39 -0
  19. conformance/programs/functions/recheck-narrowest.prose.md +51 -0
  20. conformance/programs/functions/remediate-finding.prose.md +123 -0
  21. conformance/programs/patterns/detect-fix-recheck.prose.md +110 -0
  22. conformance/suite/__init__.py +27 -0
  23. conformance/suite/checks/__init__.py +1 -0
  24. conformance/suite/checks/actions_pinning.py +202 -0
  25. conformance/suite/checks/error_handling/__init__.py +212 -0
  26. conformance/suite/checks/error_handling/_checker.py +151 -0
  27. conformance/suite/checks/error_handling/_collect.py +37 -0
  28. conformance/suite/checks/error_handling/_constants.py +115 -0
  29. conformance/suite/checks/error_handling/_directives.py +131 -0
  30. conformance/suite/checks/error_handling/_helpers.py +225 -0
  31. conformance/suite/checks/error_handling/exception_chaining.py +53 -0
  32. conformance/suite/checks/error_handling/security.py +49 -0
  33. conformance/suite/checks/error_handling/silent_swallow.py +312 -0
  34. conformance/suite/checks/error_handling/untyped_raise.py +110 -0
  35. conformance/suite/rules/__init__.py +46 -0
  36. conformance/suite/rules/ci.py +27 -0
  37. conformance/suite/rules/error_handling.py +360 -0
  38. conformance/suite/rules/logging.py +336 -0
  39. conformance/suite/runner.py +240 -0
  40. conformance/suite/schema/__init__.py +78 -0
  41. conformance/suite/schema/builder.py +328 -0
  42. conformance/suite/schema/catalog.py +120 -0
  43. conformance/suite/schema/disposition.py +146 -0
  44. conformance/suite/schema/extensions.py +185 -0
  45. conformance/suite/schema/findings.py +74 -0
  46. conformance/suite/schema/sarif-schema-2.1.0.json +2882 -0
  47. conformance/suite/schema/sarif.py +360 -0
  48. conformance/suite/schema/validate.py +63 -0
  49. conformance/tools/generate_rule_docs.py +295 -0
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: atlan-application-sdk-conformance
3
+ Version: 0.2.0
4
+ Summary: Conformance suite, remediation programs, and CLI for the Atlan Application SDK
5
+ Author-email: Atlan App Team <connect@atlan.com>
6
+ License-Expression: Apache-2.0
7
+ Keywords: atlan,conformance,linting,remediation,sdk
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: jsonschema<5.0.0,>=4.23.0
19
+ Requires-Dist: pydantic<3.0.0,>=2.10.6
20
+ Provides-Extra: test
21
+ Requires-Dist: atlan-application-sdk; extra == 'test'
22
+ Requires-Dist: pytest-asyncio<2.0.0,>=1.4.0; extra == 'test'
23
+ Requires-Dist: pytest<10.0.0,>=8.3.3; extra == 'test'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # atlan-application-sdk-conformance
27
+
28
+ Dev-only conformance suite, remediation programs, and CLI for the
29
+ [Atlan Application SDK](https://pypi.org/project/atlan-application-sdk/).
30
+
31
+ **Do not add this as a production dependency.** It is intended for developer
32
+ machines and CI only.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ uv add --dev atlan-application-sdk-conformance
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ # Run the conformance suite
44
+ uv run atlan-application-sdk-conformance detect --repo . --series E,L,C --output report.sarif
45
+
46
+ # Get the path to bundled remediation programs (for SKILL.md / reactor)
47
+ uv run atlan-application-sdk-conformance programs-dir
48
+
49
+ # Regenerate rule docs
50
+ uv run atlan-application-sdk-conformance gen-rule-docs
51
+ ```
52
+
53
+ ## In CI
54
+
55
+ Consumer repos should reference this package via the reusable workflow in
56
+ `atlanhq/application-sdk`:
57
+
58
+ ```yaml
59
+ uses: atlanhq/application-sdk/.github/workflows/conformance-reusable.yaml@main
60
+ # No inputs required — the published PyPI package is used by default.
61
+ ```
62
+
63
+ See `conformance/programs/conformance-remediation.prose.md` for the
64
+ `/remediate` skill entry contract.
@@ -0,0 +1,49 @@
1
+ conformance/__init__.py,sha256=f5ihJyNJuyw1MkpgTuPWdcf1jsksR2lpGnE3FJg1S7Y,199
2
+ conformance/cli.py,sha256=ZaQ2CUbHhHo19S9T0tx6YD7NHOpihZDsItQ6_X9fs_U,4013
3
+ conformance/package-lock.json,sha256=F8EwDEjecgD82OM19F1YGdtrVvPOzIVAaJI5JdeMKtw,58365
4
+ conformance/package.json,sha256=egDs4ZNoIpfJiGKlOD95egYFDoc53fGcEqDxOp8VBa8,463
5
+ conformance/docs/schema-contract.md,sha256=v8g3hDvzH_yNgbZ3t9ZXY483wkI8JnnWuWQK_uQbpa4,11859
6
+ conformance/docs/rules/ci.md,sha256=PlEiRfDNaybMzbvDEepCmrbS4yy1XPfahhg3jCeqngw,1229
7
+ conformance/docs/rules/error-handling.md,sha256=Z40cuoBPXZD2UNK0qoJeme5Xg1xUCihClUyECuNdomM,12648
8
+ conformance/docs/rules/logging.md,sha256=fqnpi_bhgyFzWlimGUND8WanqKsVN0IhkexL3bGtjZA,11301
9
+ conformance/programs/conformance-remediation.prose.md,sha256=eYCuS_I5iUAr34UjdbvHPUBoSva5m1Eva_Lz9lZK1Mg,2812
10
+ conformance/programs/areas/ci.prose.md,sha256=08LhyngT5KbG1SMLNnddRs5v3qz0uaeJbZ9GVb5pa1w,1390
11
+ conformance/programs/areas/error-handling.prose.md,sha256=xPoDR7uvmtqoAFLfLLrA6JbryjnK5B1rCTHERrwV1FA,1792
12
+ conformance/programs/areas/logging.prose.md,sha256=6-6NtipMJYm4m7zaoUKD4YLnipenUfnzMRbyBWCeVhI,1407
13
+ conformance/programs/functions/detect-violations.prose.md,sha256=VEYLrb4GetCOllUgnO7Nv48Mt14EkrX0ZCGyrU3tfjk,4544
14
+ conformance/programs/functions/orthogonal-gate.prose.md,sha256=t55UmBgQazvkEBj-w4krZ5PISYKLMVG_9wBg6mu2QgQ,1298
15
+ conformance/programs/functions/recheck-narrowest.prose.md,sha256=T2AKpo0t8a91i8RXKFd_1k5L8V242Vn7LEMo-MsDRdg,1895
16
+ conformance/programs/functions/remediate-finding.prose.md,sha256=MG_m_PImjSJLrbhvuQr6rQaki6X8FXoHq3EK5v61xrQ,5376
17
+ conformance/programs/patterns/detect-fix-recheck.prose.md,sha256=IM_dSjpY-9bHz9Fn0p2mA-lRiiGzrU5JIcBNK4opj4U,3795
18
+ conformance/suite/__init__.py,sha256=Xi1PgHR4r2UpvFwCaHXLbzumDWm8mv6sLp_thsDHutk,885
19
+ conformance/suite/runner.py,sha256=Pm7aC2jW-2WxrnK6nb0xFyDSgbIinx_NnukM5LKZ2hI,8502
20
+ conformance/suite/checks/__init__.py,sha256=yTctRyi-506ulQ-KXjYBuJ7Z_rl7alyaZm_07XF2rzk,69
21
+ conformance/suite/checks/actions_pinning.py,sha256=zeQbbX30AuU0eGQV0i2bsju7oKHgTWhFwQeOvZvwtAE,6233
22
+ conformance/suite/checks/error_handling/__init__.py,sha256=5bjyvqdWlG1BNpGpUKhtvueje_B62KHtqi7afWG4TkU,6628
23
+ conformance/suite/checks/error_handling/_checker.py,sha256=rWUAOWfleKZIcp1LS7hyYrspB4kc8hzYhDVNIyuUOKA,5884
24
+ conformance/suite/checks/error_handling/_collect.py,sha256=pwlG2cpOakvtQzZuAjDxyHuQKBW1wPj00ClWcOLmFrk,1420
25
+ conformance/suite/checks/error_handling/_constants.py,sha256=3rCAoaS2gT8DY0HUkgXtDr-xB9qYs5TJQdWb8rGF08I,3462
26
+ conformance/suite/checks/error_handling/_directives.py,sha256=Vam0noPsaxUZS6UBT6kN74-RaLMoTIAiBvwJfrYEMkQ,5452
27
+ conformance/suite/checks/error_handling/_helpers.py,sha256=crgvWd0HCg7eMnC3FfvHvuGLE88qFSltrIa_7gQDyAE,7101
28
+ conformance/suite/checks/error_handling/exception_chaining.py,sha256=GR56sA2vnk5Ke4u6Gv16dd6aouzhPZz79pxQdoNrWWU,2385
29
+ conformance/suite/checks/error_handling/security.py,sha256=HCLvFBo63W8N4K4qouc6TLDX5Buy1ismvMlQpD5waWc,2066
30
+ conformance/suite/checks/error_handling/silent_swallow.py,sha256=ywvst6fcKOuCbfBC8umH25WMgVG1Nm3AIcZSkZ093VU,13969
31
+ conformance/suite/checks/error_handling/untyped_raise.py,sha256=93mTp2MYMKwTmvEk7QOLxLQ5jF7HIBRyj006XgmQJdU,4611
32
+ conformance/suite/rules/__init__.py,sha256=BUpntBeRsAVXRC1fyHhrxjjZmVp1KNXOGkDj1vcaWYg,1582
33
+ conformance/suite/rules/ci.py,sha256=YoDDR2a26kGxZRYIeiE6yMza_DWxxu301Hp1EfJSxb8,1159
34
+ conformance/suite/rules/error_handling.py,sha256=dwCWFdRp4ZE5iKqU-WUgWzFzd4IziVzA4u49mkbr-G0,18091
35
+ conformance/suite/rules/logging.py,sha256=KjW4tM5P1uFdtbdq5XL8b9HONcENt31e_-bxNrjPgwM,15925
36
+ conformance/suite/schema/__init__.py,sha256=hlr9LDcKxxChzADqdET8EOYFXgklqE0ewjP6hvOqEmQ,2048
37
+ conformance/suite/schema/builder.py,sha256=DCGPa_3hy_C6JXCGiMg7A49lPAHvuYcdH4FC8PDyV78,11323
38
+ conformance/suite/schema/catalog.py,sha256=8Yj_96zE0U8ORYTjlq9sLN-7T8K5-0SwTRl79NEDX9Q,4046
39
+ conformance/suite/schema/disposition.py,sha256=YMlzNwnrWp4BaMUI6V4IiOogPoVdK-hWWeZLZLyA9C0,5770
40
+ conformance/suite/schema/extensions.py,sha256=dEXl2ZM-UDIxtMQF258duYx8iQrtJpPfIepEdi_l0dY,6547
41
+ conformance/suite/schema/findings.py,sha256=AJaN3S7I3MKPmJ8nGHK6qlpqXhOuPJJtH9Yg7g8wQjA,2330
42
+ conformance/suite/schema/sarif-schema-2.1.0.json,sha256=fJaI8KHEpOFknsx4UhCH5mRynB3_Vu6CEv8ZXHsWEyo,111720
43
+ conformance/suite/schema/sarif.py,sha256=gokZ_e-N2VK_rJRWAYdhjW75vPuLtyrJ9Sct8mFnrW4,11276
44
+ conformance/suite/schema/validate.py,sha256=GpiJG-mdeisIc6nOqMBef6xrvWqTtoiEmiZz32N56xw,2112
45
+ conformance/tools/generate_rule_docs.py,sha256=lJpAcjzQEQiCLkWU6KXHHiblzok0nHLWgTpk2vFxDuI,9717
46
+ atlan_application_sdk_conformance-0.2.0.dist-info/METADATA,sha256=Sd-evkrO9bxMShgdSgOlzNOAqgzQeaT-DFththjZuQw,2166
47
+ atlan_application_sdk_conformance-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
48
+ atlan_application_sdk_conformance-0.2.0.dist-info/entry_points.txt,sha256=qpEna_KV_1dF4C4Gdsv4ysCYT33OR8KuVS18PMBTJZ8,75
49
+ atlan_application_sdk_conformance-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ atlan-application-sdk-conformance = conformance.cli:main
@@ -0,0 +1,7 @@
1
+ """Atlan Application SDK Conformance Suite.
2
+
3
+ Dev-only package. Provides the conformance validators, remediation programs,
4
+ and CLI for the atlan-application-sdk ecosystem.
5
+ """
6
+
7
+ __version__ = "0.2.0"
conformance/cli.py ADDED
@@ -0,0 +1,126 @@
1
+ """Entry point for the atlan-application-sdk-conformance CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+
8
+ def _cmd_detect(argv: list[str]) -> int:
9
+ from conformance.suite.runner import main
10
+
11
+ return main(argv)
12
+
13
+
14
+ def _cmd_programs_dir(_argv: list[str]) -> int:
15
+ import importlib.resources as _ir
16
+ import pathlib
17
+
18
+ programs = _ir.files("conformance") / "programs"
19
+ # Resolve to a real filesystem path (works for both installed wheels and
20
+ # editable installs where the files are already on disk).
21
+ try:
22
+ ctx = _ir.as_file(programs)
23
+ with ctx as p:
24
+ print(str(p))
25
+ except (FileNotFoundError, ModuleNotFoundError):
26
+ # Fallback: direct path (editable installs)
27
+ here = pathlib.Path(__file__).parent
28
+ print(str(here / "programs"))
29
+ return 0
30
+
31
+
32
+ def _cmd_gen_rule_docs(argv: list[str]) -> int:
33
+ from conformance.tools.generate_rule_docs import main
34
+
35
+ try:
36
+ main(argv)
37
+ return 0
38
+ except SystemExit as e:
39
+ return int(e.code) if e.code is not None else 0
40
+
41
+
42
+ def _cmd_remediate(argv: list[str]) -> int:
43
+ """Print the resolved programs path + version, then exit.
44
+
45
+ The actual remediation loop is driven by the SKILL.md shim which reads
46
+ the .prose.md contracts from the printed programs directory.
47
+ """
48
+ import pathlib
49
+
50
+ from conformance import __version__
51
+
52
+ here = pathlib.Path(__file__).parent
53
+ programs = here / "programs"
54
+ print(f"atlan-application-sdk-conformance {__version__}")
55
+ print(f"programs: {programs}")
56
+ print(f"entry: {programs / 'conformance-remediation.prose.md'}")
57
+ return 0
58
+
59
+
60
+ # The SKILL.md written by `bootstrap`. Keep it minimal and stable — the real
61
+ # logic lives in the package; this shim just locates and invokes it.
62
+ _SKILL_MD = """\
63
+ ---
64
+ name: remediate
65
+ description: Drive the conformance remediation loop (validators + OpenProse programs from the atlan-application-sdk-conformance package)
66
+ argument-hint: "[--area error-handling|logging|ci] [--strict] [path]"
67
+ ---
68
+
69
+ 1. Resolve programs dir:
70
+ - Inside a connector repo: `PROGRAMS=$(uv run atlan-application-sdk-conformance programs-dir)`
71
+ - Anywhere else: `PROGRAMS=$(uvx atlan-application-sdk-conformance@latest programs-dir)`
72
+ 2. Read `$PROGRAMS/conformance-remediation.prose.md` and execute it as the entry contract.
73
+ 3. All gated re-checks call `atlan-application-sdk-conformance detect` — follow the .prose.md exactly.
74
+ """
75
+
76
+
77
+ def _cmd_bootstrap(argv: list[str]) -> int:
78
+ """Write .claude/skills/remediate/SKILL.md in the current repo (or --force to overwrite)."""
79
+ import pathlib
80
+
81
+ force = "--force" in argv
82
+ dest = pathlib.Path.cwd() / ".claude" / "skills" / "remediate" / "SKILL.md"
83
+
84
+ if dest.exists() and not force:
85
+ print(f"already installed: {dest} (pass --force to overwrite)")
86
+ return 0
87
+
88
+ existed = dest.exists()
89
+ dest.parent.mkdir(parents=True, exist_ok=True)
90
+ dest.write_text(_SKILL_MD)
91
+ action = "updated" if existed else "installed"
92
+ print(f"{action}: {dest}")
93
+ return 0
94
+
95
+
96
+ _COMMANDS = {
97
+ "detect": _cmd_detect,
98
+ "programs-dir": _cmd_programs_dir,
99
+ "gen-rule-docs": _cmd_gen_rule_docs,
100
+ "remediate": _cmd_remediate,
101
+ "bootstrap": _cmd_bootstrap,
102
+ }
103
+
104
+ _USAGE = """\
105
+ usage: atlan-application-sdk-conformance <command> [args]
106
+
107
+ commands:
108
+ detect Run the conformance suite and emit SARIF
109
+ programs-dir Print the absolute path to the bundled .prose.md programs
110
+ gen-rule-docs Regenerate rule docs from Python rule definitions
111
+ remediate Print programs path + version banner (SKILL.md drives execution)
112
+ bootstrap Write ~/.claude/skills/remediate/SKILL.md (--force to overwrite)
113
+ """
114
+
115
+
116
+ def main() -> None:
117
+ if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
118
+ print(_USAGE)
119
+ sys.exit(0)
120
+
121
+ cmd = sys.argv[1]
122
+ if cmd not in _COMMANDS:
123
+ print(f"error: unknown command '{cmd}'\n{_USAGE}", file=sys.stderr)
124
+ sys.exit(1)
125
+
126
+ sys.exit(_COMMANDS[cmd](sys.argv[2:]))
@@ -0,0 +1,33 @@
1
+ <!-- AUTO-GENERATED — do not edit this file directly.
2
+ Source of truth: conformance/suite/rules/ci.py
3
+ To regenerate: uv run poe generate-rule-docs
4
+ To check CI staleness: uv run poe generate-rule-docs --check -->
5
+
6
+ # CI/Workflow Supply-Chain Rules (C-series)
7
+
8
+ **1 rule** · Checker: `suite.checks.actions_pinning` and related workflow checks (static)
9
+
10
+ Suppress a finding on the violating line or the line directly above it:
11
+
12
+ ```python
13
+ # conformance: ignore[C001] intentional: org-internal action
14
+ ```
15
+
16
+ | ID | Name | Tier | Category | Autofixable | Since |
17
+ |---|---|---|---|---|---|
18
+ | [C001](#c001) | `UnpinnedActionReference` | `block` | `supply-chain` | yes | 3.16.0 |
19
+
20
+ ---
21
+
22
+ ## C001 — `UnpinnedActionReference` {#c001}
23
+
24
+ **Tier:** `block` · **Category:** `supply-chain` · **Autofixable:** yes · **Since:** 3.16.0
25
+
26
+ > External GitHub Action not pinned to a full commit digest
27
+
28
+ External actions reused via `uses:` must be pinned to a full-length commit SHA (digest),
29
+ never a mutable tag (@v4) or branch (@main). A tag can be re-pointed to malicious code
30
+ after review. Actions in the `atlanhq/` org are exempt (they intentionally track @main);
31
+ local `./` composite-action refs are exempt (no version to pin).
32
+
33
+ ---
@@ -0,0 +1,283 @@
1
+ <!-- AUTO-GENERATED — do not edit this file directly.
2
+ Source of truth: conformance/suite/rules/error_handling.py
3
+ To regenerate: uv run poe generate-rule-docs
4
+ To check CI staleness: uv run poe generate-rule-docs --check -->
5
+
6
+ # Error-Handling Rules (E-series)
7
+
8
+ **18 rules** · Checker: `suite.checks.error_handling` (AST-based)
9
+
10
+ Suppress a finding on the violating line or the line directly above it:
11
+
12
+ ```python
13
+ # conformance: ignore[E012] intentional: stdlib interop
14
+ ```
15
+
16
+ | ID | Name | Tier | Category | Autofixable | Since |
17
+ |---|---|---|---|---|---|
18
+ | [E001](#e001) | `BareExceptPass` | `block` | `silent-swallow` | — | 3.16.0 |
19
+ | [E002](#e002) | `TypedExceptPass` | `block` | `silent-swallow` | — | 3.16.0 |
20
+ | [E003](#e003) | `BroadContextlibSuppress` | `warn` | `silent-swallow` | — | 3.16.0 |
21
+ | [E004](#e004) | `BroadExceptClause` | `warn` | `overly-broad-catch` | — | 3.16.0 |
22
+ | [E005](#e005) | `ExceptBlockMissingExcInfo` | `warn` | `missing-traceback` | yes | 3.16.0 |
23
+ | [E006](#e006) | `BareExceptWithBody` | `block` | `silent-swallow` | — | 3.16.0 |
24
+ | [E007](#e007) | `ErrorToReturnValue` | `warn` | `error-to-return-value` | — | 3.16.0 |
25
+ | [E008](#e008) | `ImportErrorWithoutLogging` | `warn` | `optional-import` | — | 3.16.0 |
26
+ | [E009](#e009) | `ExceptBlockOnlyAssigns` | `warn` | `error-to-return-value` | — | 3.16.0 |
27
+ | [E010](#e010) | `AsyncioGatherExceptionsUnexamined` | `warn` | `asyncio-unexamined` | — | 3.16.0 |
28
+ | [E011](#e011) | `LoggingFilterUnsafeBody` | `warn` | `filter-safety` | — | 3.17.0 |
29
+ | [E012](#e012) | `UntypedBuiltinRaise` | `warn` | `untyped-raise` | — | 3.16.0 |
30
+ | [E013](#e013) | `LegacyAtlanErrorRaise` | `block` | `legacy-raise` | — | 3.16.0 |
31
+ | [E014](#e014) | `ExceptLoopControlSwallow` | `warn` | `silent-swallow` | — | 3.17.0 |
32
+ | [E015](#e015) | `ExceptionTextInErrorMessage` | `warn` | `error-message-hygiene` | — | 3.17.0 |
33
+ | [E016](#e016) | `MissingExceptionChaining` | `warn` | `exception-chaining` | yes | 3.17.0 |
34
+ | [E017](#e017) | `SecretNamedEvidenceKey` | `block` | `security` | — | 3.17.0 |
35
+ | [E018](#e018) | `BareParentLeafRaise` | `warn` | `untyped-raise` | — | 3.17.0 |
36
+
37
+ ---
38
+
39
+ ## E001 — `BareExceptPass` {#e001}
40
+
41
+ **Tier:** `block` · **Category:** `silent-swallow` · **Autofixable:** — · **Since:** 3.16.0
42
+
43
+ > Bare 'except: pass' silently discards every exception
44
+
45
+ A bare `except: pass` catches KeyboardInterrupt, SystemExit, and GeneratorExit and
46
+ discards them with no trace. This is the hardest class of bugs to debug. Replace with
47
+ a typed catch that at minimum logs the error with `exc_info=True`. Never acceptable —
48
+ even cleanup paths should log at DEBUG.
49
+
50
+ ---
51
+
52
+ ## E002 — `TypedExceptPass` {#e002}
53
+
54
+ **Tier:** `block` · **Category:** `silent-swallow` · **Autofixable:** — · **Since:** 3.16.0
55
+
56
+ > Typed 'except SomeError: pass' discards exception silently
57
+
58
+ A typed catch that still discards silently loses the stack trace entirely. Acceptable
59
+ only for truly trivial best-effort operations where failure is 100% expected AND the
60
+ surrounding code handles the missing result, AND there is a comment explaining the
61
+ reasoning.
62
+
63
+ ---
64
+
65
+ ## E003 — `BroadContextlibSuppress` {#e003}
66
+
67
+ **Tier:** `warn` · **Category:** `silent-swallow` · **Autofixable:** — · **Since:** 3.16.0
68
+
69
+ > contextlib.suppress() — check whether scope is too broad
70
+
71
+ `contextlib.suppress(Exception)` or `suppress(BaseException)` is HIGH severity; narrow
72
+ `suppress(FileNotFoundError)` on a cleanup path is acceptable. The checker must inspect
73
+ the suppressed exception type before classifying.
74
+
75
+ ---
76
+
77
+ ## E004 — `BroadExceptClause` {#e004}
78
+
79
+ **Tier:** `warn` · **Category:** `overly-broad-catch` · **Autofixable:** — · **Since:** 3.16.0
80
+
81
+ > Overly broad 'except Exception/BaseException' without exc_info
82
+
83
+ Catches everything but the specific type is unknown. HIGH severity when not logged;
84
+ MEDIUM when logged but missing `exc_info=True`. Acceptable only at top-level handlers
85
+ (worker loops, HTTP handlers) when properly logged with `exc_info=True`.
86
+
87
+ ---
88
+
89
+ ## E005 — `ExceptBlockMissingExcInfo` {#e005}
90
+
91
+ **Tier:** `warn` · **Category:** `missing-traceback` · **Autofixable:** yes · **Since:** 3.16.0
92
+
93
+ > except block logs without exc_info=True — stack trace discarded
94
+
95
+ The message is logged but the stack trace is lost. Add `exc_info=True` to every
96
+ `logger.warning()` / `logger.error()` call inside an except block. `logger.exception()`
97
+ is exempt (it implies `exc_info=True`).
98
+
99
+ ---
100
+
101
+ ## E006 — `BareExceptWithBody` {#e006}
102
+
103
+ **Tier:** `block` · **Category:** `silent-swallow` · **Autofixable:** — · **Since:** 3.16.0
104
+
105
+ > Bare 'except:' (no type) — catches SystemExit and KeyboardInterrupt
106
+
107
+ Like P001 but the block may have a body. Still catches KeyboardInterrupt and
108
+ SystemExit. Always specify at least `except Exception:`.
109
+
110
+ ---
111
+
112
+ ## E007 — `ErrorToReturnValue` {#e007}
113
+
114
+ **Tier:** `warn` · **Category:** `error-to-return-value` · **Autofixable:** — · **Since:** 3.16.0
115
+
116
+ > except block returns a value without logging — error hidden
117
+
118
+ Exception is converted to a return value (None, {}, [], False) with no trace. Callers
119
+ see a wrong result with no idea why. At minimum log before returning; prefer raising a
120
+ domain-specific exception instead.
121
+
122
+ ---
123
+
124
+ ## E008 — `ImportErrorWithoutLogging` {#e008}
125
+
126
+ **Tier:** `warn` · **Category:** `optional-import` · **Autofixable:** — · **Since:** 3.16.0
127
+
128
+ > except ImportError without logging — environment issues hidden
129
+
130
+ Optional-dependency guard. Acceptable when the import is genuinely optional AND the
131
+ fallback path is correct AND there is a comment. Log at DEBUG if the module is
132
+ preferred but not required. Flag if the module is expected to be present (will fail
133
+ later with a confusing AttributeError).
134
+
135
+ ---
136
+
137
+ ## E009 — `ExceptBlockOnlyAssigns` {#e009}
138
+
139
+ **Tier:** `warn` · **Category:** `error-to-return-value` · **Autofixable:** — · **Since:** 3.16.0
140
+
141
+ > except block only assigns a variable — error hidden with no log
142
+
143
+ Exception sets a flag or default value with no trace. Combines P007's error-hiding with
144
+ no logging. Add a `logger.warning(..., exc_info=True)` before the assignment.
145
+
146
+ ---
147
+
148
+ ## E010 — `AsyncioGatherExceptionsUnexamined` {#e010}
149
+
150
+ **Tier:** `warn` · **Category:** `asyncio-unexamined` · **Autofixable:** — · **Since:** 3.16.0
151
+
152
+ > asyncio.gather(return_exceptions=True) results not checked for exceptions
153
+
154
+ `return_exceptions=True` returns exception instances as values in the result list. If
155
+ the list is not subsequently inspected for `Exception` instances, errors vanish
156
+ silently. The pattern is only a bug when results are not checked;
157
+ `return_exceptions=True` itself is acceptable.
158
+
159
+ ---
160
+
161
+ ## E011 — `LoggingFilterUnsafeBody` {#e011}
162
+
163
+ **Tier:** `warn` · **Category:** `filter-safety` · **Autofixable:** — · **Since:** 3.17.0
164
+
165
+ > logging.Filter.filter() body not wrapped in try/except — can crash caller
166
+
167
+ `Logger.handle()` calls `self.filter(record)` with no surrounding try/except — unlike
168
+ handler errors, filter exceptions are NOT caught by `handleError()`. An unguarded
169
+ attribute access (`record.custom_field`) or any external call that can raise will
170
+ propagate directly to the code that called `logger.info()` (or similar), crashing the
171
+ caller. Wrap the entire `filter()` body in try/except with a safe fallback (return True
172
+ to pass-through, False to drop). Use `getattr(record, 'field', default)` for optional
173
+ record attributes. Never acceptable — filter methods must never let exceptions
174
+ propagate.
175
+
176
+ ---
177
+
178
+ ## E012 — `UntypedBuiltinRaise` {#e012}
179
+
180
+ **Tier:** `warn` · **Category:** `untyped-raise` · **Autofixable:** — · **Since:** 3.16.0
181
+
182
+ > raise ValueError/RuntimeError/... where a typed AppError applies
183
+
184
+ SDK code raises a bare Python builtin. The Automation Engine receives an opaque string
185
+ — no category, code, audience, or retryable field. Dashboards are blind; on-call routing
186
+ is impossible. Use the typed error leaf from `application_sdk.errors`. Acceptable only
187
+ inside dataclass `__post_init__` / stdlib validator methods where Python semantics
188
+ require `TypeError`/`ValueError` for stdlib interoperability.
189
+
190
+ ---
191
+
192
+ ## E013 — `LegacyAtlanErrorRaise` {#e013}
193
+
194
+ **Tier:** `block` · **Category:** `legacy-raise` · **Autofixable:** — · **Since:** 3.16.0
195
+
196
+ > raise ClientError/ApiError/... (deprecated AtlanError stack)
197
+
198
+ `AtlanError` and its subclasses emit a `DeprecationWarning` at construction time and
199
+ reach AE as opaque strings. They produce no typed wire envelope. Scheduled for removal
200
+ in v4.0. Replace with the appropriate leaf from `application_sdk.errors`.
201
+
202
+ ---
203
+
204
+ ## E014 — `ExceptLoopControlSwallow` {#e014}
205
+
206
+ **Tier:** `warn` · **Category:** `silent-swallow` · **Autofixable:** — · **Since:** 3.17.0
207
+
208
+ > except block exits loop silently (continue/break) without logging
209
+
210
+ An `except` block inside a loop whose body is only `continue`, `break`, or `pass` — with
211
+ no logging call — silently swallows the exception and resumes or exits the iteration.
212
+ This is the loop-body twin of P001/P002 (ruff S112 family). At minimum log at DEBUG
213
+ before the loop control statement; if the exception signals a genuine error, re-raise or
214
+ log at WARNING/ERROR with `exc_info=True`.
215
+
216
+ ---
217
+
218
+ ## E015 — `ExceptionTextInErrorMessage` {#e015}
219
+
220
+ **Tier:** `warn` · **Category:** `error-message-hygiene` · **Autofixable:** — · **Since:** 3.17.0
221
+
222
+ > Caught exception text interpolated into typed error message= — leaks unsanitised text
223
+
224
+ A typed `AppError` raise whose `message=` keyword value embeds the caught exception via
225
+ an f-string (`f'…{exc}…'`), `str(exc)`, or `repr(exc)` — see typed-error-prescription.md
226
+ §6. This leaks unsanitised, potentially user-facing text from an upstream library into
227
+ a field that is displayed to operators and indexed in dashboards. It also breaks
228
+ aggregation by collapsing distinct failure modes into one variable-text bucket. Place
229
+ the exception context in a typed evidence field (`cause=exc`, `network_error=str(exc)`)
230
+ and keep `message=` a stable human summary.
231
+
232
+ ---
233
+
234
+ ## E016 — `MissingExceptionChaining` {#e016}
235
+
236
+ **Tier:** `warn` · **Category:** `exception-chaining` · **Autofixable:** yes · **Since:** 3.17.0
237
+
238
+ > raise inside except block missing 'from exc' cause — breaks exception chain
239
+
240
+ A non-bare `raise` inside an `except … as e:` block that does not include `from e` (or
241
+ `from None`). Without explicit chaining, Python attaches the original as `__context__`
242
+ with `__suppress_context__=False` — the chain is preserved at the interpreter level but
243
+ AE's wire serialiser only follows the `__cause__` chain, so the original exception is
244
+ lost in dashboards. Fix: `raise NewError(...) from e`. Use `from None` only when
245
+ intentionally hiding the original (requires a comment explaining why). Acceptable
246
+ patterns: bare `raise` (re-raise), `raise X() from e` (already chained), `raise X() from
247
+ None` (intentional suppression).
248
+
249
+ ---
250
+
251
+ ## E017 — `SecretNamedEvidenceKey` {#e017}
252
+
253
+ **Tier:** `block` · **Category:** `security` · **Autofixable:** — · **Since:** 3.17.0
254
+
255
+ > Error evidence kwarg ending in _secret/_password/_token — rejected by wire layer at runtime
256
+
257
+ An error construction call that passes a keyword argument whose name ends in `_secret`,
258
+ `_password`, or `_token` — see `application_sdk.errors.wire` §6. The wire layer
259
+ actively rejects these suffixes at runtime (`ValueError`) to prevent credential leakage
260
+ into logs, dashboards, and SARIF reports. Static detection means the bug is caught
261
+ before any code runs. Rename the evidence field to a safe key (e.g. `credential_name`,
262
+ `token_type`) or pass the value via `cause=exc`.
263
+
264
+ ---
265
+
266
+ ## E018 — `BareParentLeafRaise` {#e018}
267
+
268
+ **Tier:** `warn` · **Category:** `untyped-raise` · **Autofixable:** — · **Since:** 3.17.0
269
+
270
+ > Raising a bare AppError leaf class without a domain subclass overriding code
271
+
272
+ Raising a parent leaf directly (`InternalError(...)`, `InvalidInputError(...)`) without
273
+ a domain-specific subclass that overrides `code` collapses all failure modes for a given
274
+ category into one dashboard bucket — impossible to route, alert on, or triage
275
+ individually. See typed-error-prescription.md §4. Only sanctioned bare-parent form:
276
+ `InternalError(classification_pending=True)` — used exclusively as a temporary
277
+ placeholder during migration, never in production-stable code. For all other sites:
278
+ define a domain subclass that overrides `code` with a specific constant (e.g.
279
+ `ENGINE_NOT_INITIALIZED`). Note: this rule is WARN tier because some bare-parent raises
280
+ may be legitimate in small apps or during active migration. Review findings before
281
+ suppressing.
282
+
283
+ ---