Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions corpus/skills/principle-explicit-errors/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ disable-model-invocation: true
# Make errors and failures explicit

Every failure path must make its disposition visible: fail fast, propagate,
translate into a named domain result, or suppress only a narrow, documented
case whose absence is safe and observable when that assumption changes.
translate into a named domain result, or log the exception with its context.
Never suppress an exception. Silence is not a policy.
Each rule below restates standard practice (see Grounding).

## Exceptions

An empty or comments-only handler is not an error policy. It hides failures
from callers, logs, tests, and maintainers. Do not write `catch {}`,
`except: pass`, ignored promise rejections, or equivalent silent fallbacks.
Replace them with an explicit action and keep the original error context.
An empty handler, a comment-only handler, and a handler whose only action
is to discard the error (`catch {}`, `void err`, `except: pass`,
`.catch(() => {})`) hide the failure from callers, logs, tests, and
maintainers. Do not write them.

If the exception must not propagate — a diagnostic callback, a metric
write, a best-effort side path — log it, then continue. The log names the
operation and includes the original exception. A comment that says why you
caught it is not a log. `void err` is not a log.

Mechanical enforcement: `engine/hooks/explicit-failures` (advisory PreToolUse hook, on by default; its README lists the shapes it catches).

Expand Down Expand Up @@ -85,9 +90,12 @@ The repo context is a batch data pipeline (filings in, CSV grids out), not a
long-running service; each line says how the source fits that.

- Silent handlers → Tim Peters, PEP 20 "The Zen of Python" (2004): "Errors
should never pass silently. Unless explicitly silenced."
<https://peps.python.org/pep-0020/>; Joshua Bloch, *Effective Java* 3rd ed.
(2018), Item 77 "Don't ignore exceptions". A batch job that swallows an
should never pass silently." <https://peps.python.org/pep-0020/>. This
skill does not take the next sentence ("Unless explicitly silenced") as
permission to drop an exception. Joshua Bloch, *Effective Java* 3rd ed.
(2018), Item 77 "Don't ignore exceptions". When the exception must not
propagate, log it with the original exception attached. A comment, an
empty catch, or `void err` is not that log. A batch job that swallows an
error ships a wrong CSV under a green run, so this applies as written.
- Fail immediately and visibly → Jim Shore, "Fail Fast", *IEEE Software*
21(5) (2004) <https://martinfowler.com/ieeeSoftware/failFast.pdf>. Shore's
Expand Down
10 changes: 7 additions & 3 deletions corpus/skills/principle-explicit-errors/tests/fires_example.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
An agent is adding a fallback around an optional dependency and proposes
`try { loadOptionalModule() } catch {}`. The agent invokes
`/principle-explicit-errors`, names the expected import failure, and replaces
the silent handler with an explicit fallback plus a test for unexpected errors.
`try { loadOptionalModule() } catch {}`. A review bot then "fixes" a
diagnostic callback by replacing `void reportingFailure` with an empty
`catch {}` so the callback cannot change the caller's outcome. The agent
invokes `/principle-explicit-errors`: neither shape is allowed. If the
exception must not propagate, the handler logs the operation and the
original exception, and a test asserts that log. An empty catch, a
comment-only catch, and `void err` are all suppression.

A second shape, same skill, no exception in sight. A realized-gains tab
passes "every value traces to a source", then a review pass finds a sell
Expand Down
28 changes: 28 additions & 0 deletions corpus/skills/principle-explicit-errors/tests/test_void_discard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""The skill's discard rule is the same check the failure hook runs."""
from __future__ import annotations

import os
import sys
import unittest

HOOK_DIR = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "engine", "hooks", "explicit-failures")
)
sys.path.insert(0, HOOK_DIR)

import detect # noqa: E402


class VoidDiscardTest(unittest.TestCase):
def test_void_only_catch_is_reported(self):
text = "try { a() } catch (err) {\n void err;\n}\n"
hits = detect.scan_js(text)
self.assertEqual(hits, [(1, "catch block that only discards the error with `void`")])

def test_logged_catch_is_quiet(self):
text = "try { a() } catch (err) {\n console.error('failed', err);\n}\n"
self.assertEqual(detect.scan_js(text), [])


if __name__ == "__main__":
unittest.main()
Loading