claude-dev-env 1.61.0 → 1.62.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/CLAUDE.md +8 -0
- package/bin/install.mjs +1 -1
- package/hooks/blocking/code_rules_dead_config_field.py +321 -0
- package/hooks/blocking/code_rules_enforcer.py +6 -0
- package/hooks/blocking/config/verified_commit_constants.py +1 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +432 -0
- package/hooks/blocking/test_verified_commit_gate.py +32 -0
- package/hooks/blocking/verified_commit_gate.py +11 -1
- package/hooks/hooks_constants/dead_config_field_constants.py +39 -0
- package/package.json +1 -1
- package/skills/autoconverge/reference/gotchas.md +11 -0
- package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +5 -0
- package/skills/autoconverge/workflow/test_render_report.py +25 -0
- package/skills/doc-gist/SKILL.md +3 -2
- package/skills/doc-gist/references/examples/21-decision-signoff.html +546 -0
- package/skills/doc-gist/references/examples/README.md +2 -2
- package/skills/task-build/SKILL.md +31 -0
package/CLAUDE.md
CHANGED
|
@@ -73,6 +73,14 @@ When asked to sync git ("get X onto origin main", "update main"), fast-forward l
|
|
|
73
73
|
|
|
74
74
|
For scheduled/cron tasks, default to sub-hour intervals (30-minute); do not propose hourly cadences.
|
|
75
75
|
|
|
76
|
+
## Task Tracking
|
|
77
|
+
|
|
78
|
+
Track every task with the task tool, always — for all sessions and all tasks. Capture each task with `TaskCreate` as it arrives, mark it `in_progress` with `TaskUpdate` when you start, and `completed` when it is done. Run `/task-build` to gather any open tasks and add them to the list in one pass.
|
|
79
|
+
|
|
80
|
+
## Working in the claude-code-config Repo
|
|
81
|
+
|
|
82
|
+
When changing how skills, rules, or hooks install or sync in this repo (for example adding a skill), read `docs/references/skill-install-system.md` — it maps the install pipeline in `packages/claude-dev-env/bin/install.mjs`.
|
|
83
|
+
|
|
76
84
|
## Additional Non-overlapping Rules
|
|
77
85
|
|
|
78
86
|
- **task_scope:** Match every action to what was explicitly requested. When intent is ambiguous, research official docs and present options via AskUserQuestion before making any changes. Proceed with edits only on explicit instruction.
|
package/bin/install.mjs
CHANGED
|
@@ -149,7 +149,7 @@ const INSTALL_GROUPS = {
|
|
|
149
149
|
skills: [
|
|
150
150
|
'anthropic-plan', 'everything-search',
|
|
151
151
|
'pr-review-responder',
|
|
152
|
-
'recall', 'remember'
|
|
152
|
+
'recall', 'remember', 'task-build'
|
|
153
153
|
],
|
|
154
154
|
includeDirectories: ['rules', 'docs', 'commands', 'agents', 'audit-rubrics'],
|
|
155
155
|
includeAllHooks: true,
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""Dead config-dataclass field check: cross-module scan for ``*Config`` @dataclass fields.
|
|
2
|
+
|
|
3
|
+
A config ``@dataclass`` (any class whose name ends in ``"Config"``) is defined
|
|
4
|
+
in one module but constructed and consumed in others, so the per-file dead-field
|
|
5
|
+
check in ``code_rules_dead_dataclass_field`` cannot judge its fields — it skips
|
|
6
|
+
any class that is not constructed in the same file. This check resolves the
|
|
7
|
+
enclosing package tree — the scan root — and flags a ``*Config`` dataclass field
|
|
8
|
+
whose name appears as an attribute read (``obj.field``) in no production module
|
|
9
|
+
anywhere under that root.
|
|
10
|
+
|
|
11
|
+
The scan is deliberately conservative to keep false positives near zero:
|
|
12
|
+
|
|
13
|
+
- Only ``@dataclass`` classes whose name ends in ``"Config"`` participate; other
|
|
14
|
+
dataclasses are covered by the per-file check.
|
|
15
|
+
- Test and migration files are exempt as write destinations, so a field added to
|
|
16
|
+
a config dataclass inside a test is never flagged.
|
|
17
|
+
- Production modules under the scan root are scanned for attribute reads; test
|
|
18
|
+
and migration modules are deliberately excluded so a field read only by test
|
|
19
|
+
code is still flagged as dead-in-production.
|
|
20
|
+
- Field reads are collected as ``ast.Attribute.attr`` values (``obj.field``),
|
|
21
|
+
augmented-assignment targets (``cfg.field += 1`` reads ``field`` before
|
|
22
|
+
writing it), string literals (covers ``getattr(obj, "field")``),
|
|
23
|
+
keyword-argument names (covers ``ThemeUpdateConfig(debug_port=1)`` and
|
|
24
|
+
``replace(cfg, debug_port=1)``), and match-pattern keyword attribute names
|
|
25
|
+
(``case Config(field=found)``). Plain ``ast.Name`` references are excluded — a
|
|
26
|
+
local variable named ``debug_port`` is not a read of ``config.debug_port``.
|
|
27
|
+
- A production module that reflectively reads a whole instance — a bare or
|
|
28
|
+
``dataclasses``-qualified call to ``asdict``, ``astuple``, ``fields``,
|
|
29
|
+
``replace``, or ``vars``, or a read of ``obj.__dict__`` — consumes every field
|
|
30
|
+
at once without naming any single field, so the check is suppressed for the
|
|
31
|
+
whole tree (returns ``[]``).
|
|
32
|
+
- A scan root whose total file count exceeds the configured cap cannot prove any
|
|
33
|
+
field dead, so the check returns ``[]`` on a cap hit.
|
|
34
|
+
- A field read only by a module outside the resolved scan root is treated as dead
|
|
35
|
+
— the same conservative scoping the dead-module-constant check accepts.
|
|
36
|
+
|
|
37
|
+
Unlike the per-file dead-dataclass-field check, this cross-module check does NOT
|
|
38
|
+
suppress on a dataclass-dunder whole-instance read — instance comparison
|
|
39
|
+
(``cfg == other``), set or dict membership, formatted-string conversion
|
|
40
|
+
(``f"{cfg}"``), or whole-instance stringification
|
|
41
|
+
(``str(cfg)``/``repr(cfg)``/``format(cfg)``). Those syntactic forms are not bound
|
|
42
|
+
to a config instance, and tree-wide one incidental match anywhere would disable
|
|
43
|
+
the check on any realistic package. The consequence is a documented, rare
|
|
44
|
+
limitation: a ``*Config`` field read ONLY via whole-instance dunder comparison or
|
|
45
|
+
stringification, and never read directly anywhere in production, may be flagged.
|
|
46
|
+
The augmented-assignment read mechanism (``cfg.field += 1`` reads ``field``
|
|
47
|
+
before writing it) is precise and remains a counted read.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import ast
|
|
51
|
+
import os
|
|
52
|
+
import sys
|
|
53
|
+
from pathlib import Path
|
|
54
|
+
|
|
55
|
+
_blocking_directory = str(Path(__file__).resolve().parent)
|
|
56
|
+
_hooks_directory = str(Path(__file__).resolve().parent.parent)
|
|
57
|
+
if _blocking_directory not in sys.path:
|
|
58
|
+
sys.path.insert(0, _blocking_directory)
|
|
59
|
+
if _hooks_directory not in sys.path:
|
|
60
|
+
sys.path.insert(0, _hooks_directory)
|
|
61
|
+
|
|
62
|
+
from code_rules_dead_dataclass_field import ( # noqa: E402
|
|
63
|
+
_augmented_assignment_attribute_names,
|
|
64
|
+
_dataclass_field_definitions,
|
|
65
|
+
_is_dataclass,
|
|
66
|
+
)
|
|
67
|
+
from code_rules_dead_module_constant import ( # noqa: E402
|
|
68
|
+
_scan_root_for_constants_module,
|
|
69
|
+
)
|
|
70
|
+
from code_rules_shared import ( # noqa: E402
|
|
71
|
+
is_migration_file,
|
|
72
|
+
is_test_file,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
from hooks_constants.dead_config_field_constants import ( # noqa: E402
|
|
76
|
+
ALL_REFLECTIVE_FIELD_CONSUMER_NAMES,
|
|
77
|
+
CONFIG_CLASS_NAME_SUFFIX,
|
|
78
|
+
DATACLASSES_MODULE_NAME,
|
|
79
|
+
DEAD_CONFIG_FIELD_GUIDANCE,
|
|
80
|
+
MAX_DEAD_CONFIG_FIELD_ISSUES,
|
|
81
|
+
MAX_SCAN_ROOT_FILE_COUNT,
|
|
82
|
+
PYTHON_SOURCE_SUFFIX,
|
|
83
|
+
WHOLE_INSTANCE_DICT_ATTRIBUTE_NAME,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _is_config_dataclass(class_node: ast.ClassDef) -> bool:
|
|
88
|
+
"""Return whether a class is a @dataclass whose name ends in ``"Config"``.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
class_node: The class definition node to test.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
True when the class carries a ``@dataclass`` decorator and its name ends
|
|
95
|
+
in ``"Config"``.
|
|
96
|
+
"""
|
|
97
|
+
return _is_dataclass(class_node) and class_node.name.endswith(CONFIG_CLASS_NAME_SUFFIX)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _reads_whole_instance_reflectively(tree: ast.Module) -> bool:
|
|
101
|
+
"""Return whether a module consumes a whole instance via a reflective read.
|
|
102
|
+
|
|
103
|
+
Detects a bare call to any reflective whole-instance consumer (``asdict``,
|
|
104
|
+
``astuple``, ``fields``, ``replace``, ``vars`` imported from ``dataclasses``),
|
|
105
|
+
a ``dataclasses``-qualified call to the same consumers
|
|
106
|
+
(``dataclasses.asdict(cfg)``, ``dataclasses.replace(cfg, ...)``), and a read
|
|
107
|
+
of the ``__dict__`` attribute. The method-call form must be ``dataclasses``-
|
|
108
|
+
qualified — an unrelated ``"text".replace(...)`` or ``frame.fields(...)`` on
|
|
109
|
+
another object does not match. Each matched form reads every field of an
|
|
110
|
+
instance at once without naming any single field, so a module that uses one
|
|
111
|
+
cannot prove a config field unread.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
tree: The parsed module to inspect.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
True when the module makes a bare or ``dataclasses``-qualified call to a
|
|
118
|
+
reflective whole-instance consumer, or reads ``obj.__dict__``.
|
|
119
|
+
"""
|
|
120
|
+
for each_node in ast.walk(tree):
|
|
121
|
+
if isinstance(each_node, ast.Attribute):
|
|
122
|
+
if each_node.attr == WHOLE_INSTANCE_DICT_ATTRIBUTE_NAME:
|
|
123
|
+
return True
|
|
124
|
+
continue
|
|
125
|
+
if not isinstance(each_node, ast.Call):
|
|
126
|
+
continue
|
|
127
|
+
function_node = each_node.func
|
|
128
|
+
if isinstance(function_node, ast.Name) and function_node.id in ALL_REFLECTIVE_FIELD_CONSUMER_NAMES:
|
|
129
|
+
return True
|
|
130
|
+
if (
|
|
131
|
+
isinstance(function_node, ast.Attribute)
|
|
132
|
+
and isinstance(function_node.value, ast.Name)
|
|
133
|
+
and function_node.value.id == DATACLASSES_MODULE_NAME
|
|
134
|
+
and function_node.attr in ALL_REFLECTIVE_FIELD_CONSUMER_NAMES
|
|
135
|
+
):
|
|
136
|
+
return True
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _attribute_read_names_in_source(source: str) -> tuple[set[str], bool]:
|
|
141
|
+
"""Return attribute names read in a module's source and a suppression flag.
|
|
142
|
+
|
|
143
|
+
Collects attribute names via five mechanisms: ``ast.Attribute.attr`` values
|
|
144
|
+
in Load context, augmented-assignment targets (so ``cfg.debug_port += 1``
|
|
145
|
+
contributes ``"debug_port"`` because ``+=`` reads the attribute before
|
|
146
|
+
writing it), string literals (so ``getattr(obj, "field")`` contributes
|
|
147
|
+
``"field"``), keyword-argument names (so ``ThemeUpdateConfig(debug_port=1)``
|
|
148
|
+
and ``replace(cfg, debug_port=1)`` each contribute ``"debug_port"``), and
|
|
149
|
+
``ast.MatchClass.kwd_attrs`` names (so ``case Config(field=x)`` contributes
|
|
150
|
+
``"field"``). The boolean reports whether the module suppresses the
|
|
151
|
+
dead-field check, which it does only when it reflectively reads a whole
|
|
152
|
+
instance — a bare or ``dataclasses``-qualified ``asdict``/``astuple``/
|
|
153
|
+
``fields``/``replace``/``vars`` call, or an ``obj.__dict__`` read — because
|
|
154
|
+
that pattern reads every field at once without naming any single field, so
|
|
155
|
+
the caller treats it as "cannot prove any field dead". A ``SyntaxError``
|
|
156
|
+
contributes no names and no suppression.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
source: The full text of a ``.py`` module.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
A (read_names, suppresses_dead_field_check) pair. The name set is every
|
|
163
|
+
attribute name the module reads via any of the five mechanisms above;
|
|
164
|
+
suppresses_dead_field_check is True only when a reflective whole-instance
|
|
165
|
+
read is present.
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
tree = ast.parse(source)
|
|
169
|
+
except SyntaxError:
|
|
170
|
+
return set(), False
|
|
171
|
+
all_read_names: set[str] = _augmented_assignment_attribute_names(tree)
|
|
172
|
+
for each_node in ast.walk(tree):
|
|
173
|
+
if isinstance(each_node, ast.Attribute) and isinstance(each_node.ctx, ast.Load):
|
|
174
|
+
all_read_names.add(each_node.attr)
|
|
175
|
+
elif isinstance(each_node, ast.Constant) and isinstance(each_node.value, str):
|
|
176
|
+
all_read_names.add(each_node.value)
|
|
177
|
+
elif isinstance(each_node, ast.MatchClass):
|
|
178
|
+
all_read_names.update(each_node.kwd_attrs)
|
|
179
|
+
elif isinstance(each_node, ast.keyword) and each_node.arg is not None:
|
|
180
|
+
all_read_names.add(each_node.arg)
|
|
181
|
+
suppresses_dead_field_check = _reads_whole_instance_reflectively(tree)
|
|
182
|
+
return all_read_names, suppresses_dead_field_check
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _all_production_read_names_under_root(
|
|
186
|
+
scan_root: Path,
|
|
187
|
+
written_path: Path,
|
|
188
|
+
written_content: str,
|
|
189
|
+
) -> tuple[set[str], bool, bool]:
|
|
190
|
+
"""Return read names, a cap-hit flag, and a suppression flag for the tree.
|
|
191
|
+
|
|
192
|
+
Scans every production ``.py`` module under ``scan_root`` (excluding test and
|
|
193
|
+
migration files) for attribute reads. The written module's post-edit content
|
|
194
|
+
replaces its on-disk text so the current edit is included. Scanning stops at
|
|
195
|
+
the configured file cap. A module that reflectively reads a whole instance —
|
|
196
|
+
a bare or ``dataclasses``-qualified ``asdict``/``astuple``/``fields``/
|
|
197
|
+
``replace``/``vars`` call, or an ``obj.__dict__`` read — sets the suppression
|
|
198
|
+
flag, signalling the caller that no field can be proven dead.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
scan_root: The directory tree to scan.
|
|
202
|
+
written_path: The resolved path of the module being written.
|
|
203
|
+
written_content: The post-edit text of the written module.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
A (read_names, cap_was_hit, suppresses_dead_field_check) triple. The name
|
|
207
|
+
set is the union of attribute reads across every scanned production
|
|
208
|
+
module; cap_was_hit is True when the scan stopped at the configured file
|
|
209
|
+
cap before finishing the tree; suppresses_dead_field_check is True when
|
|
210
|
+
any scanned module reflectively reads a whole instance.
|
|
211
|
+
"""
|
|
212
|
+
all_read_names, suppresses_dead_field_check = _attribute_read_names_in_source(written_content)
|
|
213
|
+
written_path_key = os.path.normcase(str(written_path))
|
|
214
|
+
scanned_file_count = 1
|
|
215
|
+
for each_path in scan_root.rglob("*" + PYTHON_SOURCE_SUFFIX):
|
|
216
|
+
if not each_path.is_file():
|
|
217
|
+
continue
|
|
218
|
+
if os.path.normcase(str(each_path.resolve())) == written_path_key:
|
|
219
|
+
continue
|
|
220
|
+
if is_test_file(str(each_path)):
|
|
221
|
+
continue
|
|
222
|
+
if is_migration_file(str(each_path)):
|
|
223
|
+
continue
|
|
224
|
+
scanned_file_count += 1
|
|
225
|
+
if scanned_file_count > MAX_SCAN_ROOT_FILE_COUNT:
|
|
226
|
+
return all_read_names, True, suppresses_dead_field_check
|
|
227
|
+
try:
|
|
228
|
+
sibling_source = each_path.read_text(encoding="utf-8")
|
|
229
|
+
except (OSError, UnicodeDecodeError):
|
|
230
|
+
continue
|
|
231
|
+
sibling_read_names, sibling_suppresses_dead_field_check = _attribute_read_names_in_source(
|
|
232
|
+
sibling_source
|
|
233
|
+
)
|
|
234
|
+
all_read_names |= sibling_read_names
|
|
235
|
+
suppresses_dead_field_check = (
|
|
236
|
+
suppresses_dead_field_check or sibling_suppresses_dead_field_check
|
|
237
|
+
)
|
|
238
|
+
return all_read_names, False, suppresses_dead_field_check
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def check_dead_config_dataclass_fields(
|
|
242
|
+
content: str, file_path: str, full_file_content: str | None = None
|
|
243
|
+
) -> list[str]:
|
|
244
|
+
"""Flag a ``*Config`` @dataclass field read by no production module in the package tree.
|
|
245
|
+
|
|
246
|
+
Runs a cross-module scan restricted to ``@dataclass`` classes whose name ends
|
|
247
|
+
in ``"Config"``. For each such config dataclass in the written file, every
|
|
248
|
+
instance field whose name does not appear as an attribute read (``obj.field``),
|
|
249
|
+
augmented-assignment target (``cfg.field += 1``), string literal,
|
|
250
|
+
keyword-argument name (constructor or ``replace`` keyword), or match-pattern
|
|
251
|
+
keyword attribute in any production module under the enclosing scan root is
|
|
252
|
+
flagged as dead. When any production module under the scan root reflectively
|
|
253
|
+
reads a whole instance — a bare or ``dataclasses``-qualified call to
|
|
254
|
+
``asdict``, ``astuple``, ``fields``, ``replace``, or ``vars``, or a read of
|
|
255
|
+
``obj.__dict__`` — the check is suppressed for the whole tree and returns
|
|
256
|
+
``[]``, since that pattern reads every field at once without naming any single
|
|
257
|
+
field. Test and
|
|
258
|
+
migration files are exempt as write destinations; production modules under the
|
|
259
|
+
scan root are scanned while test and migration modules in the tree are excluded
|
|
260
|
+
so fields read only by test code are still flagged as dead-in-production.
|
|
261
|
+
Whole-file analysis runs against ``full_file_content`` when supplied so an Edit
|
|
262
|
+
fragment is judged against the reconstructed post-edit file. A scan root
|
|
263
|
+
exceeding the file cap returns ``[]`` (cannot prove dead). The scan root is
|
|
264
|
+
resolved the same way as the dead-module-constant check: a ``config/`` module's
|
|
265
|
+
root is its parent directory, a module in a package directory's root is the
|
|
266
|
+
package's parent, and a top-level module's root is its enclosing directory.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
content: The new content under validation (Edit fragment or whole file).
|
|
270
|
+
file_path: The destination path, used for the test/migration exemptions
|
|
271
|
+
and scan-root resolution.
|
|
272
|
+
full_file_content: The reconstructed post-edit whole-file content for an
|
|
273
|
+
Edit, or None for a Write where ``content`` is already the whole file.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
One violation message per dead config dataclass field, capped at the
|
|
277
|
+
configured maximum. Returns an empty list when the file is exempt, no
|
|
278
|
+
qualifying config dataclass is found, the scan root exceeds the file cap,
|
|
279
|
+
or a SyntaxError prevents parsing.
|
|
280
|
+
"""
|
|
281
|
+
if is_test_file(file_path):
|
|
282
|
+
return []
|
|
283
|
+
if is_migration_file(file_path):
|
|
284
|
+
return []
|
|
285
|
+
effective_content = content if full_file_content is None else full_file_content
|
|
286
|
+
try:
|
|
287
|
+
tree = ast.parse(effective_content)
|
|
288
|
+
except SyntaxError:
|
|
289
|
+
return []
|
|
290
|
+
all_config_classes = [
|
|
291
|
+
each_node
|
|
292
|
+
for each_node in ast.walk(tree)
|
|
293
|
+
if isinstance(each_node, ast.ClassDef) and _is_config_dataclass(each_node)
|
|
294
|
+
]
|
|
295
|
+
if not all_config_classes:
|
|
296
|
+
return []
|
|
297
|
+
scan_root = _scan_root_for_constants_module(file_path)
|
|
298
|
+
written_path = Path(file_path).resolve()
|
|
299
|
+
all_read_names, cap_was_hit, suppresses_dead_field_check = (
|
|
300
|
+
_all_production_read_names_under_root(
|
|
301
|
+
scan_root,
|
|
302
|
+
written_path,
|
|
303
|
+
effective_content,
|
|
304
|
+
)
|
|
305
|
+
)
|
|
306
|
+
if cap_was_hit:
|
|
307
|
+
return []
|
|
308
|
+
if suppresses_dead_field_check:
|
|
309
|
+
return []
|
|
310
|
+
all_issues: list[str] = []
|
|
311
|
+
for each_class in all_config_classes:
|
|
312
|
+
for each_field_name, each_field_line in _dataclass_field_definitions(each_class):
|
|
313
|
+
if each_field_name in all_read_names:
|
|
314
|
+
continue
|
|
315
|
+
all_issues.append(
|
|
316
|
+
f"Line {each_field_line}: config dataclass field {each_field_name!r}"
|
|
317
|
+
f" on {each_class.name} - {DEAD_CONFIG_FIELD_GUIDANCE}"
|
|
318
|
+
)
|
|
319
|
+
if len(all_issues) >= MAX_DEAD_CONFIG_FIELD_ISSUES:
|
|
320
|
+
return all_issues
|
|
321
|
+
return all_issues
|
|
@@ -52,6 +52,9 @@ from code_rules_constants_config import ( # noqa: E402
|
|
|
52
52
|
check_constants_outside_config_advisory,
|
|
53
53
|
check_file_global_constants_use_count,
|
|
54
54
|
)
|
|
55
|
+
from code_rules_dead_config_field import ( # noqa: E402
|
|
56
|
+
check_dead_config_dataclass_fields,
|
|
57
|
+
)
|
|
55
58
|
from code_rules_dead_dataclass_field import ( # noqa: E402
|
|
56
59
|
check_dead_dataclass_fields,
|
|
57
60
|
)
|
|
@@ -279,6 +282,9 @@ def validate_content(
|
|
|
279
282
|
all_issues.extend(
|
|
280
283
|
check_dead_dataclass_fields(content, file_path, full_file_content)
|
|
281
284
|
)
|
|
285
|
+
all_issues.extend(
|
|
286
|
+
check_dead_config_dataclass_fields(content, file_path, full_file_content)
|
|
287
|
+
)
|
|
282
288
|
all_issues.extend(
|
|
283
289
|
check_dead_module_constants(content, file_path, full_file_content)
|
|
284
290
|
)
|
|
@@ -96,6 +96,7 @@ COMMAND_AFTER_DIRECTORY_CHANGE_PATTERN = r"[;&|\n][\s]*\S"
|
|
|
96
96
|
OPTION_WITH_VALUE_STEP = 2
|
|
97
97
|
ALL_GATED_TOOL_NAMES = ("Bash", "PowerShell")
|
|
98
98
|
HASH_PREVIEW_LENGTH = 16
|
|
99
|
+
VERIFICATION_BYPASS_MARKER = "# verify-skip"
|
|
99
100
|
MINTING_AGENT_TYPE = "code-verifier"
|
|
100
101
|
VERDICT_DIRECTORY_GUARD_MESSAGE = (
|
|
101
102
|
"BLOCKED: [VERDICT_DIRECTORY_GUARD] Shell access to the verification "
|