@alex-dbs/coderuler 0.0.0-stage → 1.3.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 +175 -0
- package/README.md +218 -2
- package/dist/adapter/cli/commands/check.d.ts +3 -0
- package/dist/adapter/cli/commands/check.js +93 -0
- package/dist/adapter/cli/commands/deps.d.ts +3 -0
- package/dist/adapter/cli/commands/deps.js +4 -0
- package/dist/adapter/cli/commands/inspect.d.ts +3 -0
- package/dist/adapter/cli/commands/inspect.js +59 -0
- package/dist/adapter/cli/commands/rules.d.ts +10 -0
- package/dist/adapter/cli/commands/rules.js +17 -0
- package/dist/adapter/cli/commands/skill.d.ts +4 -0
- package/dist/adapter/cli/commands/skill.js +7 -0
- package/dist/adapter/cli/commands/test.d.ts +3 -0
- package/dist/adapter/cli/commands/test.js +78 -0
- package/dist/adapter/cli/commands/update.d.ts +3 -0
- package/dist/adapter/cli/commands/update.js +7 -0
- package/dist/adapter/cli/commands/validate.d.ts +3 -0
- package/dist/adapter/cli/commands/validate.js +12 -0
- package/dist/adapter/cli/exit.d.ts +7 -0
- package/dist/adapter/cli/exit.js +10 -0
- package/dist/adapter/cli/inspect-json.d.ts +9 -0
- package/dist/adapter/cli/inspect-json.js +160 -0
- package/dist/adapter/cli/inspect-usage.d.ts +9 -0
- package/dist/adapter/cli/inspect-usage.js +14 -0
- package/dist/adapter/cli/program.d.ts +4 -0
- package/dist/adapter/cli/program.js +216 -0
- package/dist/adapter/cli/progress.d.ts +22 -0
- package/dist/adapter/cli/progress.js +79 -0
- package/dist/adapter/cli/reports.d.ts +47 -0
- package/dist/adapter/cli/reports.js +735 -0
- package/dist/adapter/cli/self-update.d.ts +1 -0
- package/dist/adapter/cli/self-update.js +89 -0
- package/dist/adapter/cli/services.d.ts +101 -0
- package/dist/adapter/cli/services.js +1 -0
- package/dist/adapter/cli/text.d.ts +5 -0
- package/dist/adapter/cli/text.js +36 -0
- package/dist/adapter/cli/version.d.ts +1 -0
- package/dist/adapter/cli/version.js +3 -0
- package/dist/adapter/decision/jev.d.ts +11 -0
- package/dist/adapter/decision/jev.js +138 -0
- package/dist/adapter/filesystem/dependencies.d.ts +26 -0
- package/dist/adapter/filesystem/dependencies.js +577 -0
- package/dist/adapter/filesystem/dependency-installation.d.ts +11 -0
- package/dist/adapter/filesystem/dependency-installation.js +373 -0
- package/dist/adapter/filesystem/project.d.ts +14 -0
- package/dist/adapter/filesystem/project.js +387 -0
- package/dist/adapter/filesystem/rule-resolution.d.ts +4 -0
- package/dist/adapter/filesystem/rule-resolution.js +516 -0
- package/dist/adapter/filesystem/rule-test-worker.d.ts +17 -0
- package/dist/adapter/filesystem/rule-test-worker.js +47 -0
- package/dist/adapter/filesystem/rule-tests.d.ts +3 -0
- package/dist/adapter/filesystem/rule-tests.js +264 -0
- package/dist/adapter/filesystem/rules.d.ts +13 -0
- package/dist/adapter/filesystem/rules.js +242 -0
- package/dist/adapter/git/changes.d.ts +1 -0
- package/dist/adapter/git/changes.js +54 -0
- package/dist/adapter/git/environment.d.ts +1 -0
- package/dist/adapter/git/environment.js +10 -0
- package/dist/adapter/github/api.d.ts +5 -0
- package/dist/adapter/github/api.js +40 -0
- package/dist/adapter/github/comments.d.ts +3 -0
- package/dist/adapter/github/comments.js +235 -0
- package/dist/adapter/github/pull-request.d.ts +16 -0
- package/dist/adapter/github/pull-request.js +139 -0
- package/dist/adapter/scope/astgrep-languages.d.ts +5 -0
- package/dist/adapter/scope/astgrep-languages.js +119 -0
- package/dist/adapter/scope/astgrep.d.ts +14 -0
- package/dist/adapter/scope/astgrep.js +183 -0
- package/dist/adapter/scope/csv.d.ts +4 -0
- package/dist/adapter/scope/csv.js +158 -0
- package/dist/adapter/scope/file.d.ts +2 -0
- package/dist/adapter/scope/file.js +4 -0
- package/dist/adapter/scope/json.d.ts +7 -0
- package/dist/adapter/scope/json.js +132 -0
- package/dist/adapter/scope/lines.d.ts +10 -0
- package/dist/adapter/scope/lines.js +42 -0
- package/dist/adapter/scope/protobuf-options.d.ts +29 -0
- package/dist/adapter/scope/protobuf-options.js +64 -0
- package/dist/adapter/scope/protobuf-parser.d.ts +30 -0
- package/dist/adapter/scope/protobuf-parser.js +772 -0
- package/dist/adapter/scope/protobuf.d.ts +5 -0
- package/dist/adapter/scope/protobuf.js +53 -0
- package/dist/adapter/scope/structured.d.ts +10 -0
- package/dist/adapter/scope/structured.js +84 -0
- package/dist/adapter/scope/yaml.d.ts +7 -0
- package/dist/adapter/scope/yaml.js +224 -0
- package/dist/adapter/skills/cli.d.ts +10 -0
- package/dist/adapter/skills/cli.js +77 -0
- package/dist/app/concurrency.d.ts +1 -0
- package/dist/app/concurrency.js +44 -0
- package/dist/app/evaluators.d.ts +14 -0
- package/dist/app/evaluators.js +175 -0
- package/dist/app/execute.d.ts +2 -0
- package/dist/app/execute.js +427 -0
- package/dist/app/jsonpath.d.ts +1 -0
- package/dist/app/jsonpath.js +8 -0
- package/dist/app/ports.d.ts +165 -0
- package/dist/app/ports.js +1 -0
- package/dist/app/rule-tests.d.ts +20 -0
- package/dist/app/rule-tests.js +11 -0
- package/dist/app/rule.d.ts +121 -0
- package/dist/app/rule.js +24 -0
- package/dist/app/selection.d.ts +2 -0
- package/dist/app/selection.js +23 -0
- package/dist/app/usage.d.ts +2 -0
- package/dist/app/usage.js +13 -0
- package/dist/app/validate.d.ts +11 -0
- package/dist/app/validate.js +415 -0
- package/dist/app/violation-comments.d.ts +10 -0
- package/dist/app/violation-comments.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +3 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +11 -0
- package/dist/infra/runtime.d.ts +3 -0
- package/dist/infra/runtime.js +168 -0
- package/dist/infra/settings.d.ts +5 -0
- package/dist/infra/settings.js +91 -0
- package/package.json +86 -4
- package/skills/coderuler-new-rule/SKILL.md +141 -0
- package/skills/coderuler-new-rule/reference.md +447 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { JevAdapter } from '../adapter/decision/jev.js';
|
|
2
|
+
const DEFAULT_BACKEND = 'jev';
|
|
3
|
+
const DEFAULT_MODEL = 'jev-latest';
|
|
4
|
+
const DEFAULT_BASE_URL = 'https://api.typesafe.ai';
|
|
5
|
+
const DEFAULT_THRESHOLD = 0.4;
|
|
6
|
+
const DEFAULT_INPUT_COST_PER_MILLION = 0.042;
|
|
7
|
+
const DEFAULT_OUTPUT_COST_PER_MILLION = 0;
|
|
8
|
+
const THRESHOLD_MESSAGE = 'must be a number between 0 and 1';
|
|
9
|
+
const DECIMAL = /^(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)$/;
|
|
10
|
+
function validateThreshold(value, label) {
|
|
11
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
12
|
+
throw new RangeError(`${label} ${THRESHOLD_MESSAGE}, got ${String(value)}`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function parseThreshold(value, label) {
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
if (!DECIMAL.test(trimmed))
|
|
19
|
+
throw new RangeError(`${label} ${THRESHOLD_MESSAGE}, got ${JSON.stringify(value)}`);
|
|
20
|
+
return validateThreshold(Number(trimmed), label);
|
|
21
|
+
}
|
|
22
|
+
function validatePrice(value, label) {
|
|
23
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
24
|
+
throw new RangeError(`${label} must be a finite nonnegative number, got ${String(value)}`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function price(override, ruleValue, env, name, fallback) {
|
|
29
|
+
if (override !== undefined)
|
|
30
|
+
return override;
|
|
31
|
+
if (ruleValue !== undefined)
|
|
32
|
+
return ruleValue;
|
|
33
|
+
const raw = setting(env, name);
|
|
34
|
+
if (raw === undefined)
|
|
35
|
+
return fallback;
|
|
36
|
+
if (!DECIMAL.test(raw))
|
|
37
|
+
throw new RangeError(`${name} must be a finite nonnegative decimal number`);
|
|
38
|
+
return validatePrice(Number(raw), name);
|
|
39
|
+
}
|
|
40
|
+
function setting(env, name) {
|
|
41
|
+
const value = env[name];
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
return undefined;
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
if (trimmed === '')
|
|
46
|
+
throw new Error(`${name} is set but empty`);
|
|
47
|
+
return trimmed;
|
|
48
|
+
}
|
|
49
|
+
export function createDecisionResolver(env, thresholdOverride, pricingOverrides) {
|
|
50
|
+
const flagThreshold = thresholdOverride === undefined ? undefined : validateThreshold(thresholdOverride, '--threshold');
|
|
51
|
+
const inputOverride = pricingOverrides?.inputCostPerMillion;
|
|
52
|
+
const outputOverride = pricingOverrides?.outputCostPerMillion;
|
|
53
|
+
if (inputOverride !== undefined)
|
|
54
|
+
validatePrice(inputOverride, '--input-cost-per-million');
|
|
55
|
+
if (outputOverride !== undefined)
|
|
56
|
+
validatePrice(outputOverride, '--output-cost-per-million');
|
|
57
|
+
return settings => {
|
|
58
|
+
const backend = settings.backend ?? setting(env, 'RK_BACKEND') ?? DEFAULT_BACKEND;
|
|
59
|
+
if (backend !== 'jev') {
|
|
60
|
+
throw new Error(`Unknown decision backend '${backend}'; supported backends: ${DEFAULT_BACKEND}`);
|
|
61
|
+
}
|
|
62
|
+
let threshold;
|
|
63
|
+
if (flagThreshold !== undefined) {
|
|
64
|
+
threshold = flagThreshold;
|
|
65
|
+
}
|
|
66
|
+
else if (settings.threshold !== undefined) {
|
|
67
|
+
threshold = validateThreshold(settings.threshold, 'rule.settings.threshold');
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const raw = setting(env, 'RK_THRESHOLD');
|
|
71
|
+
threshold = raw === undefined ? DEFAULT_THRESHOLD : parseThreshold(raw, 'RK_THRESHOLD');
|
|
72
|
+
}
|
|
73
|
+
if (settings.inputCostPerMillion !== undefined) {
|
|
74
|
+
validatePrice(settings.inputCostPerMillion, 'rule.settings.inputCostPerMillion');
|
|
75
|
+
}
|
|
76
|
+
if (settings.outputCostPerMillion !== undefined) {
|
|
77
|
+
validatePrice(settings.outputCostPerMillion, 'rule.settings.outputCostPerMillion');
|
|
78
|
+
}
|
|
79
|
+
const pricing = {
|
|
80
|
+
inputCostPerMillion: price(inputOverride, settings.inputCostPerMillion, env, 'RK_INPUT_COST_PER_MILLION', DEFAULT_INPUT_COST_PER_MILLION),
|
|
81
|
+
outputCostPerMillion: price(outputOverride, settings.outputCostPerMillion, env, 'RK_OUTPUT_COST_PER_MILLION', DEFAULT_OUTPUT_COST_PER_MILLION),
|
|
82
|
+
};
|
|
83
|
+
const model = settings.model ?? setting(env, 'TYPESAFE_DEFAULT_MODEL') ?? DEFAULT_MODEL;
|
|
84
|
+
const baseUrl = setting(env, 'TYPESAFE_BASE_URL') ?? DEFAULT_BASE_URL;
|
|
85
|
+
const apiKey = setting(env, 'TYPESAFE_API_KEY');
|
|
86
|
+
if (apiKey === undefined) {
|
|
87
|
+
throw new Error('No decision API key: set TYPESAFE_API_KEY');
|
|
88
|
+
}
|
|
89
|
+
return { backend: new JevAdapter({ model, baseUrl, apiKey }), threshold, pricing };
|
|
90
|
+
};
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,88 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alex-dbs/coderuler",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/alex-dbs/coderuler.git"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": "^24.15.0 || >=26.0.0"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"coderuler": "dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"skills/coderuler-new-rule"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.build.json",
|
|
32
|
+
"dev": "npx tsx src/cli.ts",
|
|
33
|
+
"start": "node dist/cli.js",
|
|
34
|
+
"test": "npm run build && node --test --test-concurrency=4 tests/*.test.ts",
|
|
35
|
+
"test:golden": "npm run build && node --test tests/golden.test.ts",
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@ast-grep/lang-angular": "0.0.10",
|
|
40
|
+
"@ast-grep/lang-bash": "0.0.8",
|
|
41
|
+
"@ast-grep/lang-bicep": "0.0.2",
|
|
42
|
+
"@ast-grep/lang-c": "0.0.6",
|
|
43
|
+
"@ast-grep/lang-cpp": "0.0.6",
|
|
44
|
+
"@ast-grep/lang-csharp": "0.0.6",
|
|
45
|
+
"@ast-grep/lang-dart": "0.0.7",
|
|
46
|
+
"@ast-grep/lang-elixir": "0.0.7",
|
|
47
|
+
"@ast-grep/lang-glimmer-javascript": "0.0.2",
|
|
48
|
+
"@ast-grep/lang-glimmer-typescript": "0.0.2",
|
|
49
|
+
"@ast-grep/lang-go": "0.0.6",
|
|
50
|
+
"@ast-grep/lang-haskell": "0.0.7",
|
|
51
|
+
"@ast-grep/lang-java": "0.0.7",
|
|
52
|
+
"@ast-grep/lang-json": "0.0.7",
|
|
53
|
+
"@ast-grep/lang-kotlin": "0.0.7",
|
|
54
|
+
"@ast-grep/lang-lua": "0.0.7",
|
|
55
|
+
"@ast-grep/lang-markdown": "0.0.6",
|
|
56
|
+
"@ast-grep/lang-php": "0.0.7",
|
|
57
|
+
"@ast-grep/lang-python": "0.0.6",
|
|
58
|
+
"@ast-grep/lang-ruby": "0.0.7",
|
|
59
|
+
"@ast-grep/lang-rust": "0.0.7",
|
|
60
|
+
"@ast-grep/lang-scala": "0.0.7",
|
|
61
|
+
"@ast-grep/lang-sql": "0.0.8",
|
|
62
|
+
"@ast-grep/lang-swift": "0.0.8",
|
|
63
|
+
"@ast-grep/lang-toml": "0.0.9",
|
|
64
|
+
"@ast-grep/lang-yaml": "0.0.6",
|
|
65
|
+
"@ast-grep/napi": "0.50.0",
|
|
66
|
+
"@npmcli/arborist": "10.0.4",
|
|
67
|
+
"@npmcli/package-json": "8.0.0",
|
|
68
|
+
"axios": "1.20.0",
|
|
69
|
+
"commander": "15.0.0",
|
|
70
|
+
"csv-parse": "7.0.3",
|
|
71
|
+
"jsonc-parser": "3.3.1",
|
|
72
|
+
"jsonpath-plus": "11.2.1",
|
|
73
|
+
"npm-install-checks": "9.0.0",
|
|
74
|
+
"npm-package-arg": "14.0.0",
|
|
75
|
+
"pacote": "22.0.0",
|
|
76
|
+
"skills": "1.7.2",
|
|
77
|
+
"ssri": "14.0.0",
|
|
78
|
+
"string-width": "8.3.0",
|
|
79
|
+
"yaml": "^2.9.1"
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@types/node": "^24.19.2",
|
|
83
|
+
"nock": "15.0.5",
|
|
84
|
+
"tar": "7.5.22",
|
|
85
|
+
"tsx": "4.23.15",
|
|
86
|
+
"typescript": "^7.0.2"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coderuler-new-rule
|
|
3
|
+
description: Create CodeRuler rules, including custom selection, filters and checks, inline tests, and inspection.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create a CodeRuler rule
|
|
7
|
+
|
|
8
|
+
Use the installed `coderuler` CLI from the project root. In commands, `.` means this project. Import the public SDK from `@alex-dbs/coderuler`.
|
|
9
|
+
|
|
10
|
+
For all rule options, read [reference.md](reference.md) when that branch is needed.
|
|
11
|
+
|
|
12
|
+
## 1. Define the policy
|
|
13
|
+
|
|
14
|
+
Establish:
|
|
15
|
+
|
|
16
|
+
- **Applicability:** which files and scopes belong to this policy, and which data must exist?
|
|
17
|
+
- **Decision:** what exact condition separates a pass from a violation?
|
|
18
|
+
- **Evidence:** one realistic violation, one compliant counterpart, and the boundaries that could change the decision.
|
|
19
|
+
|
|
20
|
+
Each rule must check exactly one thing. Split independent checks into separate rules.
|
|
21
|
+
|
|
22
|
+
Use deterministic code for structural, numeric, equality, and pattern policies. Use AI when the decision genuinely requires semantic judgment; keep selection and applicability deterministic.
|
|
23
|
+
|
|
24
|
+
## 2. Write the dataset, then the rule
|
|
25
|
+
|
|
26
|
+
Create `.coderuler/<name>.rule.ts` and default-export a class extending `Rule`. The filename supplies the rule name. Supported module suffixes are `.rule.ts`, `.rule.mts`, `.rule.js`, and `.rule.mjs`; helper modules use ordinary filenames.
|
|
27
|
+
|
|
28
|
+
Start the name with its language or technology prefix, such as `go-`, `ts-`, or `pg-`, followed by the policy.
|
|
29
|
+
|
|
30
|
+
Keep rules short, with only a few lines of policy code. Reuse built-in selectors, evaluators, and standard libraries before writing custom logic. Add helpers only for real reuse.
|
|
31
|
+
|
|
32
|
+
Add a named `tests` export in the same file. Use `.passes()` and `.violates()` cases that exercise the policy. Omit `.skips()` assertions when creating a rule: skipped inputs never reach the check and provide no evidence of policy behavior.
|
|
33
|
+
|
|
34
|
+
Run the relevant cases before the policy change and require an assertion mismatch for the intended violation, not a loading error. Implement the policy, then require every case to pass.
|
|
35
|
+
|
|
36
|
+
This complete example requires at least two replicas whenever `spec.replicas` is an integer:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { Rule, pass, scopes, test, violation } from '@alex-dbs/coderuler';
|
|
40
|
+
import type { Evaluator, ScopeSubject } from '@alex-dbs/coderuler';
|
|
41
|
+
|
|
42
|
+
type Deployment = { replicas: number };
|
|
43
|
+
|
|
44
|
+
export default class ReplicaFloor extends Rule<Deployment> {
|
|
45
|
+
files = ['deploy/**/*.yml'];
|
|
46
|
+
scope = scopes.yaml<Deployment>({ path: '$.spec' });
|
|
47
|
+
|
|
48
|
+
filter({ data }: ScopeSubject<Deployment>) {
|
|
49
|
+
return {
|
|
50
|
+
applies: data !== null && typeof data === 'object'
|
|
51
|
+
&& Number.isInteger(data.replicas),
|
|
52
|
+
reason: 'Requires an integer spec.replicas',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
check: Evaluator<Deployment> = subject => {
|
|
57
|
+
const { replicas } = (subject as ScopeSubject<Deployment>).data!;
|
|
58
|
+
return replicas >= 2 ? pass() : violation(`Set spec.replicas to at least 2; found ${replicas}`);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const tests = [
|
|
63
|
+
test('the minimum replica count passes').withFiles({
|
|
64
|
+
'deploy/api.yml': { spec: { replicas: 2 } },
|
|
65
|
+
}).passes(),
|
|
66
|
+
test('one replica violates').withFiles({
|
|
67
|
+
'deploy/api.yml': { spec: { replicas: 1 } },
|
|
68
|
+
}).violates(),
|
|
69
|
+
test('zero is a real value, not missing data').withFiles({
|
|
70
|
+
'deploy/api.yml': { spec: { replicas: 0 } },
|
|
71
|
+
}).violates(),
|
|
72
|
+
];
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Save it as `.coderuler/k8s-replica-floor.rule.ts`. The scope cast reflects the fixed default `group = 'scope'`; the non-null assertion reflects the filter's required-data precondition. A TypeScript generic describes expected data; the filter still establishes that shape at runtime. If a missing or malformed field is itself a violation, select its enclosing object and check that requirement instead of filtering it away.
|
|
76
|
+
|
|
77
|
+
## 3. Validate and run the tests
|
|
78
|
+
|
|
79
|
+
For the example:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
coderuler validate .coderuler/k8s-replica-floor.rule.ts
|
|
83
|
+
coderuler test .coderuler/k8s-replica-floor.rule.ts
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Then run the project's rules and their datasets:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
coderuler validate
|
|
90
|
+
coderuler test
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`validate` checks the module contract, not policy behavior or TypeScript semantics. `check` and `validate` do not execute inline tests. `test` with no selected cases is an error, not success.
|
|
94
|
+
|
|
95
|
+
Useful test options:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
coderuler test . --failures-only
|
|
99
|
+
coderuler test . --concurrency 4 --timeout 30s
|
|
100
|
+
coderuler test .coderuler/k8s-semantic-policy.rule.ts --ai --timeout 45s
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Replace the semantic-policy filename with the actual AI rule. `--ai` authorizes live provider calls for `aiTests`, requires provider configuration, and may incur charges. Outcomes can vary; provider errors are errors, not passes.
|
|
104
|
+
|
|
105
|
+
## 4. Inspect the rule on the project
|
|
106
|
+
|
|
107
|
+
Tests use temporary fixtures. Inspection uses actual files under `.`; create representative project data there if none exists yet.
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
coderuler inspect .coderuler/k8s-replica-floor.rule.ts .
|
|
111
|
+
coderuler inspect k8s-replica-floor . --json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
An exact rule name uses discovery; a module path selects that file directly. Inspection accepts one rule, not a directory. Use one output format at a time (`--json`, `--jsonl`, or `--raw`).
|
|
115
|
+
|
|
116
|
+
Follow the pipeline:
|
|
117
|
+
|
|
118
|
+
`enabled → when → select → filter → group → check`
|
|
119
|
+
|
|
120
|
+
- **No files/scopes:** check globs, exclusions, selector paths, and the inspected root.
|
|
121
|
+
- **Filtered scopes:** read the applicability reason; confirm the filter excludes only out-of-policy data.
|
|
122
|
+
- **Unexpected decision:** inspect selected `data`/`source`, captures, group membership, and the result. Use `context.trace(message, details)` inside hooks/checks when intermediate domain values would explain the decision.
|
|
123
|
+
- **Runtime error:** fix invalid data, parsing, or implementation behavior; converting an error into a pass hides a broken rule.
|
|
124
|
+
|
|
125
|
+
Inspection runs the actual check, including live AI calls when present.
|
|
126
|
+
|
|
127
|
+
Finally exercise normal evaluation:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
coderuler check . --rules .coderuler/k8s-replica-floor.rule.ts --output-all
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Good and bad practices
|
|
134
|
+
|
|
135
|
+
| Prefer | Avoid |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| Narrow selection, applicability in `when`/`filter`, policy in `check` | Repeating guards in the check or filtering out the violations the rule should report |
|
|
138
|
+
| A function-valued `check = ...` or typed `check: Evaluator<T> = ...` | A `check(...)` method instead of the required property |
|
|
139
|
+
| Actionable violations naming the field/value and required correction | Generic messages with no location-specific reason |
|
|
140
|
+
| Pure per-subject decisions and local state | Results depending on concurrent check order or mutable module caches |
|
|
141
|
+
| Real failing-before/passing-after datasets and an inspected applicable subject | Merely validating imports, running without selected subjects, or treating a skip as a pass |
|