vigiles 2.3.0 → 2.5.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/dist/spec.js CHANGED
@@ -23,6 +23,10 @@ exports.project = project;
23
23
  exports.input = input;
24
24
  exports.step = step;
25
25
  exports.skill = skill;
26
+ exports.agent = agent;
27
+ exports.result = result;
28
+ exports.delegate = delegate;
29
+ exports.railway = railway;
26
30
  exports.defineConfig = defineConfig;
27
31
  // ---------------------------------------------------------------------------
28
32
  // Builder functions
@@ -154,6 +158,57 @@ function step(instr, opts = {}) {
154
158
  function skill(spec) {
155
159
  return { _specType: "skill", ...spec };
156
160
  }
161
+ /**
162
+ * Define a subagent specification (compiles to `agents/<name>.md`).
163
+ *
164
+ * // agents/reviewer.md.spec.ts
165
+ * export default agent({
166
+ * name: "reviewer",
167
+ * description: "Review a diff for correctness. Dispatch PROACTIVELY after edits.",
168
+ * model: "sonnet",
169
+ * tools: ["Read", "Grep", "Bash"],
170
+ * body: instructions`Review the diff. Run ${cmd("npm test")} first.`,
171
+ * rules: {
172
+ * "no-floating": enforce("@typescript-eslint/no-floating-promises", "Await promises."),
173
+ * },
174
+ * });
175
+ */
176
+ function agent(spec) {
177
+ return { _specType: "agent", ...spec };
178
+ }
179
+ /**
180
+ * Declare a subagent's success/error result contract.
181
+ *
182
+ * result(
183
+ * { files: "string[]", summary: "string" }, // rich success
184
+ * { reason: "string", retryable: "boolean" }, // rich error
185
+ * )
186
+ *
187
+ * (Distinct from a skill's `result:` postcondition gate — this types a
188
+ * subagent's *return value*, the success/error tracks of the railway.)
189
+ */
190
+ function result(ok, err) {
191
+ return { _ref: "output", ok, err };
192
+ }
193
+ /** Build a railway step that dispatches `agent` (optionally with a task hint). */
194
+ function delegate(agent, task) {
195
+ return task === undefined
196
+ ? { _step: "delegate", agent }
197
+ : { _step: "delegate", agent, task };
198
+ }
199
+ /**
200
+ * Compose flat subagents into a railway (compiles to an orchestrator command).
201
+ *
202
+ * railway({
203
+ * name: "ship",
204
+ * steps: [delegate("planner"), delegate("coder"), delegate("reviewer")],
205
+ * onError: delegate("reporter"),
206
+ * recover: { step: delegate("fixer"), max: 2 },
207
+ * })
208
+ */
209
+ function railway(spec) {
210
+ return { _specType: "railway", ...spec };
211
+ }
157
212
  function defineConfig(config) {
158
213
  return config;
159
214
  }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * vigiles — significance testing for eval A/B arms.
3
+ *
4
+ * The eval tier already reports mean ± se per arm; this answers the question that
5
+ * `assertImproves(..., { by: se })` punted to the user: is the gap between two
6
+ * arms real, or noise? A Welch's t-test over the per-arm summary stats (mean, se,
7
+ * n) — no raw rows needed — yields a two-sided p-value and a significance verdict.
8
+ * Pure + model-free, so it's fully unit-tested against known t-table values.
9
+ *
10
+ * For 0/1 (proportion) metrics this is the t approximation to the two-proportion
11
+ * test — close at the trial counts evals use, and one code path for any metric.
12
+ * The numerics (log-gamma, incomplete beta) are specialized to the argument range
13
+ * these tests produce (a, b ≥ 0.5; x ∈ (0,1)); they are not a general library.
14
+ */
15
+ import type { EvalReport } from "./eval.js";
16
+ /** Regularized incomplete beta I_x(a, b) ∈ [0, 1]. */
17
+ export declare function regularizedIncompleteBeta(a: number, b: number, x: number): number;
18
+ /** Two-sided p-value for Student's t with `df` degrees of freedom. */
19
+ export declare function tPValueTwoSided(t: number, df: number): number;
20
+ /** The verdict on one arm-vs-baseline comparison for a single metric. */
21
+ export interface Comparison {
22
+ /** mean(arm) − mean(baseline). */
23
+ readonly delta: number;
24
+ /** Combined standard error of the difference. */
25
+ readonly seDelta: number;
26
+ /** Welch t statistic (delta / seDelta). */
27
+ readonly t: number;
28
+ /** Welch–Satterthwaite degrees of freedom. */
29
+ readonly df: number;
30
+ /** Two-sided p-value for the difference. */
31
+ readonly pValue: number;
32
+ /** p < alpha — the difference is unlikely to be noise. */
33
+ readonly significant: boolean;
34
+ }
35
+ type Summary = {
36
+ readonly mean: number;
37
+ readonly se: number;
38
+ readonly n: number;
39
+ };
40
+ /** Welch's unequal-variance t-test between two arms' summary stats. */
41
+ export declare function welchTTest(arm: Summary, baseline: Summary, alpha?: number): Comparison;
42
+ /**
43
+ * Compare two arms on a metric using their reported summary stats, or null if
44
+ * either arm/metric is absent. The grounded form of `assertImproves`'s `by`: it
45
+ * computes the noise floor instead of asking the caller to supply it.
46
+ */
47
+ export declare function compareArms(report: EvalReport, baseline: string, arm: string, metric: string, alpha?: number): Comparison | null;
48
+ export {};
49
+ //# sourceMappingURL=stats.d.ts.map
package/dist/stats.js ADDED
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.regularizedIncompleteBeta = regularizedIncompleteBeta;
4
+ exports.tPValueTwoSided = tPValueTwoSided;
5
+ exports.welchTTest = welchTTest;
6
+ exports.compareArms = compareArms;
7
+ // Lanczos coefficients (g = 7) for log-gamma; sufficient for the beta args here.
8
+ const LANCZOS = [
9
+ 676.5203681218851, -1259.1392167224028, 771.32342877765313,
10
+ -176.61502916214059, 12.507343278686905, -0.13857109526572012,
11
+ 9.9843695780195716e-6, 1.5056327351493116e-7,
12
+ ];
13
+ /** Log-gamma via Lanczos. Valid for x ≥ 0.5 (all args used below satisfy this). */
14
+ function lgamma(x) {
15
+ const g = 7;
16
+ const xm1 = x - 1;
17
+ const base = LANCZOS.reduce((acc, c, i) => acc + c / (xm1 + i + 1), 0.99999999999980993);
18
+ const tt = xm1 + g + 0.5;
19
+ return (0.5 * Math.log(2 * Math.PI) +
20
+ (xm1 + 0.5) * Math.log(tt) -
21
+ tt +
22
+ Math.log(base));
23
+ }
24
+ /** Continued fraction for the incomplete beta (Numerical Recipes betacf). */
25
+ function betacf(a, b, x) {
26
+ const MAXIT = 200;
27
+ const EPS = 1e-12;
28
+ const qab = a + b;
29
+ const qap = a + 1;
30
+ const qam = a - 1;
31
+ let c = 1;
32
+ let d = 1 / (1 - (qab * x) / qap);
33
+ let h = d;
34
+ for (let m = 1; m <= MAXIT; m++) {
35
+ const m2 = 2 * m;
36
+ let aa = (m * (b - m) * x) / ((qam + m2) * (a + m2));
37
+ d = 1 / (1 + aa * d);
38
+ c = 1 + aa / c;
39
+ h *= d * c;
40
+ aa = (-(a + m) * (qab + m) * x) / ((a + m2) * (qap + m2));
41
+ d = 1 / (1 + aa * d);
42
+ c = 1 + aa / c;
43
+ const del = d * c;
44
+ h *= del;
45
+ if (Math.abs(del - 1) < EPS)
46
+ break;
47
+ }
48
+ return h;
49
+ }
50
+ /** Regularized incomplete beta I_x(a, b) ∈ [0, 1]. */
51
+ function regularizedIncompleteBeta(a, b, x) {
52
+ if (x <= 0)
53
+ return 0;
54
+ if (x >= 1)
55
+ return 1;
56
+ const front = Math.exp(lgamma(a + b) -
57
+ lgamma(a) -
58
+ lgamma(b) +
59
+ a * Math.log(x) +
60
+ b * Math.log(1 - x));
61
+ return x < (a + 1) / (a + b + 2)
62
+ ? (front * betacf(a, b, x)) / a
63
+ : 1 - (front * betacf(b, a, 1 - x)) / b;
64
+ }
65
+ /** Two-sided p-value for Student's t with `df` degrees of freedom. */
66
+ function tPValueTwoSided(t, df) {
67
+ if (df <= 0)
68
+ return 1;
69
+ return regularizedIncompleteBeta(df / 2, 0.5, df / (df + t * t));
70
+ }
71
+ // Variance contribution of one arm to the Welch df denominator. Guarded by v > 0
72
+ // (se > 0 ⇒ n ≥ 2, so n − 1 ≥ 1); a deterministic arm (se = 0) contributes 0.
73
+ const dfTerm = (v, n) => v > 0 ? (v * v) / (n - 1) : 0;
74
+ /** Welch's unequal-variance t-test between two arms' summary stats. */
75
+ function welchTTest(arm, baseline, alpha = 0.05) {
76
+ const delta = arm.mean - baseline.mean;
77
+ const va = arm.se ** 2;
78
+ const vb = baseline.se ** 2;
79
+ const seDelta = Math.sqrt(va + vb);
80
+ if (seDelta === 0) {
81
+ // Both arms are deterministic: significant iff they differ at all.
82
+ const significant = delta !== 0;
83
+ return {
84
+ delta,
85
+ seDelta,
86
+ t: 0,
87
+ df: 0,
88
+ pValue: significant ? 0 : 1,
89
+ significant,
90
+ };
91
+ }
92
+ const t = delta / seDelta;
93
+ const df = (va + vb) ** 2 / (dfTerm(va, arm.n) + dfTerm(vb, baseline.n));
94
+ const pValue = tPValueTwoSided(t, df);
95
+ return { delta, seDelta, t, df, pValue, significant: pValue < alpha };
96
+ }
97
+ /**
98
+ * Compare two arms on a metric using their reported summary stats, or null if
99
+ * either arm/metric is absent. The grounded form of `assertImproves`'s `by`: it
100
+ * computes the noise floor instead of asking the caller to supply it.
101
+ */
102
+ function compareArms(report, baseline, arm, metric, alpha = 0.05) {
103
+ const a = report.arms[arm]?.stats[metric];
104
+ const b = report.arms[baseline]?.stats[metric];
105
+ if (!a || !b)
106
+ return null;
107
+ return welchTTest(a, b, alpha);
108
+ }
109
+ //# sourceMappingURL=stats.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigiles",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Compile .spec.ts files to instruction files (CLAUDE.md, AGENTS.md) with linter cross-referencing",
5
5
  "bin": {
6
6
  "vigiles": "dist/cli.js"
@@ -17,6 +17,7 @@
17
17
  "./harness-assert": "./dist/harness-assert.js",
18
18
  "./run-hook": "./dist/run-hook.js",
19
19
  "./plugin-loader": "./dist/plugin-loader.js",
20
+ "./mcp": "./dist/mcp.js",
20
21
  "./judge": "./dist/judge.js",
21
22
  "./mock-model": "./dist/mock-model.js",
22
23
  "./vitest": {
@@ -39,14 +40,16 @@
39
40
  ],
40
41
  "scripts": {
41
42
  "build": "tsc",
42
- "test": "npm run build && node --test dist/spec.test.js dist/validate.test.js dist/cli.test.js dist/proofs.test.js dist/inline.test.js dist/sidecar.test.js dist/coverage.test.js dist/session.test.js dist/orphans.test.js dist/cedar.test.js dist/doc-refs.test.js dist/frontmatter.test.js dist/skill-pipeline.test.js dist/skill-runtime.test.js dist/skill-driver.test.js dist/skill-test.test.js dist/compile-generator.test.js dist/community-skills.test.js dist/action-gate.test.js dist/symbols.test.js dist/refs.test.js dist/harness-test.test.js dist/eval.test.js dist/run-scripts.test.js dist/plugin-loader.test.js dist/harness-assert.test.js dist/judge.test.js dist/run-hook.test.js",
43
+ "test": "npm run build && vitest run",
44
+ "coverage": "npm run build && vitest run --coverage",
43
45
  "lint": "eslint src/",
44
46
  "fmt": "prettier --write .",
45
47
  "fmt:check": "prettier --check .",
48
+ "demo": "npm run build && bash examples/demo/run.sh",
46
49
  "test:e2e": "bash test/e2e/run.sh",
47
50
  "test:harness": "npm run build && node dist/cli.js test",
48
51
  "test:eval": "npm run build && node dist/cli.js eval",
49
- "test:vitest": "npm run build && vitest run",
52
+ "test:vitest": "npm run build && vitest run --project runners",
50
53
  "test:jest": "npm run build && jest",
51
54
  "test:types": "npm run build && tsc --noEmit -p test/types/tsconfig.json"
52
55
  },
@@ -57,6 +60,7 @@
57
60
  "@types/node": "^20.19.39",
58
61
  "@typescript-eslint/eslint-plugin": "^8.58.0",
59
62
  "@typescript-eslint/parser": "^8.58.0",
63
+ "@vitest/coverage-v8": "^4.1.8",
60
64
  "eslint": "^10.1.0",
61
65
  "eslint-plugin-sonarjs": "^4.0.2",
62
66
  "globals": "^17.4.0",