claude-dev-env 2.35.0 → 4.0.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.
Files changed (99) hide show
  1. package/.agents/skills/_shared/advisor/scripts/README.md +1 -1
  2. package/.agents/skills/autoconverge/SKILL.md +19 -15
  3. package/.agents/skills/autoconverge/reference/gotchas.md +3 -3
  4. package/.agents/skills/autoconverge/reference/multi-pr.md +6 -2
  5. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +11 -0
  6. package/.agents/skills/autoconverge/workflow/converge.mjs +26 -33
  7. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +26 -2
  8. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +12 -28
  9. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +16 -0
  10. package/.agents/skills/team-advisor/SKILL.md +11 -11
  11. package/_shared/advisor/advisor-protocol.md +51 -51
  12. package/_shared/advisor/reference/advisor-block.md +9 -9
  13. package/_shared/advisor/reference/astra-rung.md +39 -0
  14. package/_shared/advisor/reference/identity.md +4 -4
  15. package/_shared/advisor/reference/lifecycle.md +6 -6
  16. package/_shared/advisor/reference/spawn-walk-log.md +9 -9
  17. package/_shared/advisor/reference/third-party-bind.md +11 -11
  18. package/_shared/advisor/reference/warm-up.md +13 -13
  19. package/_shared/advisor/scripts/codex_astra_advisor.py +308 -0
  20. package/_shared/advisor/scripts/codex_astra_preflight.py +115 -0
  21. package/_shared/advisor/scripts/codex_astra_reply.py +159 -0
  22. package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +3 -3
  23. package/_shared/advisor/scripts/config/advisor_scripts_constants/astra_advisor_constants.py +38 -0
  24. package/_shared/advisor/scripts/model_tier_run_validator.py +17 -18
  25. package/_shared/advisor/scripts/tests/test_codex_astra_advisor.py +271 -0
  26. package/_shared/advisor/scripts/tests/test_codex_astra_preflight.py +24 -0
  27. package/_shared/advisor/scripts/tests/test_codex_astra_reply.py +42 -0
  28. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +55 -30
  29. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +14 -4
  30. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +14 -0
  31. package/_shared/pr-loop/scripts/terminology_sweep.py +138 -50
  32. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +13 -1
  33. package/bin/cde.mjs +175 -0
  34. package/bin/cde.test.mjs +222 -0
  35. package/bin/install-constants.mjs +4 -0
  36. package/bin/install.cursor-rules.test.mjs +233 -2
  37. package/bin/install.mjs +49 -3
  38. package/bin/pstack-model-preferences.codex.json +23 -0
  39. package/bin/select-pstack-models.test.mjs +378 -0
  40. package/docs/references/team-advisor-skill.md +3 -3
  41. package/hooks/hooks_constants/tool_names.py +20 -0
  42. package/hooks/validators/README.md +35 -1
  43. package/hooks/validators/fast_save_validators.py +63 -89
  44. package/hooks/validators/run_all_validators.py +16 -8
  45. package/hooks/validators/test_fast_save_validators_policy_lint.py +25 -0
  46. package/hooks/validators/test_run_all_validators_policy_lint.py +52 -0
  47. package/package.json +3 -2
  48. package/rules/pstack-models.md +119 -0
  49. package/scripts/cde_lint.py +258 -0
  50. package/scripts/check.ps1 +18 -6
  51. package/scripts/policy_lint/__init__.py +41 -0
  52. package/scripts/policy_lint/adapter_configuration.py +91 -0
  53. package/scripts/policy_lint/adapter_detectors.py +368 -0
  54. package/scripts/policy_lint/adapter_pairing.py +145 -0
  55. package/scripts/policy_lint/adapter_support.py +97 -0
  56. package/scripts/policy_lint/adapters.py +376 -0
  57. package/scripts/policy_lint/config/__init__.py +1 -0
  58. package/scripts/policy_lint/config/constants.py +77 -0
  59. package/scripts/policy_lint/engine.py +224 -0
  60. package/scripts/policy_lint/model.py +388 -0
  61. package/scripts/policy_lint/registry.py +171 -0
  62. package/scripts/policy_lint/render.py +91 -0
  63. package/scripts/policy_lint/selection.py +372 -0
  64. package/scripts/policy_lint/selection_git.py +147 -0
  65. package/scripts/repository_checks/__init__.py +1 -0
  66. package/scripts/repository_checks/claude_md.py +108 -0
  67. package/scripts/repository_checks/config/__init__.py +1 -0
  68. package/scripts/repository_checks/config/constants.py +150 -0
  69. package/scripts/repository_checks/env_var_documentation.py +243 -0
  70. package/scripts/repository_checks/hook_modules.py +34 -0
  71. package/scripts/repository_checks/models.py +37 -0
  72. package/scripts/repository_checks/package_inventory.py +118 -0
  73. package/scripts/repository_checks/pytest_testpaths.py +84 -0
  74. package/scripts/repository_checks/reporting.py +39 -0
  75. package/scripts/repository_checks/runner.py +93 -0
  76. package/scripts/repository_checks/tracked_secrets.py +147 -0
  77. package/scripts/repository_checks/tracked_tree.py +28 -0
  78. package/scripts/repository_policy.py +120 -0
  79. package/scripts/select_pstack_models.mjs +304 -0
  80. package/scripts/tests/repository_policy_test_support.py +89 -0
  81. package/scripts/tests/test_cde_lint.py +398 -0
  82. package/scripts/tests/test_claude_md.py +68 -0
  83. package/scripts/tests/test_env_var_documentation.py +133 -0
  84. package/scripts/tests/test_hook_modules.py +17 -0
  85. package/scripts/tests/test_models.py +26 -0
  86. package/scripts/tests/test_package_inventory.py +96 -0
  87. package/scripts/tests/test_policy_lint_rules_engine.py +381 -0
  88. package/scripts/tests/test_policy_lint_rules_registry.py +369 -0
  89. package/scripts/tests/test_policy_lint_selection.py +373 -0
  90. package/scripts/tests/test_pytest_testpaths.py +66 -0
  91. package/scripts/tests/test_reporting.py +54 -0
  92. package/scripts/tests/test_repository_policy.py +71 -0
  93. package/scripts/tests/test_runner.py +59 -0
  94. package/scripts/tests/test_tracked_secrets.py +175 -0
  95. package/scripts/tests/test_tracked_tree.py +28 -0
  96. package/_shared/advisor/reference/sol-rung.md +0 -39
  97. package/_shared/advisor/scripts/codex_sol_advisor.py +0 -581
  98. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +0 -38
  99. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +0 -740
@@ -0,0 +1,378 @@
1
+ import { test } from 'node:test';
2
+ import { strict as assert } from 'node:assert';
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import {
7
+ allPstackRoleRequirements,
8
+ selectPstackDelegation,
9
+ } from '../scripts/select_pstack_models.mjs';
10
+
11
+ const ALL_PSTACK_ROLES = [
12
+ 'feature, refactoring',
13
+ 'bug-fix',
14
+ 'perf-issue',
15
+ 'hillclimb',
16
+ 'judgment and prose',
17
+ 'hardest tasks',
18
+ 'how explorer',
19
+ 'how explainer',
20
+ 'how critics',
21
+ 'why investigators',
22
+ 'why synthesizer',
23
+ 'reflect tooling',
24
+ 'reflect judgment, divergent, synthesizer',
25
+ 'arena runners',
26
+ 'arena cross-judge pool',
27
+ 'swarm workers',
28
+ 'architect runners',
29
+ 'interrogate reviewers',
30
+ ];
31
+
32
+ function writeHostPreferences(preferencesDirectory, host, modelsByRole) {
33
+ mkdirSync(preferencesDirectory, { recursive: true });
34
+ writeFileSync(
35
+ join(preferencesDirectory, 'pstack-model-preferences.' + host + '.json'),
36
+ JSON.stringify({ host, modelsByRole }),
37
+ );
38
+ }
39
+
40
+ function selectWithPreferences(preferencesDirectory, overrides) {
41
+ return selectPstackDelegation({
42
+ host: 'codex',
43
+ inventoryHost: 'codex',
44
+ role: 'feature, refactoring',
45
+ delegationIndex: 0,
46
+ availableModelIds: ['gpt-5.6-sol'],
47
+ confirmedSuitableModelIds: [],
48
+ parentFallback: {
49
+ isAllowed: false,
50
+ hasMaterialCapabilityLoss: false,
51
+ },
52
+ preferencesDirectory,
53
+ ...overrides,
54
+ });
55
+ }
56
+
57
+ test('portable policy represents every pstack role without model ids', () => {
58
+ assert.deepEqual(Object.keys(allPstackRoleRequirements), ALL_PSTACK_ROLES);
59
+ const serializedRequirements = JSON.stringify(allPstackRoleRequirements);
60
+ assert.doesNotMatch(serializedRequirements, /gpt-|claude-|grok-/i);
61
+ });
62
+
63
+ test('Codex preferences select every available model in a diverse panel', () => {
64
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-codex-'));
65
+ try {
66
+ writeHostPreferences(preferencesDirectory, 'codex', {
67
+ 'how critics': ['gpt-5.6-terra', 'gpt-5.6-sol', 'gpt-6-astra'],
68
+ });
69
+ const selection = selectWithPreferences(preferencesDirectory, {
70
+ role: 'how critics',
71
+ delegationIndex: 1,
72
+ availableModelIds: ['gpt-5.6-sol', 'gpt-6-astra', 'gpt-5.6-terra'],
73
+ });
74
+ assert.equal(selection.canDelegate, true);
75
+ assert.equal(selection.selectedHost, 'codex');
76
+ assert.equal(selection.selectionSource, 'host-preference');
77
+ assert.deepEqual(selection.panel.selectedModelIds, [
78
+ 'gpt-5.6-terra',
79
+ 'gpt-5.6-sol',
80
+ 'gpt-6-astra',
81
+ ]);
82
+ assert.equal(selection.panel.isCrossModelDiverse, true);
83
+ assert.deepEqual(selection.nativeSpawnArguments, { model: 'gpt-5.6-sol' });
84
+ assert.equal(selection.omitNativeModelArgument, false);
85
+ assert.equal(selection.availabilityValidated, true);
86
+ } finally {
87
+ rmSync(preferencesDirectory, { recursive: true, force: true });
88
+ }
89
+ });
90
+
91
+ test('a foreign host reads only its own saved preferences', () => {
92
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-host-'));
93
+ try {
94
+ writeHostPreferences(preferencesDirectory, 'codex', {
95
+ 'feature, refactoring': ['gpt-5.6-sol'],
96
+ });
97
+ writeHostPreferences(preferencesDirectory, 'claude', {
98
+ 'feature, refactoring': ['confirmed-claude-model'],
99
+ });
100
+ const selection = selectWithPreferences(preferencesDirectory, {
101
+ host: 'claude',
102
+ inventoryHost: 'claude',
103
+ availableModelIds: ['confirmed-claude-model', 'gpt-5.6-sol'],
104
+ });
105
+ assert.equal(selection.selectedHost, 'claude');
106
+ assert.deepEqual(selection.nativeSpawnArguments, {
107
+ model: 'confirmed-claude-model',
108
+ });
109
+ } finally {
110
+ rmSync(preferencesDirectory, { recursive: true, force: true });
111
+ }
112
+ });
113
+
114
+ test('an unavailable preferred model uses a confirmed host alternative', () => {
115
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-alternative-'));
116
+ try {
117
+ writeHostPreferences(preferencesDirectory, 'codex', {
118
+ 'feature, refactoring': ['gpt-5.6-sol'],
119
+ });
120
+ const selection = selectWithPreferences(preferencesDirectory, {
121
+ availableModelIds: ['confirmed-current-model'],
122
+ confirmedSuitableModelIds: ['confirmed-current-model'],
123
+ });
124
+ assert.equal(selection.canDelegate, true);
125
+ assert.equal(selection.selectionSource, 'confirmed-host-alternative');
126
+ assert.deepEqual(selection.nativeSpawnArguments, {
127
+ model: 'confirmed-current-model',
128
+ });
129
+ } finally {
130
+ rmSync(preferencesDirectory, { recursive: true, force: true });
131
+ }
132
+ });
133
+
134
+ test('the arena cross-judge pool selects one model other than the parent', () => {
135
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-judge-'));
136
+ try {
137
+ writeHostPreferences(preferencesDirectory, 'codex', {
138
+ 'arena cross-judge pool': ['gpt-5.6-sol', 'gpt-6-astra'],
139
+ });
140
+ const selection = selectWithPreferences(preferencesDirectory, {
141
+ role: 'arena cross-judge pool',
142
+ parentModelId: 'gpt-5.6-sol',
143
+ availableModelIds: ['gpt-5.6-sol', 'gpt-6-astra'],
144
+ });
145
+ assert.equal(selection.panel.agentCount, 1);
146
+ assert.deepEqual(selection.nativeSpawnArguments, { model: 'gpt-6-astra' });
147
+ } finally {
148
+ rmSync(preferencesDirectory, { recursive: true, force: true });
149
+ }
150
+ });
151
+ test('the arena cross-judge prefers a real alternative over parent inheritance', () => {
152
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-judge-parent-'));
153
+ try {
154
+ writeHostPreferences(preferencesDirectory, 'codex', {
155
+ 'arena cross-judge pool': ['auto', 'confirmed-other-model'],
156
+ });
157
+ const selection = selectWithPreferences(preferencesDirectory, {
158
+ role: 'arena cross-judge pool',
159
+ parentModelId: 'current-parent-model',
160
+ availableModelIds: ['confirmed-other-model'],
161
+ parentFallback: {
162
+ isAllowed: true,
163
+ hasMaterialCapabilityLoss: false,
164
+ },
165
+ });
166
+ assert.deepEqual(selection.nativeSpawnArguments, {
167
+ model: 'confirmed-other-model',
168
+ });
169
+ assert.equal(selection.omitNativeModelArgument, false);
170
+ } finally {
171
+ rmSync(preferencesDirectory, { recursive: true, force: true });
172
+ }
173
+ });
174
+
175
+ test('a same-model swarm reports its actual panel composition', () => {
176
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-swarm-'));
177
+ try {
178
+ writeHostPreferences(preferencesDirectory, 'codex', {
179
+ 'swarm workers': ['gpt-5.6-luna'],
180
+ });
181
+ const selection = selectWithPreferences(preferencesDirectory, {
182
+ role: 'swarm workers',
183
+ delegationIndex: 2,
184
+ panel: {
185
+ agentCount: 4,
186
+ requiresDistinctModels: false,
187
+ },
188
+ availableModelIds: ['gpt-5.6-luna'],
189
+ });
190
+ assert.equal(selection.canDelegate, true);
191
+ assert.equal(selection.panel.agentCount, 4);
192
+ assert.deepEqual(selection.panel.selectedModelIds, [
193
+ 'gpt-5.6-luna',
194
+ 'gpt-5.6-luna',
195
+ 'gpt-5.6-luna',
196
+ 'gpt-5.6-luna',
197
+ ]);
198
+ assert.equal(selection.panel.isCrossModelDiverse, false);
199
+ assert.deepEqual(selection.nativeSpawnArguments, { model: 'gpt-5.6-luna' });
200
+ } finally {
201
+ rmSync(preferencesDirectory, { recursive: true, force: true });
202
+ }
203
+ });
204
+
205
+ test('a diversity-required panel fails without enough distinct real models', () => {
206
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-diversity-'));
207
+ try {
208
+ writeHostPreferences(preferencesDirectory, 'codex', {
209
+ 'interrogate reviewers': ['gpt-5.6-terra', 'auto', 'gpt-5.6-sol'],
210
+ });
211
+ const selection = selectWithPreferences(preferencesDirectory, {
212
+ role: 'interrogate reviewers',
213
+ availableModelIds: ['gpt-5.6-terra', 'gpt-5.6-sol'],
214
+ });
215
+ assert.equal(selection.canDelegate, false);
216
+ assert.equal(selection.requiresUserChoice, true);
217
+ assert.equal(selection.failure, 'distinct-models-unavailable');
218
+ assert.equal(selection.panel.isCrossModelDiverse, false);
219
+ assert.deepEqual(selection.nativeSpawnArguments, {});
220
+ } finally {
221
+ rmSync(preferencesDirectory, { recursive: true, force: true });
222
+ }
223
+ });
224
+
225
+ test('parent inheritance omits the native model argument', () => {
226
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-parent-'));
227
+ try {
228
+ writeHostPreferences(preferencesDirectory, 'codex', {
229
+ 'feature, refactoring': ['inherit-parent'],
230
+ });
231
+ const selection = selectWithPreferences(preferencesDirectory, {
232
+ availableModelIds: [],
233
+ parentFallback: {
234
+ isAllowed: true,
235
+ hasMaterialCapabilityLoss: false,
236
+ },
237
+ });
238
+ assert.equal(selection.canDelegate, true);
239
+ assert.equal(selection.selectionSource, 'parent-inheritance');
240
+ assert.deepEqual(selection.nativeSpawnArguments, {});
241
+ assert.equal(selection.omitNativeModelArgument, true);
242
+ } finally {
243
+ rmSync(preferencesDirectory, { recursive: true, force: true });
244
+ }
245
+ });
246
+
247
+ test('material capability loss requires a user choice before inheritance', () => {
248
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-parent-loss-'));
249
+ try {
250
+ writeHostPreferences(preferencesDirectory, 'codex', {
251
+ 'hardest tasks': ['inherit-parent'],
252
+ });
253
+ const selection = selectWithPreferences(preferencesDirectory, {
254
+ role: 'hardest tasks',
255
+ availableModelIds: [],
256
+ parentFallback: {
257
+ isAllowed: true,
258
+ hasMaterialCapabilityLoss: true,
259
+ },
260
+ });
261
+ assert.equal(selection.canDelegate, false);
262
+ assert.equal(selection.requiresUserChoice, true);
263
+ assert.equal(selection.failure, 'parent-capability-loss');
264
+ assert.deepEqual(selection.nativeSpawnArguments, {});
265
+ } finally {
266
+ rmSync(preferencesDirectory, { recursive: true, force: true });
267
+ }
268
+ });
269
+
270
+ test('inventory and confirmed alternatives must belong to the selected host', () => {
271
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-inventory-'));
272
+ try {
273
+ assert.throws(
274
+ () => selectWithPreferences(preferencesDirectory, {
275
+ host: 'codex',
276
+ inventoryHost: 'claude',
277
+ }),
278
+ /inventory host must match selected host/,
279
+ );
280
+ assert.throws(
281
+ () => selectWithPreferences(preferencesDirectory, {
282
+ availableModelIds: ['confirmed-current-model'],
283
+ confirmedSuitableModelIds: ['missing-model'],
284
+ }),
285
+ /confirmed suitable models must be available/,
286
+ );
287
+ } finally {
288
+ rmSync(preferencesDirectory, { recursive: true, force: true });
289
+ }
290
+ });
291
+
292
+ test('an empty model set materializes allowed parent inheritance', () => {
293
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-empty-parent-'));
294
+ try {
295
+ const selection = selectWithPreferences(preferencesDirectory, {
296
+ availableModelIds: [],
297
+ parentFallback: {
298
+ isAllowed: true,
299
+ hasMaterialCapabilityLoss: false,
300
+ },
301
+ });
302
+ assert.equal(selection.canDelegate, true);
303
+ assert.equal(selection.selectionSource, 'parent-inheritance');
304
+ assert.deepEqual(selection.panel.selectedModelIds, [null]);
305
+ assert.equal(selection.omitNativeModelArgument, true);
306
+ assert.deepEqual(selection.nativeSpawnArguments, {});
307
+ } finally {
308
+ rmSync(preferencesDirectory, { recursive: true, force: true });
309
+ }
310
+ });
311
+
312
+ test('delegation index and fallback flags require exact boundary types', () => {
313
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-boundary-'));
314
+ try {
315
+ for (const invalidIndex of [undefined, Number.NaN, 0.5, -1]) {
316
+ assert.throws(
317
+ () => selectWithPreferences(preferencesDirectory, {
318
+ delegationIndex: invalidIndex,
319
+ }),
320
+ /delegation index must be a non-negative integer/,
321
+ );
322
+ }
323
+ for (const invalidRole of ['toString', ['feature, refactoring']]) {
324
+ assert.throws(
325
+ () => selectWithPreferences(preferencesDirectory, {
326
+ role: invalidRole,
327
+ }),
328
+ /role must name a portable pstack role/,
329
+ );
330
+ }
331
+ assert.throws(
332
+ () => selectWithPreferences(preferencesDirectory, {
333
+ parentFallback: {
334
+ isAllowed: 'yes',
335
+ hasMaterialCapabilityLoss: false,
336
+ },
337
+ }),
338
+ /parent fallback flags must be booleans/,
339
+ );
340
+ assert.throws(
341
+ () => selectWithPreferences(preferencesDirectory, {
342
+ parentFallback: {
343
+ isAllowed: true,
344
+ },
345
+ }),
346
+ /parent fallback flags must be booleans/,
347
+ );
348
+ } finally {
349
+ rmSync(preferencesDirectory, { recursive: true, force: true });
350
+ }
351
+ });
352
+
353
+ test('an optional-diversity review panel reports repeated models', () => {
354
+ const preferencesDirectory = mkdtempSync(join(tmpdir(), 'pstack-review-panel-'));
355
+ try {
356
+ writeHostPreferences(preferencesDirectory, 'codex', {
357
+ 'how critics': ['gpt-5.6-sol'],
358
+ });
359
+ const selection = selectWithPreferences(preferencesDirectory, {
360
+ role: 'how critics',
361
+ delegationIndex: 2,
362
+ panel: {
363
+ agentCount: 3,
364
+ requiresDistinctModels: false,
365
+ },
366
+ availableModelIds: ['gpt-5.6-sol'],
367
+ });
368
+ assert.equal(selection.canDelegate, true);
369
+ assert.deepEqual(selection.panel.selectedModelIds, [
370
+ 'gpt-5.6-sol',
371
+ 'gpt-5.6-sol',
372
+ 'gpt-5.6-sol',
373
+ ]);
374
+ assert.equal(selection.panel.isCrossModelDiverse, false);
375
+ } finally {
376
+ rmSync(preferencesDirectory, { recursive: true, force: true });
377
+ }
378
+ });
@@ -1,6 +1,6 @@
1
- # Team-Advisor Skill
1
+ # Team-advisor skill
2
2
 
3
- `/team-advisor` binds one standing warm advisor for this session at the strongest reachable tier. This skill is the selected custom reproduction path for advisor behavior: it forwards explicit packets to a warm Agent/SendMessage advisor or the read-only `codex_sol_advisor.py` path across many decision points.
3
+ `/team-advisor` binds one standing warm advisor for this session at the strongest reachable tier. It sends explicit packets to a warm Agent/SendMessage advisor or the read-only `codex_astra_advisor.py` helper.
4
4
 
5
5
  ## Refs
6
6
 
@@ -17,4 +17,4 @@ Follow the call rules in `advisor-tool.md` (orientation first, then consult befo
17
17
 
18
18
  ## Selected path
19
19
 
20
- `/team-advisor` is the repository's advisor implementation. It provides explicit first-consult packets, delta consults, a standing warm reviewer, an in-session Sol spawn on Codex, and a read-only Sol CLI option.
20
+ `/team-advisor` is the repository's advisor implementation. It provides explicit first-consult packets, delta consults, a standing warm reviewer, an in-session Astra spawn on Codex, and a read-only Astra CLI option.
@@ -0,0 +1,20 @@
1
+ """Shared names for mutation and shell tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ BASH_TOOL_NAME: str = "Bash"
6
+ POWERSHELL_TOOL_NAME: str = "PowerShell"
7
+ WRITE_TOOL_NAME: str = "Write"
8
+ EDIT_TOOL_NAME: str = "Edit"
9
+ MULTI_EDIT_TOOL_NAME: str = "MultiEdit"
10
+ APPLY_PATCH_TOOL_NAME: str = "apply_patch"
11
+
12
+ ALL_BASH_AND_POWERSHELL_TOOL_NAMES: frozenset[str] = frozenset(
13
+ {BASH_TOOL_NAME, POWERSHELL_TOOL_NAME}
14
+ )
15
+ ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES: frozenset[str] = frozenset(
16
+ {WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME}
17
+ )
18
+ ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES: frozenset[str] = frozenset(
19
+ ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES | {APPLY_PATCH_TOOL_NAME}
20
+ )
@@ -2,7 +2,41 @@
2
2
 
3
3
  AST-based Python style checks for code quality enforcement.
4
4
 
5
- The checks live in `python_style_checks.py`; the shared source-line splitting, function-discovery, and statement-classification helpers they build on live in `python_style_helpers.py`. `python_style_import_bootstrap.py` recognizes the repo's sys.path bootstrap-guard idiom so the imports-at-top check does not penalize a file for following it. `hook_timing_harness.py` measures real wall-clock time for the Write/Edit hooks `hooks.json` registers, reporting p50 and p95 over a configurable run count. `fast_save_validators.py` runs the twelve non-Mypy, non-Ruff checks in-process for the Write/Edit save-path gate, with no per-check subprocess.
5
+ The checks live in `python_style_checks.py`. Shared source-line splitting, function discovery, and statement classification live in `python_style_helpers.py`. `python_style_import_bootstrap.py` recognizes the repo's sys.path bootstrap-guard idiom so the imports-at-top check does not penalize a file for following it. `fast_save_validators.py` runs the twelve non-Mypy, non-Ruff checks in-process for the Write/Edit save-path gate, with no per-check subprocess.
6
+
7
+ ## Production modules
8
+
9
+ | File | Role |
10
+ |---|---|
11
+ | `abbreviation_checks.py` | Abbreviation and single-letter name checks |
12
+ | `code_quality_checks.py` | Function, nesting, and file length checks |
13
+ | `comment_checks.py` | Comment detection for the opt-in diff-aware linter |
14
+ | `exempt_paths.py` | Shared config, test, and hook-infrastructure path exemptions |
15
+ | `fast_save_validators.py` | In-process Write/Edit save-path validator roster |
16
+ | `file_structure_checks.py` | File structure checks for pre-PR validation |
17
+ | `git_checks.py` | Git and GitHub checks for pre-push review |
18
+ | `health_check.py` | Validator availability, dependency, and version checks |
19
+ | `hook_timing_harness.py` | Hook runtime measurement and threshold report |
20
+ | `magic_value_checks.py` | Hardcoded magic-number checks |
21
+ | `mypy_integration.py` | Mypy static type checking integration |
22
+ | `output_formatter.py` | Colored, diff, progress, and JSON validator output |
23
+ | `pr_reference_checks.py` | PR and commit reference checks in comments |
24
+ | `project_roots.py` | Project-root resolution from a path |
25
+ | `pyproject_config_discovery.py` | Walk-up discovery of tool pyproject tables |
26
+ | `python_antipattern_checks.py` | Mutable defaults, bare except, and print checks |
27
+ | `python_style_checks.py` | Import placement, decorator spacing, blank lines, and view naming |
28
+ | `python_style_helpers.py` | Source-line splitting and function-discovery helpers |
29
+ | `python_style_import_bootstrap.py` | sys.path bootstrap-guard recognizer |
30
+ | `react_checks.py` | React class-component and error-boundary checks |
31
+ | `ruff_integration.py` | Ruff lint integration |
32
+ | `run_all_validators.py` | Pre-push validator orchestration and report |
33
+ | `security_checks.py` | Hardcoded-secret, SQL injection, and XSS checks |
34
+ | `system_temporary_roots.py` | System temp-root membership for staged copies |
35
+ | `todo_checks.py` | TODO/FIXME tracking with issue-reference requirement |
36
+ | `type_safety_checks.py` | Missing type hints and Any usage checks |
37
+ | `useless_test_checks.py` | Useless-test detection |
38
+ | `validator_base.py` | Shared validator dataclasses, source read, and parse |
39
+ | `validator_defaults.py` | Shared constants for the validators package |
6
40
 
7
41
  ## Checks Implemented
8
42
 
@@ -14,7 +14,7 @@ the CLI mode calls. A save-path finding matches the CLI mode's finding for
14
14
  the same validator; only Mypy and the process-per-check overhead are gone.
15
15
  """
16
16
 
17
- from collections.abc import Callable, Sequence
17
+ from collections.abc import Callable, Collection, Sequence
18
18
  from dataclasses import dataclass
19
19
  from pathlib import Path
20
20
 
@@ -125,92 +125,61 @@ class _FastSaveValidatorSpecification:
125
125
  no_matching_files_message: str
126
126
 
127
127
 
128
- def _all_fast_save_validator_specifications() -> (
129
- "tuple[_FastSaveValidatorSpecification, ...]"
130
- ):
131
- """Return the save-path roster: every non-Mypy, non-Ruff file-scoped check."""
128
+ def _build_specification(
129
+ display_name: str,
130
+ checks: str,
131
+ applies_to_file: FilePredicate,
132
+ collect_violation_lines: FileViolationLineCollector,
133
+ no_matching_files_message: str,
134
+ ) -> _FastSaveValidatorSpecification:
135
+ return _FastSaveValidatorSpecification(
136
+ display_name,
137
+ checks,
138
+ applies_to_file,
139
+ collect_violation_lines,
140
+ no_matching_files_message,
141
+ )
142
+
143
+
144
+ def _python_specifications() -> tuple[_FastSaveValidatorSpecification, ...]:
145
+ return (
146
+ _build_specification("Python Style", "1,2,3,4", _is_python_file, _violation_lines_from(validate_python_style_file), NO_PYTHON_FILES_MESSAGE),
147
+ _build_specification("Abbreviations", "5", _is_python_file, _violation_lines_from(validate_abbreviation_file), NO_PYTHON_FILES_MESSAGE),
148
+ _build_specification("Magic Values", "7", _is_python_file, _violation_lines_from(validate_magic_literal_file), NO_PYTHON_FILES_MESSAGE),
149
+ _build_specification("Security", "27,28,29", _is_python_file, _violation_lines_from(validate_security_file), NO_PYTHON_FILES_MESSAGE),
150
+ _build_specification("Code Quality", "30,31,32", _is_python_file, _violation_lines_from(validate_code_quality_file), NO_PYTHON_FILES_MESSAGE),
151
+ _build_specification("Python Anti-patterns", "33,34,35", _is_python_file, _violation_lines_from(validate_python_antipattern_file), NO_PYTHON_FILES_MESSAGE),
152
+ _build_specification("Type Safety", "39,40", _is_python_file, _violation_lines_from(validate_type_safety_file), NO_PYTHON_FILES_MESSAGE),
153
+ )
154
+
155
+
156
+ def _test_specifications() -> tuple[_FastSaveValidatorSpecification, ...]:
157
+ return (
158
+ _build_specification("Test Safety", "11,21", _is_test_python_file, _collect_test_safety_violation_lines, NO_TEST_FILES_MESSAGE),
159
+ _build_specification("Useless Tests", "12", _is_test_python_file, _violation_lines_from(validate_useless_test_file), NO_TEST_FILES_MESSAGE),
160
+ )
161
+
162
+
163
+ def _non_python_specifications() -> tuple[_FastSaveValidatorSpecification, ...]:
132
164
  return (
133
- # Python naming, style, and constants.
134
- _FastSaveValidatorSpecification(
135
- "Python Style",
136
- "1,2,3,4",
137
- _is_python_file,
138
- _violation_lines_from(validate_python_style_file),
139
- NO_PYTHON_FILES_MESSAGE,
140
- ),
141
- _FastSaveValidatorSpecification(
142
- "Abbreviations",
143
- "5",
144
- _is_python_file,
145
- _violation_lines_from(validate_abbreviation_file),
146
- NO_PYTHON_FILES_MESSAGE,
147
- ),
148
- _FastSaveValidatorSpecification(
149
- "Magic Values",
150
- "7",
151
- _is_python_file,
152
- _violation_lines_from(validate_magic_literal_file),
153
- NO_PYTHON_FILES_MESSAGE,
154
- ),
155
- _FastSaveValidatorSpecification(
156
- "Security",
157
- "27,28,29",
158
- _is_python_file,
159
- _violation_lines_from(validate_security_file),
160
- NO_PYTHON_FILES_MESSAGE,
161
- ),
162
- _FastSaveValidatorSpecification(
163
- "Code Quality",
164
- "30,31,32",
165
- _is_python_file,
166
- _violation_lines_from(validate_code_quality_file),
167
- NO_PYTHON_FILES_MESSAGE,
168
- ),
169
- # Python anti-patterns and typing.
170
- _FastSaveValidatorSpecification(
171
- "Python Anti-patterns",
172
- "33,34,35",
173
- _is_python_file,
174
- _violation_lines_from(validate_python_antipattern_file),
175
- NO_PYTHON_FILES_MESSAGE,
176
- ),
177
- _FastSaveValidatorSpecification(
178
- "Type Safety",
179
- "39,40",
180
- _is_python_file,
181
- _violation_lines_from(validate_type_safety_file),
182
- NO_PYTHON_FILES_MESSAGE,
183
- ),
184
- # Scoped to a test_*.py-shaped file.
185
- _FastSaveValidatorSpecification(
186
- "Test Safety",
187
- "11,21",
188
- _is_test_python_file,
189
- _collect_test_safety_violation_lines,
190
- NO_TEST_FILES_MESSAGE,
191
- ),
192
- _FastSaveValidatorSpecification(
193
- "Useless Tests",
194
- "12",
195
- _is_test_python_file,
196
- _violation_lines_from(validate_useless_test_file),
197
- NO_TEST_FILES_MESSAGE,
198
- ),
199
- # Non-Python or multi-language files.
200
- _FastSaveValidatorSpecification(
201
- "React",
202
- "17",
203
- _is_react_file,
204
- _collect_react_violation_lines,
205
- NO_REACT_FILES_MESSAGE,
206
- ),
207
- _FastSaveValidatorSpecification(
208
- "PR References",
209
- "6",
210
- _is_code_reference_file,
211
- _violation_lines_from(validate_pr_reference_file),
212
- NO_CODE_REFERENCE_FILES_MESSAGE,
213
- ),
165
+ _build_specification("React", "17", _is_react_file, _collect_react_violation_lines, NO_REACT_FILES_MESSAGE),
166
+ _build_specification("PR References", "6", _is_code_reference_file, _violation_lines_from(validate_pr_reference_file), NO_CODE_REFERENCE_FILES_MESSAGE),
167
+ )
168
+
169
+
170
+ def _all_fast_save_validator_specifications(
171
+ excluded_validator_names: Collection[str] = (),
172
+ ) -> tuple[_FastSaveValidatorSpecification, ...]:
173
+ """Return the save-path roster: every non-Mypy, non-Ruff file-scoped check."""
174
+ all_specifications = (
175
+ *_python_specifications(),
176
+ *_test_specifications(),
177
+ *_non_python_specifications(),
178
+ )
179
+ return tuple(
180
+ each_specification
181
+ for each_specification in all_specifications
182
+ if each_specification.display_name not in excluded_validator_names
214
183
  )
215
184
 
216
185
 
@@ -250,11 +219,14 @@ def _run_specification(
250
219
  return _fast_save_outcome(specification, False, violation_report)
251
220
 
252
221
 
253
- def run_fast_save_validators(files: "list[Path]") -> "list[ValidatorResult]":
222
+ def run_fast_save_validators(
223
+ files: "list[Path]", excluded_validator_names: Collection[str] = ()
224
+ ) -> "list[ValidatorResult]":
254
225
  """Run every save-path validator in-process, skipping Mypy and Ruff.
255
226
 
256
227
  Args:
257
- files: The files under validation -- one reconstructed file in gate mode.
228
+ files: Files under validation. Gate mode passes one reconstructed file.
229
+ excluded_validator_names: Display names omitted from this run.
258
230
 
259
231
  Returns:
260
232
  One result per in-process validator, in roster order. The caller adds
@@ -262,5 +234,7 @@ def run_fast_save_validators(files: "list[Path]") -> "list[ValidatorResult]":
262
234
  """
263
235
  return [
264
236
  _run_specification(each_specification, files)
265
- for each_specification in _all_fast_save_validator_specifications()
237
+ for each_specification in _all_fast_save_validator_specifications(
238
+ excluded_validator_names
239
+ )
266
240
  ]