jevkit-lint 0.1.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/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # jevkit-lint
2
+
3
+ Static linter for [TypeSafe](https://typesafe.ai) Jev questions.
4
+
5
+ TypeSafe publishes a [list of failure modes](https://docs.typesafe.ai/model-jaggedness/jev-1.13)
6
+ for `jev-1.13`: it reads instructions literally, it cannot count, it reads dates
7
+ as text rather than ordered quantities, it loses accuracy on indirection. Most
8
+ of those are visible in your question definitions before you send anything.
9
+
10
+ `jevkit-lint` reads the definitions and tells you. It never calls the API, so it
11
+ needs no key and costs nothing to run in CI.
12
+
13
+ > Unofficial and unaffiliated with TypeSafe.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install jevkit-lint
19
+ ```
20
+
21
+ ## Use
22
+
23
+ ```ts
24
+ import { lint } from "jevkit-lint";
25
+
26
+ const result = lint({
27
+ urgency: { type: "noul", instructions: "How many days has the customer waited?" },
28
+ team: {
29
+ type: "choice",
30
+ instructions: "Which team should handle this",
31
+ criteria: { billing: "Payment issues", technical: "Bugs" },
32
+ },
33
+ });
34
+
35
+ console.log(result.format());
36
+ ```
37
+
38
+ ```
39
+ urgency: error [JEV001] Question asks the model to count or do arithmetic.
40
+ found: How many
41
+ hint: jev-1.13 is not a calculator and does not count reliably. Iterate the
42
+ candidates in code, ask one Noul per item, and sum the answers yourself.
43
+ team: warning [JEV008] Choice has no no-match option.
44
+ found: billing, technical
45
+ hint: A Choice always returns one of its options. With nothing meaning 'none of
46
+ these', a state that fits no option still produces a confident-looking
47
+ answer. Add an explicit none/unknown option, or gate on a separate
48
+ presence Noul.
49
+ ```
50
+
51
+ `result.ok` is `true` when nothing rose to an error, so it drops straight into a
52
+ guard:
53
+
54
+ ```ts
55
+ if (!lint(questions, { state }).ok) {
56
+ throw new Error("refusing to send a request that will not answer what we meant");
57
+ }
58
+ ```
59
+
60
+ ## CLI
61
+
62
+ ```bash
63
+ npx jevkit-lint request.json # a {state, questions} object, or bare questions
64
+ npx jevkit-lint cassette.jevl # lint every recorded request
65
+ npx jevkit-lint - < request.json # stdin
66
+ npx jevkit-lint request.json --strict # exit non-zero on warnings too
67
+ npx jevkit-lint request.json --format json
68
+ npx jevkit-lint --list-rules
69
+ ```
70
+
71
+ Exit codes: `0` clean, `1` findings, `2` bad usage.
72
+
73
+ ## Rules
74
+
75
+ Each rule names the documented failure mode it comes from.
76
+
77
+ | Code | Rule | Failure mode |
78
+ | --- | --- | --- |
79
+ | JEV001 | math-and-counting | Math and Numbers |
80
+ | JEV002 | date-comparison | Date and time comparison |
81
+ | JEV003 | generation-request | Generation |
82
+ | JEV004 | negation | Literal reading |
83
+ | JEV005 | vague-scoping | Literal reading |
84
+ | JEV006 | indirection | Indirection |
85
+ | JEV007 | noul-polarity | Contradictory instructions and criteria |
86
+ | JEV008 | choice-no-match | Common-sense structural invariants |
87
+ | JEV009 | choice-arity | Common-sense structural invariants |
88
+ | JEV010 | option-descriptions | Literal reading |
89
+ | JEV011 | score-levels | Literal reading |
90
+ | JEV012 | instructions-present | Literal reading |
91
+ | JEV013 | question-id-reference | Indirection |
92
+ | JEV014 | state-budget | Large state full of irrelevant detail |
93
+ | JEV015 | total-budget | Large state full of irrelevant detail |
94
+ | JEV016 | state-noise-ratio | Large state full of irrelevant detail |
95
+ | JEV017 | numeric-representation | Math and Numbers |
96
+ | JEV018 | duplicate-questions | Common-sense structural invariants |
97
+
98
+ Select or suppress by code:
99
+
100
+ ```ts
101
+ lint(questions, { select: ["JEV001", "JEV002"] });
102
+ lint(questions, { ignore: ["JEV008"] });
103
+ ```
104
+
105
+ ## What it cannot do
106
+
107
+ Adversarial content is a documented failure mode and is **not** linted. Whether
108
+ a state is hostile depends on the content at runtime, not on the question
109
+ definition, so a static rule would be theatre. Screen untrusted state at
110
+ request time instead.
111
+
112
+ Token counts are estimates. jevkit deliberately does not bundle a tokenizer:
113
+ TypeSafe does not publish which one Jev uses, and a confidently wrong count is
114
+ worse than an honest approximation. The estimator errs conservative, so leave
115
+ headroom near the limits.
116
+
117
+ ## License
118
+
119
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /** `jevkit-lint` command line interface. */
3
+ export declare const EXIT_OK = 0;
4
+ export declare const EXIT_FINDINGS = 1;
5
+ export declare const EXIT_USAGE = 2;
6
+ export declare function main(argv?: string[]): number;
7
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,4CAA4C;AAS5C,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,UAAU,IAAI,CAAC;AA6C5B,wBAAgB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,MAAM,CAsGnE"}
package/dist/cli.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ /** `jevkit-lint` command line interface. */
3
+ import { readFileSync } from "node:fs";
4
+ import { RecordFormatError, parseRecords } from "jevkit-core";
5
+ import { lint } from "./linter.js";
6
+ import { RULES } from "./rules.js";
7
+ export const EXIT_OK = 0;
8
+ export const EXIT_FINDINGS = 1;
9
+ export const EXIT_USAGE = 2;
10
+ function readStdin() {
11
+ try {
12
+ return readFileSync(0, "utf8");
13
+ }
14
+ catch {
15
+ return "";
16
+ }
17
+ }
18
+ function loadPayload(path) {
19
+ const text = path === "-" ? readStdin() : readFileSync(path, "utf8");
20
+ const source = path === "-" ? "<stdin>" : path;
21
+ if (path.endsWith(".jevl")) {
22
+ return parseRecords(text, source).map((record, i) => ({
23
+ label: `${source}#${i}`,
24
+ state: record.state,
25
+ questions: record.questions,
26
+ }));
27
+ }
28
+ const data = JSON.parse(text);
29
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
30
+ throw new Error(`${source}: expected a JSON object at the top level`);
31
+ }
32
+ const obj = data;
33
+ const q = obj["questions"];
34
+ if (q && typeof q === "object" && !Array.isArray(q)) {
35
+ return [{ label: source, state: obj["state"] ?? "", questions: q }];
36
+ }
37
+ return [{ label: source, state: "", questions: obj }];
38
+ }
39
+ function printRules() {
40
+ const width = Math.max(...RULES.map((r) => r.name.length));
41
+ console.log("code name".padEnd(8 + width + 2) + " documented failure mode");
42
+ console.log("-".repeat(8 + width + 42));
43
+ for (const r of RULES) {
44
+ console.log(`${r.code} ${r.name.padEnd(width)} ${r.mode}`);
45
+ }
46
+ }
47
+ export function main(argv = process.argv.slice(2)) {
48
+ const files = [];
49
+ let select;
50
+ let ignore;
51
+ let format = "text";
52
+ let strict = false;
53
+ let noColor = false;
54
+ for (let i = 0; i < argv.length; i++) {
55
+ const arg = argv[i];
56
+ switch (arg) {
57
+ case "--select":
58
+ select = argv[++i]?.split(",");
59
+ break;
60
+ case "--ignore":
61
+ ignore = argv[++i]?.split(",");
62
+ break;
63
+ case "--format": {
64
+ const v = argv[++i];
65
+ if (v !== "text" && v !== "json") {
66
+ console.error(`jevkit-lint: --format expects text or json`);
67
+ return EXIT_USAGE;
68
+ }
69
+ format = v;
70
+ break;
71
+ }
72
+ case "--strict":
73
+ strict = true;
74
+ break;
75
+ case "--no-color":
76
+ noColor = true;
77
+ break;
78
+ case "--list-rules":
79
+ printRules();
80
+ return EXIT_OK;
81
+ case "-h":
82
+ case "--help":
83
+ console.log("usage: jevkit-lint [files...] [--select CODES] [--ignore CODES]\n" +
84
+ " [--format text|json] [--strict] [--no-color] [--list-rules]\n\n" +
85
+ "Statically lint TypeSafe Jev questions against the documented jev-1.13\n" +
86
+ "failure modes. Never calls the API.");
87
+ return EXIT_OK;
88
+ default:
89
+ if (arg.startsWith("--")) {
90
+ console.error(`jevkit-lint: unknown option ${arg}`);
91
+ return EXIT_USAGE;
92
+ }
93
+ files.push(arg);
94
+ }
95
+ }
96
+ if (!files.length) {
97
+ console.error("jevkit-lint: no input files (use - to read stdin, or --list-rules)");
98
+ return EXIT_USAGE;
99
+ }
100
+ const payloads = [];
101
+ for (const path of files) {
102
+ try {
103
+ payloads.push(...loadPayload(path));
104
+ }
105
+ catch (err) {
106
+ const message = err instanceof RecordFormatError ? err.message : err.message;
107
+ console.error(`jevkit-lint: ${message}`);
108
+ return EXIT_USAGE;
109
+ }
110
+ }
111
+ const color = Boolean(process.stdout.isTTY) && !noColor;
112
+ const reports = [];
113
+ let anyError = false;
114
+ let anyWarning = false;
115
+ for (const { label, state, questions } of payloads) {
116
+ let result;
117
+ try {
118
+ result = lint(questions, { state, select, ignore });
119
+ }
120
+ catch (err) {
121
+ console.error(`jevkit-lint: ${label}: ${err.message}`);
122
+ return EXIT_USAGE;
123
+ }
124
+ anyError = anyError || result.errors.length > 0;
125
+ anyWarning = anyWarning || result.warnings.length > 0;
126
+ reports.push({ label, result });
127
+ }
128
+ if (format === "json") {
129
+ console.log(JSON.stringify({ results: reports.map(({ label, result }) => ({ source: label, ...result.toJSON() })) }, null, 2));
130
+ }
131
+ else {
132
+ const total = { error: 0, warning: 0, info: 0 };
133
+ for (const { label, result } of reports) {
134
+ if (payloads.length > 1)
135
+ console.log(`== ${label}`);
136
+ console.log(result.format(color));
137
+ if (payloads.length > 1)
138
+ console.log("");
139
+ const c = result.counts();
140
+ total.error += c.error;
141
+ total.warning += c.warning;
142
+ total.info += c.info;
143
+ }
144
+ if (total.error + total.warning + total.info > 0) {
145
+ console.log(`\n${total.error} error(s), ${total.warning} warning(s), ${total.info} info`);
146
+ }
147
+ }
148
+ if (anyError)
149
+ return EXIT_FINDINGS;
150
+ if (strict && anyWarning)
151
+ return EXIT_FINDINGS;
152
+ return EXIT_OK;
153
+ }
154
+ const invokedDirectly = process.argv[1] !== undefined && import.meta.url === `file://${process.argv[1]}`;
155
+ if (invokedDirectly)
156
+ process.exit(main());
157
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** Diagnostics produced by the linter. */
2
+ export type Severity = "error" | "warning" | "info";
3
+ export interface DiagnosticInit {
4
+ code: string;
5
+ severity: Severity;
6
+ questionId: string | null;
7
+ message: string;
8
+ hint: string;
9
+ evidence?: string;
10
+ }
11
+ export declare class Diagnostic {
12
+ readonly code: string;
13
+ readonly severity: Severity;
14
+ readonly questionId: string | null;
15
+ readonly message: string;
16
+ readonly hint: string;
17
+ readonly evidence: string;
18
+ constructor(init: DiagnosticInit);
19
+ get sortKey(): [number, string, string];
20
+ format(color?: boolean): string;
21
+ toJSON(): {
22
+ code: string;
23
+ severity: Severity;
24
+ questionId: string | null;
25
+ message: string;
26
+ hint: string;
27
+ evidence: string;
28
+ };
29
+ }
30
+ export declare function compareDiagnostics(a: Diagnostic, b: Diagnostic): number;
31
+ //# sourceMappingURL=diagnostic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic.d.ts","sourceRoot":"","sources":["../src/diagnostic.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAIpD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,UAAU;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,IAAI,EAAE,cAAc;IAShC,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAEtC;IAED,MAAM,CAAC,KAAK,UAAQ,GAAG,MAAM;IAiB7B,MAAM;;;;;;;;CAUP;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAOvE"}
@@ -0,0 +1,60 @@
1
+ /** Diagnostics produced by the linter. */
2
+ const ORDER = { error: 0, warning: 1, info: 2 };
3
+ export class Diagnostic {
4
+ code;
5
+ severity;
6
+ questionId;
7
+ message;
8
+ hint;
9
+ evidence;
10
+ constructor(init) {
11
+ this.code = init.code;
12
+ this.severity = init.severity;
13
+ this.questionId = init.questionId;
14
+ this.message = init.message;
15
+ this.hint = init.hint;
16
+ this.evidence = init.evidence ?? "";
17
+ }
18
+ get sortKey() {
19
+ return [ORDER[this.severity], this.code, this.questionId ?? ""];
20
+ }
21
+ format(color = false) {
22
+ const loc = this.questionId ?? "<request>";
23
+ let head = `${loc}: ${this.severity} [${this.code}] ${this.message}`;
24
+ if (color) {
25
+ const tint = {
26
+ error: "\u001b[31m",
27
+ warning: "\u001b[33m",
28
+ info: "\u001b[36m",
29
+ };
30
+ head = `${tint[this.severity]}${head}\u001b[0m`;
31
+ }
32
+ const lines = [head];
33
+ if (this.evidence)
34
+ lines.push(` found: ${this.evidence}`);
35
+ lines.push(` hint: ${this.hint}`);
36
+ return lines.join("\n");
37
+ }
38
+ toJSON() {
39
+ return {
40
+ code: this.code,
41
+ severity: this.severity,
42
+ questionId: this.questionId,
43
+ message: this.message,
44
+ hint: this.hint,
45
+ evidence: this.evidence,
46
+ };
47
+ }
48
+ }
49
+ export function compareDiagnostics(a, b) {
50
+ const [as_, ac, aq] = a.sortKey;
51
+ const [bs, bc, bq] = b.sortKey;
52
+ if (as_ !== bs)
53
+ return as_ - bs;
54
+ if (ac !== bc)
55
+ return ac < bc ? -1 : 1;
56
+ if (aq !== bq)
57
+ return aq < bq ? -1 : 1;
58
+ return 0;
59
+ }
60
+ //# sourceMappingURL=diagnostic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic.js","sourceRoot":"","sources":["../src/diagnostic.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAI1C,MAAM,KAAK,GAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAW1E,MAAM,OAAO,UAAU;IACZ,IAAI,CAAS;IACb,QAAQ,CAAW;IACnB,UAAU,CAAgB;IAC1B,OAAO,CAAS;IAChB,IAAI,CAAS;IACb,QAAQ,CAAS;IAE1B,YAAY,IAAoB;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACtC,CAAC;IAED,IAAI,OAAO;QACT,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,CAAC,KAAK,GAAG,KAAK;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,WAAW,CAAC;QAC3C,IAAI,IAAI,GAAG,GAAG,GAAG,KAAK,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACrE,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,GAA6B;gBACrC,KAAK,EAAE,YAAY;gBACnB,OAAO,EAAE,YAAY;gBACrB,IAAI,EAAE,YAAY;aACnB,CAAC;YACF,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,WAAW,CAAC;QAClD,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM;QACJ,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,CAAa,EAAE,CAAa;IAC7D,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;IAChC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;IAC/B,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,GAAG,GAAG,EAAE,CAAC;IAChC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Static linter for TypeSafe Jev questions.
3
+ *
4
+ * Catches the failure modes TypeSafe documents for jev-1.13 before you spend a
5
+ * token on them. Needs no API key.
6
+ */
7
+ export { Diagnostic, type Severity } from "./diagnostic.js";
8
+ export { LintResult, lint, type LintOptions } from "./linter.js";
9
+ export { RULES, allCodes, rulesFor, type Rule, type LintContext } from "./rules.js";
10
+ export declare const VERSION = "0.1.0";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpF,eAAO,MAAM,OAAO,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Static linter for TypeSafe Jev questions.
3
+ *
4
+ * Catches the failure modes TypeSafe documents for jev-1.13 before you spend a
5
+ * token on them. Needs no API key.
6
+ */
7
+ export { Diagnostic } from "./diagnostic.js";
8
+ export { LintResult, lint } from "./linter.js";
9
+ export { RULES, allCodes, rulesFor } from "./rules.js";
10
+ export const VERSION = "0.1.0";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAoB,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAA+B,MAAM,YAAY,CAAC;AAEpF,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
@@ -0,0 +1,51 @@
1
+ /** The lint entry point. */
2
+ import { Diagnostic, type Severity } from "./diagnostic.js";
3
+ export interface LintOptions {
4
+ state?: unknown;
5
+ select?: Iterable<string>;
6
+ ignore?: Iterable<string>;
7
+ }
8
+ /** Diagnostics for one request, ordered most severe first. */
9
+ export declare class LintResult {
10
+ readonly diagnostics: Diagnostic[];
11
+ constructor(diagnostics: Diagnostic[]);
12
+ [Symbol.iterator](): Iterator<Diagnostic>;
13
+ get length(): number;
14
+ bySeverity(severity: Severity): Diagnostic[];
15
+ get errors(): Diagnostic[];
16
+ get warnings(): Diagnostic[];
17
+ get infos(): Diagnostic[];
18
+ /** True when nothing rose to an error. */
19
+ get ok(): boolean;
20
+ counts(): {
21
+ error: number;
22
+ warning: number;
23
+ info: number;
24
+ };
25
+ toJSON(): {
26
+ ok: boolean;
27
+ counts: {
28
+ error: number;
29
+ warning: number;
30
+ info: number;
31
+ };
32
+ diagnostics: {
33
+ code: string;
34
+ severity: Severity;
35
+ questionId: string | null;
36
+ message: string;
37
+ hint: string;
38
+ evidence: string;
39
+ }[];
40
+ };
41
+ format(color?: boolean): string;
42
+ }
43
+ /**
44
+ * Lint a jev request without calling the API.
45
+ *
46
+ * `questions` accepts SDK objects or plain objects. `state` is optional: omit it
47
+ * to check the questions alone, though the budget rules can only report
48
+ * meaningfully when the real state is supplied.
49
+ */
50
+ export declare function lint(questions: Record<string, unknown>, options?: LintOptions): LintResult;
51
+ //# sourceMappingURL=linter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAI5B,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAsB,MAAM,iBAAiB,CAAC;AAGhF,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,8DAA8D;AAC9D,qBAAa,UAAU;IACrB,QAAQ,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC;gBAEvB,WAAW,EAAE,UAAU,EAAE;IAIrC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC;IAIzC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,EAAE;IAI5C,IAAI,MAAM,IAAI,UAAU,EAAE,CAEzB;IAED,IAAI,QAAQ,IAAI,UAAU,EAAE,CAE3B;IAED,IAAI,KAAK,IAAI,UAAU,EAAE,CAExB;IAED,0CAA0C;IAC1C,IAAI,EAAE,IAAI,OAAO,CAEhB;IAED,MAAM,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAQ1D,MAAM;;;mBARa,MAAM;qBAAW,MAAM;kBAAQ,MAAM;;;;;;;;;;;IAgBxD,MAAM,CAAC,KAAK,UAAQ,GAAG,MAAM;CAI9B;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,OAAO,GAAE,WAAgB,GACxB,UAAU,CAWZ"}
package/dist/linter.js ADDED
@@ -0,0 +1,72 @@
1
+ /** The lint entry point. */
2
+ import { normalizeQuestions } from "jevkit-core";
3
+ import { compareDiagnostics } from "./diagnostic.js";
4
+ import { buildContext, rulesFor } from "./rules.js";
5
+ /** Diagnostics for one request, ordered most severe first. */
6
+ export class LintResult {
7
+ diagnostics;
8
+ constructor(diagnostics) {
9
+ this.diagnostics = [...diagnostics].sort(compareDiagnostics);
10
+ }
11
+ [Symbol.iterator]() {
12
+ return this.diagnostics[Symbol.iterator]();
13
+ }
14
+ get length() {
15
+ return this.diagnostics.length;
16
+ }
17
+ bySeverity(severity) {
18
+ return this.diagnostics.filter((d) => d.severity === severity);
19
+ }
20
+ get errors() {
21
+ return this.bySeverity("error");
22
+ }
23
+ get warnings() {
24
+ return this.bySeverity("warning");
25
+ }
26
+ get infos() {
27
+ return this.bySeverity("info");
28
+ }
29
+ /** True when nothing rose to an error. */
30
+ get ok() {
31
+ return this.errors.length === 0;
32
+ }
33
+ counts() {
34
+ return {
35
+ error: this.errors.length,
36
+ warning: this.warnings.length,
37
+ info: this.infos.length,
38
+ };
39
+ }
40
+ toJSON() {
41
+ return {
42
+ ok: this.ok,
43
+ counts: this.counts(),
44
+ diagnostics: this.diagnostics.map((d) => d.toJSON()),
45
+ };
46
+ }
47
+ format(color = false) {
48
+ if (!this.diagnostics.length)
49
+ return "No problems found.";
50
+ return this.diagnostics.map((d) => d.format(color)).join("\n");
51
+ }
52
+ }
53
+ /**
54
+ * Lint a jev request without calling the API.
55
+ *
56
+ * `questions` accepts SDK objects or plain objects. `state` is optional: omit it
57
+ * to check the questions alone, though the budget rules can only report
58
+ * meaningfully when the real state is supplied.
59
+ */
60
+ export function lint(questions, options = {}) {
61
+ const normalized = normalizeQuestions(questions);
62
+ const ctx = buildContext(options.state ?? "", normalized);
63
+ const ignored = new Set([...(options.ignore ?? [])].map((c) => c.toUpperCase()));
64
+ const found = [];
65
+ for (const rule of rulesFor(options.select)) {
66
+ if (ignored.has(rule.code))
67
+ continue;
68
+ found.push(...rule.check(ctx));
69
+ }
70
+ return new LintResult(found);
71
+ }
72
+ //# sourceMappingURL=linter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linter.js","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Lint rules.
3
+ *
4
+ * Every rule maps to a failure mode TypeSafe documents for `jev-1.13`
5
+ * (https://docs.typesafe.ai/model-jaggedness/jev-1.13) or to a structural
6
+ * mistake that makes an answer unusable. The `mode` field names the documented
7
+ * failure mode so the CLI can point at the source.
8
+ *
9
+ * Rules are static. They read your question definitions and never call the API,
10
+ * which is why jevkit-lint works without an API key.
11
+ */
12
+ import { type BudgetReport, type Question } from "jevkit-core";
13
+ import { Diagnostic } from "./diagnostic.js";
14
+ export interface LintContext {
15
+ questions: Question[];
16
+ state: unknown;
17
+ budget: BudgetReport;
18
+ }
19
+ export interface Rule {
20
+ code: string;
21
+ name: string;
22
+ mode: string;
23
+ check: (ctx: LintContext) => Diagnostic[];
24
+ }
25
+ export declare function buildContext(state: unknown, questions: Question[]): LintContext;
26
+ export declare const RULES: Rule[];
27
+ export declare function rulesFor(codes?: Iterable<string>): Rule[];
28
+ export declare function allCodes(): string[];
29
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,QAAQ,EAAe,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,UAAU,EAAE,CAAC;CAC3C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,WAAW,CAS/E;AAkbD,eAAO,MAAM,KAAK,EAAE,IAAI,EAmBvB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAIzD;AAED,wBAAgB,QAAQ,IAAI,MAAM,EAAE,CAEnC"}
package/dist/rules.js ADDED
@@ -0,0 +1,433 @@
1
+ /**
2
+ * Lint rules.
3
+ *
4
+ * Every rule maps to a failure mode TypeSafe documents for `jev-1.13`
5
+ * (https://docs.typesafe.ai/model-jaggedness/jev-1.13) or to a structural
6
+ * mistake that makes an answer unusable. The `mode` field names the documented
7
+ * failure mode so the CLI can point at the source.
8
+ *
9
+ * Rules are static. They read your question definitions and never call the API,
10
+ * which is why jevkit-lint works without an API key.
11
+ */
12
+ import { checkBudget } from "jevkit-core";
13
+ import { Diagnostic } from "./diagnostic.js";
14
+ export function buildContext(state, questions) {
15
+ // Budget estimation canonicalizes its input, so questions are reduced to
16
+ // plain JSON here. SDK objects may not be serializable, and the raw form is
17
+ // only needed by rules that read the normalized view anyway.
18
+ const plain = {};
19
+ for (const q of questions) {
20
+ plain[q.id] = { type: q.type, instructions: q.instructions, criteria: q.criteria };
21
+ }
22
+ return { questions, state, budget: checkBudget(state, plain) };
23
+ }
24
+ function find(patterns, text) {
25
+ for (const pattern of patterns) {
26
+ const match = new RegExp(pattern.source, pattern.flags.replace("g", "")).exec(text);
27
+ if (match)
28
+ return match[0].trim();
29
+ }
30
+ return null;
31
+ }
32
+ function findAll(pattern, text) {
33
+ return [...text.matchAll(new RegExp(pattern.source, pattern.flags + "g"))].map((m) => m[0]);
34
+ }
35
+ // -- JEV001 Math and Numbers ------------------------------------------------
36
+ const COUNT_PATTERNS = [
37
+ /\bhow many\b/i, /\bnumber of\b/i, /\bcount (?:the|of|how)\b/i, /\btally\b/i,
38
+ /\btotal (?:number|count|of)\b/i, /\bsum of\b/i, /\baverage\b/i, /\bmean of\b/i,
39
+ /\bcalculate\b/i, /\bcompute the\b/i, /\bpercentage of\b/i, /\bhow much (?:is|does)\b/i,
40
+ /\bmultipl(?:y|ied)\b/i, /\bdivided?\b/i, /\bsubtract\b/i,
41
+ ];
42
+ const checkMath = (ctx) => ctx.questions.flatMap((q) => {
43
+ const hit = find(COUNT_PATTERNS, q.text);
44
+ return hit
45
+ ? [new Diagnostic({
46
+ code: "JEV001", severity: "error", questionId: q.id,
47
+ message: "Question asks the model to count or do arithmetic.",
48
+ evidence: hit,
49
+ hint: "jev-1.13 is not a calculator and does not count reliably. Iterate the " +
50
+ "candidates in code, ask one Noul per item, and sum the answers yourself.",
51
+ })]
52
+ : [];
53
+ });
54
+ // -- JEV002 Date and time comparison ---------------------------------------
55
+ const DATE_PATTERNS = [
56
+ /\b(?:before|after|earlier than|later than|prior to)\b[^.?]{0,40}\b(?:date|day|month|year|deadline|timestamp)\b/i,
57
+ /\b(?:date|day|month|year|deadline|timestamp)\b[^.?]{0,40}\b(?:before|after|earlier|later)\b/i,
58
+ /\bhow (?:long|many days|many months|many years)\b/i,
59
+ /\bwithin \d+ (?:day|week|month|year)s?\b/i,
60
+ /\b(?:days|weeks|months|years) (?:between|apart|since|until)\b/i,
61
+ /\bmost recent\b/i, /\bchronologic(?:al|ally)\b/i,
62
+ /\b(?:which|what)\b[^.?]{0,30}\b(?:came|comes|happened|occurred|was|is)\s+(?:first|last|earliest|latest|more recent)\b/i,
63
+ /\b(?:earliest|latest|oldest|newest)\b[^.?]{0,20}\b(?:date|day|month|year|deadline|timestamp|event|entry)\b/i,
64
+ /\b(?:date|day|month|year|deadline|timestamp)\b[^.?]{0,20}\b(?:first|last|earliest|latest)\b/i,
65
+ /\bexpired?\b/i, /\boverdue\b/i, /\bin the (?:past|last|next) \d+\b/i,
66
+ ];
67
+ const checkDates = (ctx) => ctx.questions.flatMap((q) => {
68
+ const hit = find(DATE_PATTERNS, q.text);
69
+ return hit
70
+ ? [new Diagnostic({
71
+ code: "JEV002", severity: "error", questionId: q.id,
72
+ message: "Question compares or measures dates.",
73
+ evidence: hit,
74
+ hint: "jev-1.13 reads dates as text, not ordered quantities. Extract the parts " +
75
+ "as Choices over closed sets (12 months, 31 days, a bounded year range, " +
76
+ "plus an explicit 'not stated'), then order and subtract in code.",
77
+ })]
78
+ : [];
79
+ });
80
+ // -- JEV003 Generation ------------------------------------------------------
81
+ const GENERATION_PATTERNS = [
82
+ /\b(?:write|draft|compose|author) (?:a|an|the|some)\b/i,
83
+ /\b(?:generate|produce|create) (?:a|an|the)\s+(?:summary|response|reply|message|list|description|explanation|text|paragraph)\b/i,
84
+ /\bsummari[sz]e\b/i, /\bparaphrase\b/i, /\brephrase\b/i, /\brewrite\b/i,
85
+ /\bexplain (?:why|how|what)\b/i, /\bdescribe (?:in|the|what|how)\b/i,
86
+ /\bin your own words\b/i,
87
+ /\bprovide (?:a|an) (?:summary|explanation|rationale|reason)\b/i,
88
+ /\bgive (?:a|an) (?:reason|explanation|rationale)\b/i, /\btranslate\b/i,
89
+ ];
90
+ const checkGeneration = (ctx) => ctx.questions.flatMap((q) => {
91
+ const hit = find(GENERATION_PATTERNS, q.instructionsText);
92
+ return hit
93
+ ? [new Diagnostic({
94
+ code: "JEV003", severity: "error", questionId: q.id,
95
+ message: "Question asks the model to generate text.",
96
+ evidence: hit,
97
+ hint: "Jev returns typed answers and probabilities, never generated text. Use a " +
98
+ "generative model for this, or restate it as a selection over candidates " +
99
+ "your code already has.",
100
+ })]
101
+ : [];
102
+ });
103
+ // -- JEV004 Literal reading: negation --------------------------------------
104
+ const NEGATIONS = /\b(?:not|never|no|none|neither|nor|without|except|unless|excluding|absent|lacks?|fails? to|cannot|can't|doesn't|does not|isn't|is not|aren't|won't)\b/i;
105
+ const checkNegation = (ctx) => ctx.questions.flatMap((q) => {
106
+ const hits = findAll(NEGATIONS, q.instructionsText);
107
+ if (hits.length >= 2) {
108
+ const unique = [...new Set(hits.map((h) => h.toLowerCase()))].sort();
109
+ return [new Diagnostic({
110
+ code: "JEV004", severity: "warning", questionId: q.id,
111
+ message: `Instructions contain ${hits.length} negations, which compounds into a double negative.`,
112
+ evidence: unique.join(", "),
113
+ hint: "jev-1.13 reads negations literally and loses accuracy on double negatives. " +
114
+ "Restate positively, or split into two literal questions and combine them in code.",
115
+ })];
116
+ }
117
+ if (hits.length === 1 && q.type === "noul") {
118
+ return [new Diagnostic({
119
+ code: "JEV004", severity: "info", questionId: q.id,
120
+ message: "Noul instruction is phrased negatively.",
121
+ evidence: hits[0].toLowerCase(),
122
+ hint: "A Noul returns the probability the statement is true. A negative statement " +
123
+ "inverts the reading of every threshold downstream. Prefer the positive form " +
124
+ "and invert in code if you need it.",
125
+ })];
126
+ }
127
+ return [];
128
+ });
129
+ // -- JEV005 Literal reading: vague scoping ---------------------------------
130
+ const VAGUE = /\b(?:relevant|appropriate|important|significant|suitable|proper|good|bad|better|reasonable|acceptable|sufficient|adequate|meaningful|noteworthy|problematic)\b/i;
131
+ const checkVague = (ctx) => ctx.questions.flatMap((q) => {
132
+ const found = [...new Set(findAll(VAGUE, q.instructionsText).map((h) => h.toLowerCase()))].sort();
133
+ if (found.length && !q.criteriaText.trim()) {
134
+ return [new Diagnostic({
135
+ code: "JEV005", severity: "warning", questionId: q.id,
136
+ message: "Instructions lean on an undefined evaluative word and give no criteria to pin it down.",
137
+ evidence: found.join(", "),
138
+ hint: "jev-1.13 answers the question you wrote, not the one you meant. Define what " +
139
+ "the word means here in the criteria, including the boundary cases.",
140
+ })];
141
+ }
142
+ if (found.length >= 2) {
143
+ return [new Diagnostic({
144
+ code: "JEV005", severity: "info", questionId: q.id,
145
+ message: "Instructions stack several undefined evaluative words.",
146
+ evidence: found.join(", "),
147
+ hint: "Each one is a separate judgment the model has to guess at. Name the exact " +
148
+ "condition, or split into separate questions.",
149
+ })];
150
+ }
151
+ return [];
152
+ });
153
+ // -- JEV006 Indirection -----------------------------------------------------
154
+ const INDIRECTION_PATTERNS = [
155
+ /\bthe \w+ of the \w+ of the\b/i,
156
+ /\bwhoever\b[^.?]{0,40}\bwhose\b/i,
157
+ /\bif .{0,60}\bthen\b.{0,60}\bif\b/i,
158
+ /\bwould have (?:been|had)\b/i,
159
+ /\bimplies? that\b[^.?]{0,40}\bwhich\b/i,
160
+ /\bindirectly\b/i, /\btransitively\b/i,
161
+ ];
162
+ const checkIndirection = (ctx) => ctx.questions.flatMap((q) => {
163
+ const hit = find(INDIRECTION_PATTERNS, q.instructionsText);
164
+ return hit
165
+ ? [new Diagnostic({
166
+ code: "JEV006", severity: "warning", questionId: q.id,
167
+ message: "Instructions require multiple hops of reasoning.",
168
+ evidence: hit,
169
+ hint: "Every hop costs accuracy. Resolve the intermediate step in code and name " +
170
+ "the resulting state field directly in the instruction.",
171
+ })]
172
+ : [];
173
+ });
174
+ // -- JEV007 Noul polarity ---------------------------------------------------
175
+ const FALSEY = ["no", "false", "absent", "none", "negative", "not present", "does not", "fails"];
176
+ const TRUTHY = ["yes", "true", "present", "positive", "does", "passes"];
177
+ const checkNoulPolarity = (ctx) => ctx.questions.flatMap((q) => {
178
+ const c = q.criteria;
179
+ if (q.type !== "noul" || !c || typeof c !== "object" || Array.isArray(c))
180
+ return [];
181
+ const crit = c;
182
+ const trueSide = String(crit["true"] ?? "").toLowerCase();
183
+ const falseSide = String(crit["false"] ?? "").toLowerCase();
184
+ if (!trueSide && !falseSide)
185
+ return [];
186
+ const inverted = FALSEY.some((t) => trueSide.includes(t)) && TRUTHY.some((t) => falseSide.includes(t));
187
+ return inverted
188
+ ? [new Diagnostic({
189
+ code: "JEV007", severity: "error", questionId: q.id,
190
+ message: "Noul criteria invert polarity: 'true' describes a no and 'false' describes a yes.",
191
+ evidence: `true=${JSON.stringify(trueSide)} false=${JSON.stringify(falseSide)}`,
192
+ hint: "TypeSafe documents this exact shape as a performance loss. Swap the two " +
193
+ "descriptions and rewrite the instruction so 'true' means yes.",
194
+ })]
195
+ : [];
196
+ });
197
+ // -- JEV008 / JEV009 / JEV010 Choice structure -----------------------------
198
+ const NO_MATCH = new Set([
199
+ "none", "none_of_the_above", "no_match", "nomatch", "other", "unknown", "unclear",
200
+ "not_stated", "not_applicable", "n_a", "na", "neither", "cannot_tell", "insufficient",
201
+ "uncertain", "ambiguous", "not_specified",
202
+ ]);
203
+ const checkChoiceNoMatch = (ctx) => ctx.questions.flatMap((q) => {
204
+ if (q.type !== "choice")
205
+ return [];
206
+ const keys = q.options.map((k) => k.toLowerCase().replace(/[- ]/g, "_"));
207
+ if (!keys.length || keys.some((k) => NO_MATCH.has(k)))
208
+ return [];
209
+ return [new Diagnostic({
210
+ code: "JEV008", severity: "warning", questionId: q.id,
211
+ message: "Choice has no no-match option.",
212
+ evidence: [...keys].sort().join(", "),
213
+ hint: "A Choice always returns one of its options. With nothing meaning 'none of " +
214
+ "these', a state that fits no option still produces a confident-looking answer. " +
215
+ "Add an explicit none/unknown option, or gate on a separate presence Noul.",
216
+ })];
217
+ });
218
+ const checkChoiceArity = (ctx) => ctx.questions.flatMap((q) => {
219
+ if (q.type !== "choice")
220
+ return [];
221
+ const n = q.options.length;
222
+ if (n === 0) {
223
+ return [new Diagnostic({
224
+ code: "JEV009", severity: "error", questionId: q.id,
225
+ message: "Choice defines no options.",
226
+ hint: "Give the Choice a criteria object mapping each option key to a description " +
227
+ "of when it applies.",
228
+ })];
229
+ }
230
+ if (n === 1) {
231
+ return [new Diagnostic({
232
+ code: "JEV009", severity: "error", questionId: q.id,
233
+ message: "Choice defines a single option, so the answer is predetermined.",
234
+ evidence: q.options[0],
235
+ hint: "Use a Noul if the question is really yes/no, or add the competing options.",
236
+ })];
237
+ }
238
+ return [];
239
+ });
240
+ const checkEmptyDescriptions = (ctx) => ctx.questions.flatMap((q) => {
241
+ if (q.type !== "choice")
242
+ return [];
243
+ const blank = q.optionDescriptions().filter(([, d]) => !d.trim()).map(([k]) => k);
244
+ return blank.length
245
+ ? [new Diagnostic({
246
+ code: "JEV010", severity: "warning", questionId: q.id,
247
+ message: "Choice options have no description.",
248
+ evidence: blank.join(", "),
249
+ hint: "The option key alone is all the model gets. Describe when each option " +
250
+ "applies, especially the boundary against its nearest rival.",
251
+ })]
252
+ : [];
253
+ });
254
+ // -- JEV011 Score structure -------------------------------------------------
255
+ const checkScoreLevels = (ctx) => ctx.questions.flatMap((q) => {
256
+ if (q.type !== "score")
257
+ return [];
258
+ const levels = q.options;
259
+ if (levels.length < 2) {
260
+ return [new Diagnostic({
261
+ code: "JEV011", severity: "error", questionId: q.id,
262
+ message: `Score defines ${levels.length} level(s); at least 2 are needed to form a scale.`,
263
+ hint: "Give each level a concrete description of the situation it covers.",
264
+ })];
265
+ }
266
+ const terse = levels.filter((lv) => lv.trim().split(/\s+/).length < 2);
267
+ return terse.length
268
+ ? [new Diagnostic({
269
+ code: "JEV011", severity: "warning", questionId: q.id,
270
+ message: "Score levels are bare labels rather than descriptions.",
271
+ evidence: terse.join(", "),
272
+ hint: "Levels must describe concrete situations and stand on their own. " +
273
+ "'Frustrated but civil' works; 'medium' does not.",
274
+ })]
275
+ : [];
276
+ });
277
+ // -- JEV012 Instructions present -------------------------------------------
278
+ const checkInstructions = (ctx) => ctx.questions.flatMap((q) => {
279
+ const text = q.instructionsText.trim();
280
+ if (!text) {
281
+ return [new Diagnostic({
282
+ code: "JEV012", severity: "error", questionId: q.id,
283
+ message: "Question has no instructions.",
284
+ hint: "The instruction carries the judgment. Without it the model only has the " +
285
+ "criteria to go on.",
286
+ })];
287
+ }
288
+ if (text.split(/\s+/).length < 3) {
289
+ return [new Diagnostic({
290
+ code: "JEV012", severity: "warning", questionId: q.id,
291
+ message: "Instructions are too terse to state a condition.",
292
+ evidence: text,
293
+ hint: "State the exact condition being judged, in language an average reader would " +
294
+ "resolve the same way you do.",
295
+ })];
296
+ }
297
+ return [];
298
+ });
299
+ // -- JEV013 Question ids are not sent to the model --------------------------
300
+ function escapeRe(s) {
301
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
302
+ }
303
+ const checkIdReference = (ctx) => {
304
+ const ids = ctx.questions.map((q) => q.id);
305
+ return ctx.questions.flatMap((q) => {
306
+ const text = q.instructionsText;
307
+ const referenced = ids
308
+ .filter((other) => other !== q.id)
309
+ .filter((other) => new RegExp(`(?<![\\w.\`])${escapeRe(other)}(?![\\w\`])`).test(text))
310
+ .sort();
311
+ return referenced.length
312
+ ? [new Diagnostic({
313
+ code: "JEV013", severity: "warning", questionId: q.id,
314
+ message: "Instructions refer to another question by its id.",
315
+ evidence: referenced.join(", "),
316
+ hint: "Question ids are for your code and are not sent to the model. Questions in " +
317
+ "one request are answered in parallel and cannot see each other. State the " +
318
+ "premise explicitly, or split into a second request.",
319
+ })]
320
+ : [];
321
+ });
322
+ };
323
+ // -- JEV014 / JEV015 Budgets ------------------------------------------------
324
+ const checkStateBudget = (ctx) => ctx.budget.overState
325
+ ? [new Diagnostic({
326
+ code: "JEV014", severity: "error", questionId: null,
327
+ message: `state plus the longest question is about ${ctx.budget.longestPair.toLocaleString("en-US")} tokens, over the 32,000 limit.`,
328
+ evidence: `longest question: ${ctx.budget.longestQuestionId}`,
329
+ hint: "Retrieve and filter in code so the state carries only the fields this " +
330
+ "judgment needs. Estimates are approximate; leave headroom.",
331
+ })]
332
+ : [];
333
+ const checkTotalBudget = (ctx) => ctx.budget.overTotal
334
+ ? [new Diagnostic({
335
+ code: "JEV015", severity: "error", questionId: null,
336
+ message: `state plus all ${ctx.questions.length} questions is about ${ctx.budget.total.toLocaleString("en-US")} tokens, over the 64,000 limit.`,
337
+ hint: "Split into several requests, or drop speculative questions that this state " +
338
+ "can never make relevant.",
339
+ })]
340
+ : [];
341
+ // -- JEV016 Noisy state -----------------------------------------------------
342
+ const LARGE_STATE_RATIO = 8;
343
+ const LARGE_STATE_FLOOR = 4_000;
344
+ const checkStateSize = (ctx) => {
345
+ const b = ctx.budget;
346
+ if (b.overState || b.overTotal)
347
+ return []; // Already an error; don't double up.
348
+ const questionTokens = Object.values(b.questionTokens).reduce((a, c) => a + c, 0) || 1;
349
+ const ratio = b.stateTokens / questionTokens;
350
+ if (b.stateTokens >= LARGE_STATE_FLOOR && ratio >= LARGE_STATE_RATIO) {
351
+ return [new Diagnostic({
352
+ code: "JEV016", severity: "info", questionId: null,
353
+ message: `state is about ${b.stateTokens.toLocaleString("en-US")} tokens against ${questionTokens.toLocaleString("en-US")} tokens of questions (${Math.round(ratio)}x).`,
354
+ hint: "Accuracy falls as the state grows with content unrelated to the decision, and " +
355
+ "a large state makes a wrong answer hard to attribute. Filter in code first, or " +
356
+ "use a Noul to screen for relevance.",
357
+ })];
358
+ }
359
+ return [];
360
+ };
361
+ // -- JEV017 Numeric representations ----------------------------------------
362
+ const NUMERIC_REPR = [
363
+ /#[0-9a-fA-F]{6}\b/, /\brgba?\s*\(/i, /\bhex(?:adecimal)? (?:value|code|colou?r)\b/i,
364
+ /\bbinary (?:value|encoding|representation)\b/i, /\bbase64\b/i,
365
+ /\bassembly (?:instruction|opcode)\b/i, /\bopcode\b/i, /\bbytecode\b/i,
366
+ ];
367
+ const checkNumericRepr = (ctx) => ctx.questions.flatMap((q) => {
368
+ const hit = find(NUMERIC_REPR, q.text);
369
+ return hit
370
+ ? [new Diagnostic({
371
+ code: "JEV017", severity: "warning", questionId: q.id,
372
+ message: "Question reasons over a machine-oriented numeric representation.",
373
+ evidence: hit,
374
+ hint: "jev-1.13 does better on semantic representations than numeric ones: colour " +
375
+ "names beat hex, high-level code beats bytecode. Convert in code and pass " +
376
+ "the name or a named bucket.",
377
+ })]
378
+ : [];
379
+ });
380
+ // -- JEV018 Duplicate judgments --------------------------------------------
381
+ const checkDuplicates = (ctx) => {
382
+ const seen = new Map();
383
+ const out = [];
384
+ for (const q of ctx.questions) {
385
+ const key = q.text.toLowerCase().replace(/\W+/g, " ").trim();
386
+ if (!key)
387
+ continue;
388
+ const prior = seen.get(key);
389
+ if (prior !== undefined) {
390
+ out.push(new Diagnostic({
391
+ code: "JEV018", severity: "info", questionId: q.id,
392
+ message: `Question is textually identical to "${prior}".`,
393
+ hint: "Identical questions in one request cost tokens twice for the same answer. If " +
394
+ "you meant to measure self-consistency, note that they are evaluated in one " +
395
+ "pass and are not independent samples.",
396
+ }));
397
+ }
398
+ else {
399
+ seen.set(key, q.id);
400
+ }
401
+ }
402
+ return out;
403
+ };
404
+ export const RULES = [
405
+ { code: "JEV001", name: "math-and-counting", mode: "Math and Numbers", check: checkMath },
406
+ { code: "JEV002", name: "date-comparison", mode: "Date and time comparison", check: checkDates },
407
+ { code: "JEV003", name: "generation-request", mode: "Generation", check: checkGeneration },
408
+ { code: "JEV004", name: "negation", mode: "Literal reading", check: checkNegation },
409
+ { code: "JEV005", name: "vague-scoping", mode: "Literal reading", check: checkVague },
410
+ { code: "JEV006", name: "indirection", mode: "Indirection", check: checkIndirection },
411
+ { code: "JEV007", name: "noul-polarity", mode: "Contradictory instructions and criteria", check: checkNoulPolarity },
412
+ { code: "JEV008", name: "choice-no-match", mode: "Common-sense structural invariants", check: checkChoiceNoMatch },
413
+ { code: "JEV009", name: "choice-arity", mode: "Common-sense structural invariants", check: checkChoiceArity },
414
+ { code: "JEV010", name: "option-descriptions", mode: "Literal reading", check: checkEmptyDescriptions },
415
+ { code: "JEV011", name: "score-levels", mode: "Literal reading", check: checkScoreLevels },
416
+ { code: "JEV012", name: "instructions-present", mode: "Literal reading", check: checkInstructions },
417
+ { code: "JEV013", name: "question-id-reference", mode: "Indirection", check: checkIdReference },
418
+ { code: "JEV014", name: "state-budget", mode: "Large state full of irrelevant detail", check: checkStateBudget },
419
+ { code: "JEV015", name: "total-budget", mode: "Large state full of irrelevant detail", check: checkTotalBudget },
420
+ { code: "JEV016", name: "state-noise-ratio", mode: "Large state full of irrelevant detail", check: checkStateSize },
421
+ { code: "JEV017", name: "numeric-representation", mode: "Math and Numbers", check: checkNumericRepr },
422
+ { code: "JEV018", name: "duplicate-questions", mode: "Common-sense structural invariants", check: checkDuplicates },
423
+ ];
424
+ export function rulesFor(codes) {
425
+ if (!codes)
426
+ return [...RULES];
427
+ const wanted = new Set([...codes].map((c) => c.toUpperCase()));
428
+ return RULES.filter((r) => wanted.has(r.code));
429
+ }
430
+ export function allCodes() {
431
+ return RULES.map((r) => r.code);
432
+ }
433
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "jevkit-lint",
3
+ "version": "0.1.0",
4
+ "description": "Static linter for TypeSafe Jev questions. Catches the documented jev-1.13 failure modes before you spend a token. No API key required.",
5
+ "keywords": [
6
+ "jev",
7
+ "typesafe",
8
+ "system-one",
9
+ "lint",
10
+ "linter",
11
+ "static-analysis"
12
+ ],
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "bin": {
24
+ "jevkit-lint": "./dist/cli.js"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/pjdurden/jevkit-js.git"
36
+ },
37
+ "dependencies": {
38
+ "jevkit-core": "^0.2.0"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc -b"
42
+ }
43
+ }