vigiles 15.3.0 → 15.4.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/adapters/claude-code/skill-driver.d.ts +3 -0
- package/dist/adapters/claude-code/skill-driver.js +3 -0
- package/dist/adapters/claude-code/typed-spec.d.ts +1 -1
- package/dist/adapters/claude-code/typed-spec.js +3 -3
- package/dist/claude-code.d.ts +1 -1
- package/dist/claude-code.js +12 -6
- package/dist/core/adopt.d.ts +1 -1
- package/dist/core/adopt.js +4 -4
- package/dist/core/compile-generator.d.ts +9 -0
- package/dist/core/compile-generator.js +9 -0
- package/dist/core/spec.d.ts +26 -20
- package/dist/core/spec.js +34 -26
- package/dist/e2e.d.ts +3 -1
- package/dist/e2e.js +3 -1
- package/dist/experimental.d.ts +1 -1
- package/dist/experimental.js +2 -2
- package/dist/harness-assert.d.ts +1 -1
- package/dist/harness-assert.js +3 -2
- package/dist/services-docker.d.ts +1 -1
- package/dist/services-docker.js +3 -3
- package/dist/skill-test.d.ts +5 -0
- package/dist/skill-test.js +5 -0
- package/dist/testing.d.ts +8 -2
- package/dist/testing.js +9 -10
- package/dist/unit.d.ts +17 -2
- package/dist/unit.js +17 -2
- package/package.json +3 -1
- package/dist/mutations.d.ts +0 -96
- package/dist/mutations.js +0 -237
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vigiles — Skill driver: the generator (durable-imperative) form of a skill.
|
|
3
3
|
*
|
|
4
|
+
* PARKED (2026-08-16) — see the header of `src/core/compile-generator.ts`.
|
|
5
|
+
* Exported from no package subpath; absent from `docs/skills.md` on purpose.
|
|
6
|
+
*
|
|
4
7
|
* The declarative `skill({ steps })` form compiles to a *static* SKILL.md — it
|
|
5
8
|
* can't express branching/looping, because that control flow depends on runtime
|
|
6
9
|
* values. The generator form does: a skill is a generator that `yield`s
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* vigiles — Skill driver: the generator (durable-imperative) form of a skill.
|
|
4
4
|
*
|
|
5
|
+
* PARKED (2026-08-16) — see the header of `src/core/compile-generator.ts`.
|
|
6
|
+
* Exported from no package subpath; absent from `docs/skills.md` on purpose.
|
|
7
|
+
*
|
|
5
8
|
* The declarative `skill({ steps })` form compiles to a *static* SKILL.md — it
|
|
6
9
|
* can't express branching/looping, because that control flow depends on runtime
|
|
7
10
|
* values. The generator form does: a skill is a generator that `yield`s
|
|
@@ -54,5 +54,5 @@ export declare function agent<const P extends AuthoredPurity | undefined = undef
|
|
|
54
54
|
* against the Claude Code tool catalog. Identical to the core `skill()` at
|
|
55
55
|
* runtime; the typed `tools` constraint is the only difference.
|
|
56
56
|
*/
|
|
57
|
-
export declare function
|
|
57
|
+
export declare function experimental_skill<const P extends AuthoredPurity | undefined = undefined>(spec: SkillSpecInput<P, ClaudeCodeToolVocabulary>): SkillSpec;
|
|
58
58
|
//# sourceMappingURL=typed-spec.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.agent = agent;
|
|
4
|
-
exports.
|
|
4
|
+
exports.experimental_skill = experimental_skill;
|
|
5
5
|
/**
|
|
6
6
|
* Typed Claude Code authoring surface — the compile-time half of the purity
|
|
7
7
|
* contract, bound to the Claude Code tool vocabulary.
|
|
@@ -49,7 +49,7 @@ function agent(spec) {
|
|
|
49
49
|
* against the Claude Code tool catalog. Identical to the core `skill()` at
|
|
50
50
|
* runtime; the typed `tools` constraint is the only difference.
|
|
51
51
|
*/
|
|
52
|
-
function
|
|
53
|
-
return (0, spec_js_1.
|
|
52
|
+
function experimental_skill(spec) {
|
|
53
|
+
return (0, spec_js_1.experimental_skill)(spec);
|
|
54
54
|
}
|
|
55
55
|
//# sourceMappingURL=typed-spec.js.map
|
package/dist/claude-code.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from "./adapters/claude-code/plugin-loader.js";
|
|
|
9
9
|
export * from "./mock-model.js";
|
|
10
10
|
export { claudeCodeDriver, buildClaudeArgs, parseClaudeRun, claudeAvailable, } from "./harness-test.js";
|
|
11
11
|
export * from "./adapters/claude-code/dialect.js";
|
|
12
|
-
export { agent,
|
|
12
|
+
export { agent, experimental_skill, type ClaudeCodeToolVocabulary, } from "./adapters/claude-code/typed-spec.js";
|
|
13
13
|
export { measureSelectionMatrix, assertNoCollision, formatSelectionReport, } from "./scan-behavioral.js";
|
|
14
14
|
export type { SelectionReport, SkillSelectionStat, SelectionOptions, SelectionMatrixOptions, } from "./scan-behavioral.js";
|
|
15
15
|
export * from "./adapters/claude-code/layout.js";
|
package/dist/claude-code.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.formatSelectionReport = exports.assertNoCollision = exports.measureSelectionMatrix = exports.
|
|
17
|
+
exports.formatSelectionReport = exports.assertNoCollision = exports.measureSelectionMatrix = exports.experimental_skill = exports.agent = exports.claudeAvailable = exports.parseClaudeRun = exports.buildClaudeArgs = exports.claudeCodeDriver = void 0;
|
|
18
18
|
/**
|
|
19
19
|
* `vigiles/claude-code` — the Claude Code-specific harness pieces a *different*
|
|
20
20
|
* harness would swap out: the plugin/repo loader (reads real Claude Code plugin
|
|
@@ -35,13 +35,19 @@ Object.defineProperty(exports, "buildClaudeArgs", { enumerable: true, get: funct
|
|
|
35
35
|
Object.defineProperty(exports, "parseClaudeRun", { enumerable: true, get: function () { return harness_test_js_1.parseClaudeRun; } });
|
|
36
36
|
Object.defineProperty(exports, "claudeAvailable", { enumerable: true, get: function () { return harness_test_js_1.claudeAvailable; } });
|
|
37
37
|
__exportStar(require("./adapters/claude-code/dialect.js"), exports);
|
|
38
|
-
// The typed Claude Code authoring surface: `agent` / `
|
|
39
|
-
// floor enforced AT COMPILE TIME against the CC tool catalog (a
|
|
40
|
-
// e.g. `purity: "pure"` + `"Bash"`). A strict addition to the
|
|
41
|
-
// purity checks; the bare core `agent()`/`
|
|
38
|
+
// The typed Claude Code authoring surface: `agent` / `experimental_skill` with
|
|
39
|
+
// the `purity` floor enforced AT COMPILE TIME against the CC tool catalog (a
|
|
40
|
+
// `tsc` error for e.g. `purity: "pure"` + `"Bash"`). A strict addition to the
|
|
41
|
+
// runtime/compile purity checks; the bare core `agent()`/`experimental_skill()`
|
|
42
|
+
// (`vigiles/spec`) stay open.
|
|
43
|
+
//
|
|
44
|
+
// NOTE the collision this rename also resolves: `vigiles/testing` exports a
|
|
45
|
+
// `skill()` too, and it is a DIFFERENT function — a `Check<Trace>` asking "did
|
|
46
|
+
// this skill fire?", taking an id string. One word, two concepts, told apart
|
|
47
|
+
// only by which door you imported from. Only the authoring builder is prefixed.
|
|
42
48
|
var typed_spec_js_1 = require("./adapters/claude-code/typed-spec.js");
|
|
43
49
|
Object.defineProperty(exports, "agent", { enumerable: true, get: function () { return typed_spec_js_1.agent; } });
|
|
44
|
-
Object.defineProperty(exports, "
|
|
50
|
+
Object.defineProperty(exports, "experimental_skill", { enumerable: true, get: function () { return typed_spec_js_1.experimental_skill; } });
|
|
45
51
|
// Selection-collision — a Claude-Code-ONLY behavioral measurement (Codex has no
|
|
46
52
|
// skill-selection event to read), so it lives on this surface, not the agnostic
|
|
47
53
|
// `vigiles/testing`. `measureSelectionMatrix` builds the N×N "which skill fired?"
|
package/dist/core/adopt.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export interface AdoptSurfaceResult {
|
|
|
80
80
|
unmappedKeys: string[];
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
|
-
* Adopt an existing SKILL.md into
|
|
83
|
+
* Adopt an existing SKILL.md into an `experimental_skill()` spec. The body is carried verbatim
|
|
84
84
|
* (skills are freeform markdown — `##` headings stay in the body), so a clean
|
|
85
85
|
* skill round-trips below the integrity header.
|
|
86
86
|
*
|
package/dist/core/adopt.js
CHANGED
|
@@ -289,7 +289,7 @@ function unmappedNote(kind, keys) {
|
|
|
289
289
|
const SURFACE_HEADER = (from) => `// Adopted from ${from} by \`vigiles init\` — body verbatim, standard\n` +
|
|
290
290
|
`// frontmatter mapped; no rules inferred. Review the diff, then \`compile\`.\n`;
|
|
291
291
|
/**
|
|
292
|
-
* Adopt an existing SKILL.md into
|
|
292
|
+
* Adopt an existing SKILL.md into an `experimental_skill()` spec. The body is carried verbatim
|
|
293
293
|
* (skills are freeform markdown — `##` headings stay in the body), so a clean
|
|
294
294
|
* skill round-trips below the integrity header.
|
|
295
295
|
*
|
|
@@ -325,7 +325,7 @@ function adoptSkill(markdown, dirName) {
|
|
|
325
325
|
const trimmedBody = body.trim();
|
|
326
326
|
if (trimmedBody)
|
|
327
327
|
lines.push(` body: ${tsTemplate(trimmedBody)},`);
|
|
328
|
-
const spec = (0, spec_js_1.
|
|
328
|
+
const spec = (0, spec_js_1.experimental_skill)({
|
|
329
329
|
name,
|
|
330
330
|
description,
|
|
331
331
|
...(argumentHint ? { argumentHint } : {}),
|
|
@@ -336,8 +336,8 @@ function adoptSkill(markdown, dirName) {
|
|
|
336
336
|
});
|
|
337
337
|
const source = SURFACE_HEADER(`${dirName}/SKILL.md`) +
|
|
338
338
|
unmappedNote("skill", unmappedKeys) +
|
|
339
|
-
`import {
|
|
340
|
-
`export default
|
|
339
|
+
`import { experimental_skill } from "vigiles/spec";\n\n` +
|
|
340
|
+
`export default experimental_skill({\n${lines.join("\n")}\n});\n`;
|
|
341
341
|
return { source, kind: "skill", spec, unmappedKeys };
|
|
342
342
|
}
|
|
343
343
|
/**
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vigiles — Generator → SKILL.md compiler.
|
|
3
3
|
*
|
|
4
|
+
* PARKED (2026-08-16). Not part of the public API and not in `docs/skills.md`.
|
|
5
|
+
* It works — `vigiles compile` detects `genSkill(` and routes here (`cli.ts`),
|
|
6
|
+
* verified end-to-end — but `genSkill` / `act` / `checkpoint` / `finish` /
|
|
7
|
+
* `runSkill` are reachable from NO package subpath, so nobody outside this repo
|
|
8
|
+
* can author one. Parked rather than deleted because the code is sound and its
|
|
9
|
+
* tests pass; there is simply nothing to evaluate its usefulness against.
|
|
10
|
+
* Do not re-document it or give it an entry point without settling that first —
|
|
11
|
+
* see `docs/skills.md` §Status and the adoption-ladder work.
|
|
12
|
+
*
|
|
4
13
|
* A generator skill (`function* () { … yield act/gate/result … }`) has dynamic
|
|
5
14
|
* control flow, so it can't be rendered by *executing* it. Instead we parse its
|
|
6
15
|
* SOURCE with the TypeScript compiler API and render the structure to markdown:
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* vigiles — Generator → SKILL.md compiler.
|
|
4
4
|
*
|
|
5
|
+
* PARKED (2026-08-16). Not part of the public API and not in `docs/skills.md`.
|
|
6
|
+
* It works — `vigiles compile` detects `genSkill(` and routes here (`cli.ts`),
|
|
7
|
+
* verified end-to-end — but `genSkill` / `act` / `checkpoint` / `finish` /
|
|
8
|
+
* `runSkill` are reachable from NO package subpath, so nobody outside this repo
|
|
9
|
+
* can author one. Parked rather than deleted because the code is sound and its
|
|
10
|
+
* tests pass; there is simply nothing to evaluate its usefulness against.
|
|
11
|
+
* Do not re-document it or give it an entry point without settling that first —
|
|
12
|
+
* see `docs/skills.md` §Status and the adoption-ladder work.
|
|
13
|
+
*
|
|
5
14
|
* A generator skill (`function* () { … yield act/gate/result … }`) has dynamic
|
|
6
15
|
* control flow, so it can't be rendered by *executing* it. Instead we parse its
|
|
7
16
|
* SOURCE with the TypeScript compiler API and render the structure to markdown:
|
package/dist/core/spec.d.ts
CHANGED
|
@@ -281,10 +281,10 @@ export declare function instructions(strings: TemplateStringsArray, ...values: I
|
|
|
281
281
|
* `<!-- vigiles:effect -->` markers. Independent of the `doc()` authoring
|
|
282
282
|
* surface — it does not block on it.
|
|
283
283
|
*
|
|
284
|
-
* @
|
|
284
|
+
* @experimental Experimental (parked P3) — NOT part of the frozen public surface;
|
|
285
285
|
* may change or be removed without a major bump pre-1.0.
|
|
286
286
|
*/
|
|
287
|
-
export declare function
|
|
287
|
+
export declare function experimental_effect(strings: TemplateStringsArray, ...values: InstructionFragment[]): EffectRegion;
|
|
288
288
|
/**
|
|
289
289
|
* The purity an author DECLARES for a skill/agent — the floor `compile`
|
|
290
290
|
* enforces against the tool contract (see `purityViolations` in
|
|
@@ -493,14 +493,20 @@ export type SkillSpecInput<P extends AuthoredPurity | undefined, V extends ToolV
|
|
|
493
493
|
* Define a SKILL.md specification.
|
|
494
494
|
*
|
|
495
495
|
* // skills/my-skill/SKILL.md.spec.ts
|
|
496
|
-
* export default
|
|
496
|
+
* export default experimental_skill({ name: "my-skill", description: "…" });
|
|
497
497
|
*
|
|
498
498
|
* Generic over a tool `Vocabulary` (default `OpenToolVocabulary` — no
|
|
499
|
-
* constraint), exactly like `agent()`: a vocabulary-bound `
|
|
500
|
-
* `vigiles/claude-code`) makes `purity: "pure"` + a side-effecting tool a
|
|
501
|
-
* error; the bare core
|
|
499
|
+
* constraint), exactly like `agent()`: a vocabulary-bound `experimental_skill`
|
|
500
|
+
* (e.g. `vigiles/claude-code`) makes `purity: "pure"` + a side-effecting tool a
|
|
501
|
+
* `tsc` error; the bare core one accepts any tools, as before.
|
|
502
|
+
*
|
|
503
|
+
* The name also disambiguates: `vigiles/testing` exports a `skill()` that is a
|
|
504
|
+
* DIFFERENT function — a `Check<Trace>` taking an id string, asking whether a
|
|
505
|
+
* skill fired. This one authors a skill; that one observes one.
|
|
506
|
+
*
|
|
507
|
+
* @experimental
|
|
502
508
|
*/
|
|
503
|
-
export declare function
|
|
509
|
+
export declare function experimental_skill<const P extends AuthoredPurity | undefined = undefined, V extends ToolVocabulary = OpenToolVocabulary>(spec: SkillSpecInput<P, V>): SkillSpec;
|
|
504
510
|
/**
|
|
505
511
|
* A subagent definition (compiles to `agents/<name>.md`). Unlike a skill —
|
|
506
512
|
* reference material the model reads on activation — a subagent is a *delegated
|
|
@@ -758,10 +764,10 @@ export type NeedsContract<N extends Shape> = N;
|
|
|
758
764
|
*
|
|
759
765
|
* needs({ plan: "string", files: "string[]" })
|
|
760
766
|
*
|
|
761
|
-
* @
|
|
767
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
762
768
|
* public API (pre-1.0); may change without a major bump.
|
|
763
769
|
*/
|
|
764
|
-
export declare function
|
|
770
|
+
export declare function experimental_needs<const N extends Shape>(shape: N): NeedsContract<N>;
|
|
765
771
|
/**
|
|
766
772
|
* A typed pipeline step: a `TypedAgentSpec` paired with the input `needs` it
|
|
767
773
|
* reads from the prior step's `ok`. `step()` builds one; `pipe` checks that the
|
|
@@ -782,10 +788,10 @@ export interface PipeStep<Needs extends Shape, Ok extends Shape, Err extends Sha
|
|
|
782
788
|
*
|
|
783
789
|
* pipeStep(implementer, needs({ plan: "string", files: "string[]" }))
|
|
784
790
|
*
|
|
785
|
-
* @
|
|
791
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
786
792
|
* public API (pre-1.0); may change without a major bump.
|
|
787
793
|
*/
|
|
788
|
-
export declare function
|
|
794
|
+
export declare function experimental_pipeStep<Needs extends Shape, Ok extends Shape, Err extends Shape>(a: TypedAgentSpec<Ok, Err>, needsContract?: Needs): PipeStep<Needs, Ok, Err>;
|
|
789
795
|
/**
|
|
790
796
|
* True iff `Producer` provides EVERY field `Consumer` needs, with matching
|
|
791
797
|
* field types. When satisfiable it is `true`; otherwise it collapses to a
|
|
@@ -844,10 +850,10 @@ export interface Pipeline<Ok extends Shape, Err extends Shape> {
|
|
|
844
850
|
* its `needs` must be empty (`needs({})` or omitted). Returns a `Pipeline`
|
|
845
851
|
* carrying that step's `ok`/`err` forward.
|
|
846
852
|
*
|
|
847
|
-
* @
|
|
853
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
848
854
|
* public API (pre-1.0); may change without a major bump.
|
|
849
855
|
*/
|
|
850
|
-
export declare function
|
|
856
|
+
export declare function experimental_start<Ok extends Shape, Err extends Shape>(first: PipeStep<Record<string, never>, Ok, Err> | TypedAgentSpec<Ok, Err>): Pipeline<Ok, Err>;
|
|
851
857
|
/**
|
|
852
858
|
* Append a step to a typed pipeline. The handoff is CHECKED: the constraint
|
|
853
859
|
* `Supplies<PriorOk, Needs>` must be `true`, else the `next` parameter's type
|
|
@@ -859,10 +865,10 @@ export declare function start<Ok extends Shape, Err extends Shape>(first: PipeSt
|
|
|
859
865
|
* exporting a function called `then` becomes a thenable, so `await import()` of
|
|
860
866
|
* any barrel re-exporting it would invoke it — a footgun the rename avoids.
|
|
861
867
|
*
|
|
862
|
-
* @
|
|
868
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
863
869
|
* public API (pre-1.0); may change without a major bump.
|
|
864
870
|
*/
|
|
865
|
-
export declare function
|
|
871
|
+
export declare function experimental_andThen<PriorOk extends Shape, PriorErr extends Shape, Needs extends Shape, Ok extends Shape, Err extends Shape>(prior: Pipeline<PriorOk, PriorErr>, next: Supplies<PriorOk, Needs> extends true ? PipeStep<Needs, Ok, Err> : {
|
|
866
872
|
readonly __HANDOFF_ERROR: Supplies<PriorOk, Needs>;
|
|
867
873
|
}): Pipeline<Ok, PriorErr | Err>;
|
|
868
874
|
/**
|
|
@@ -882,19 +888,19 @@ export declare function andThen<PriorOk extends Shape, PriorErr extends Shape, N
|
|
|
882
888
|
* pipeStep(reviewer, needs({ diff: "string" })),
|
|
883
889
|
* ) // ← won't compile if a handoff doesn't line up
|
|
884
890
|
*
|
|
885
|
-
* @
|
|
891
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
886
892
|
* public API (pre-1.0); may change without a major bump.
|
|
887
893
|
*/
|
|
888
|
-
export declare function
|
|
889
|
-
export declare function
|
|
894
|
+
export declare function experimental_pipe<A extends Shape, AE extends Shape>(a: TypedAgentSpec<A, AE>): Pipeline<A, AE>;
|
|
895
|
+
export declare function experimental_pipe<A extends Shape, AE extends Shape, BN extends Shape, B extends Shape, BE extends Shape>(a: TypedAgentSpec<A, AE>, b: Supplies<A, BN> extends true ? PipeStep<BN, B, BE> : {
|
|
890
896
|
readonly __HANDOFF_ERROR: Supplies<A, BN>;
|
|
891
897
|
}): Pipeline<B, AE | BE>;
|
|
892
|
-
export declare function
|
|
898
|
+
export declare function experimental_pipe<A extends Shape, AE extends Shape, BN extends Shape, B extends Shape, BE extends Shape, CN extends Shape, C extends Shape, CE extends Shape>(a: TypedAgentSpec<A, AE>, b: Supplies<A, BN> extends true ? PipeStep<BN, B, BE> : {
|
|
893
899
|
readonly __HANDOFF_ERROR: Supplies<A, BN>;
|
|
894
900
|
}, c: Supplies<B, CN> extends true ? PipeStep<CN, C, CE> : {
|
|
895
901
|
readonly __HANDOFF_ERROR: Supplies<B, CN>;
|
|
896
902
|
}): Pipeline<C, AE | BE | CE>;
|
|
897
|
-
export declare function
|
|
903
|
+
export declare function experimental_pipe<A extends Shape, AE extends Shape, BN extends Shape, B extends Shape, BE extends Shape, CN extends Shape, C extends Shape, CE extends Shape, DN extends Shape, D extends Shape, DE extends Shape>(a: TypedAgentSpec<A, AE>, b: Supplies<A, BN> extends true ? PipeStep<BN, B, BE> : {
|
|
898
904
|
readonly __HANDOFF_ERROR: Supplies<A, BN>;
|
|
899
905
|
}, c: Supplies<B, CN> extends true ? PipeStep<CN, C, CE> : {
|
|
900
906
|
readonly __HANDOFF_ERROR: Supplies<B, CN>;
|
package/dist/core/spec.js
CHANGED
|
@@ -21,21 +21,21 @@ exports.ref = ref;
|
|
|
21
21
|
exports.dir = dir;
|
|
22
22
|
exports.glob = glob;
|
|
23
23
|
exports.instructions = instructions;
|
|
24
|
-
exports.
|
|
24
|
+
exports.experimental_effect = experimental_effect;
|
|
25
25
|
exports.claude = claude;
|
|
26
26
|
exports.project = project;
|
|
27
27
|
exports.input = input;
|
|
28
28
|
exports.step = step;
|
|
29
|
-
exports.
|
|
29
|
+
exports.experimental_skill = experimental_skill;
|
|
30
30
|
exports.agent = agent;
|
|
31
31
|
exports.result = result;
|
|
32
32
|
exports.delegate = delegate;
|
|
33
33
|
exports.railway = railway;
|
|
34
|
-
exports.
|
|
35
|
-
exports.
|
|
36
|
-
exports.
|
|
37
|
-
exports.
|
|
38
|
-
exports.
|
|
34
|
+
exports.experimental_needs = experimental_needs;
|
|
35
|
+
exports.experimental_pipeStep = experimental_pipeStep;
|
|
36
|
+
exports.experimental_start = experimental_start;
|
|
37
|
+
exports.experimental_andThen = experimental_andThen;
|
|
38
|
+
exports.experimental_pipe = experimental_pipe;
|
|
39
39
|
exports.defineConfig = defineConfig;
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
// Template literal types for type-safe linter references
|
|
@@ -192,10 +192,10 @@ function instructions(strings, ...values) {
|
|
|
192
192
|
* `<!-- vigiles:effect -->` markers. Independent of the `doc()` authoring
|
|
193
193
|
* surface — it does not block on it.
|
|
194
194
|
*
|
|
195
|
-
* @
|
|
195
|
+
* @experimental Experimental (parked P3) — NOT part of the frozen public surface;
|
|
196
196
|
* may change or be removed without a major bump pre-1.0.
|
|
197
197
|
*/
|
|
198
|
-
function
|
|
198
|
+
function experimental_effect(strings, ...values) {
|
|
199
199
|
const body = [];
|
|
200
200
|
for (let i = 0; i < strings.length; i++) {
|
|
201
201
|
if (strings[i])
|
|
@@ -234,14 +234,20 @@ function step(instr, opts = {}) {
|
|
|
234
234
|
* Define a SKILL.md specification.
|
|
235
235
|
*
|
|
236
236
|
* // skills/my-skill/SKILL.md.spec.ts
|
|
237
|
-
* export default
|
|
237
|
+
* export default experimental_skill({ name: "my-skill", description: "…" });
|
|
238
238
|
*
|
|
239
239
|
* Generic over a tool `Vocabulary` (default `OpenToolVocabulary` — no
|
|
240
|
-
* constraint), exactly like `agent()`: a vocabulary-bound `
|
|
241
|
-
* `vigiles/claude-code`) makes `purity: "pure"` + a side-effecting tool a
|
|
242
|
-
* error; the bare core
|
|
240
|
+
* constraint), exactly like `agent()`: a vocabulary-bound `experimental_skill`
|
|
241
|
+
* (e.g. `vigiles/claude-code`) makes `purity: "pure"` + a side-effecting tool a
|
|
242
|
+
* `tsc` error; the bare core one accepts any tools, as before.
|
|
243
|
+
*
|
|
244
|
+
* The name also disambiguates: `vigiles/testing` exports a `skill()` that is a
|
|
245
|
+
* DIFFERENT function — a `Check<Trace>` taking an id string, asking whether a
|
|
246
|
+
* skill fired. This one authors a skill; that one observes one.
|
|
247
|
+
*
|
|
248
|
+
* @experimental
|
|
243
249
|
*/
|
|
244
|
-
function
|
|
250
|
+
function experimental_skill(spec) {
|
|
245
251
|
return { _specType: "skill", ...spec };
|
|
246
252
|
}
|
|
247
253
|
/**
|
|
@@ -333,10 +339,10 @@ function railway(spec) {
|
|
|
333
339
|
*
|
|
334
340
|
* needs({ plan: "string", files: "string[]" })
|
|
335
341
|
*
|
|
336
|
-
* @
|
|
342
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
337
343
|
* public API (pre-1.0); may change without a major bump.
|
|
338
344
|
*/
|
|
339
|
-
function
|
|
345
|
+
function experimental_needs(shape) {
|
|
340
346
|
return shape;
|
|
341
347
|
}
|
|
342
348
|
/**
|
|
@@ -346,10 +352,10 @@ function needs(shape) {
|
|
|
346
352
|
*
|
|
347
353
|
* pipeStep(implementer, needs({ plan: "string", files: "string[]" }))
|
|
348
354
|
*
|
|
349
|
-
* @
|
|
355
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
350
356
|
* public API (pre-1.0); may change without a major bump.
|
|
351
357
|
*/
|
|
352
|
-
function
|
|
358
|
+
function experimental_pipeStep(a, needsContract = {}) {
|
|
353
359
|
return { _step: "typed-delegate", agent: a, needs: needsContract };
|
|
354
360
|
}
|
|
355
361
|
/**
|
|
@@ -357,11 +363,13 @@ function pipeStep(a, needsContract = {}) {
|
|
|
357
363
|
* its `needs` must be empty (`needs({})` or omitted). Returns a `Pipeline`
|
|
358
364
|
* carrying that step's `ok`/`err` forward.
|
|
359
365
|
*
|
|
360
|
-
* @
|
|
366
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
361
367
|
* public API (pre-1.0); may change without a major bump.
|
|
362
368
|
*/
|
|
363
|
-
function
|
|
364
|
-
const step = "_step" in first
|
|
369
|
+
function experimental_start(first) {
|
|
370
|
+
const step = "_step" in first
|
|
371
|
+
? first
|
|
372
|
+
: experimental_pipeStep(first, {});
|
|
365
373
|
const out = step.agent.output;
|
|
366
374
|
return {
|
|
367
375
|
_specType: "pipeline",
|
|
@@ -385,10 +393,10 @@ function start(first) {
|
|
|
385
393
|
* exporting a function called `then` becomes a thenable, so `await import()` of
|
|
386
394
|
* any barrel re-exporting it would invoke it — a footgun the rename avoids.
|
|
387
395
|
*
|
|
388
|
-
* @
|
|
396
|
+
* @experimental Experimental typed-composition surface — NOT part of the frozen
|
|
389
397
|
* public API (pre-1.0); may change without a major bump.
|
|
390
398
|
*/
|
|
391
|
-
function
|
|
399
|
+
function experimental_andThen(prior, next) {
|
|
392
400
|
const real = next;
|
|
393
401
|
const out = real.agent.output;
|
|
394
402
|
const rw = railway({
|
|
@@ -403,12 +411,12 @@ function andThen(prior, next) {
|
|
|
403
411
|
railway: rw,
|
|
404
412
|
};
|
|
405
413
|
}
|
|
406
|
-
function
|
|
414
|
+
function experimental_pipe(first, ...rest) {
|
|
407
415
|
// The overloads above enforce each handoff at the type level; the runtime body
|
|
408
416
|
// is the same left fold of start/andThen, untyped (the checks already happened).
|
|
409
|
-
let pipeline =
|
|
417
|
+
let pipeline = experimental_start(first);
|
|
410
418
|
for (const s of rest) {
|
|
411
|
-
pipeline =
|
|
419
|
+
pipeline = experimental_andThen(pipeline, s);
|
|
412
420
|
}
|
|
413
421
|
return pipeline;
|
|
414
422
|
}
|
package/dist/e2e.d.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* four tiers). Prefer `vigiles/integration`.
|
|
11
11
|
*
|
|
12
12
|
* NOT here: **evals** (`runEval` / `measure` / `measureTriggerRate` / `judge`) —
|
|
13
|
-
* those are non-deterministic measurement
|
|
13
|
+
* those are non-deterministic measurement, a different axis. (They live on
|
|
14
|
+
* `vigiles/testing`; `vigiles/eval` is named here in the original comment and does
|
|
15
|
+
* not exist as an entry point.)
|
|
14
16
|
*/
|
|
15
17
|
export * from "./integration.js";
|
|
16
18
|
//# sourceMappingURL=e2e.d.ts.map
|
package/dist/e2e.js
CHANGED
|
@@ -26,7 +26,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
26
|
* four tiers). Prefer `vigiles/integration`.
|
|
27
27
|
*
|
|
28
28
|
* NOT here: **evals** (`runEval` / `measure` / `measureTriggerRate` / `judge`) —
|
|
29
|
-
* those are non-deterministic measurement
|
|
29
|
+
* those are non-deterministic measurement, a different axis. (They live on
|
|
30
|
+
* `vigiles/testing`; `vigiles/eval` is named here in the original comment and does
|
|
31
|
+
* not exist as an entry point.)
|
|
30
32
|
*/
|
|
31
33
|
__exportStar(require("./integration.js"), exports);
|
|
32
34
|
//# sourceMappingURL=e2e.js.map
|
package/dist/experimental.d.ts
CHANGED
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
* @module vigiles/experimental
|
|
30
30
|
*/
|
|
31
31
|
export { experimental_startServices, experimental_withServices, type ServiceSpec, type ServiceReady, type ServiceReset, type ServiceHandle, type ServiceSession, type ContainerRuntime, } from "./services.js";
|
|
32
|
-
export { experimental_dockerRuntime,
|
|
32
|
+
export { experimental_dockerRuntime, experimental_makeDockerRuntime, type DockerExec, type NetProbe, } from "./services-docker.js";
|
|
33
33
|
export { experimental_emitTool, experimental_parseEmitted, experimental_assertEmittedOk, type EmitFieldSchema, type EmitObjectSchema, type EmitPropertySchema, type EmitTrackSchema, type EmitToolDefinition, type ExperimentalEmitTool, } from "./experimental-emit.js";
|
|
34
34
|
//# sourceMappingURL=experimental.d.ts.map
|
package/dist/experimental.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.experimental_assertEmittedOk = exports.experimental_parseEmitted = exports.experimental_emitTool = exports.
|
|
3
|
+
exports.experimental_assertEmittedOk = exports.experimental_parseEmitted = exports.experimental_emitTool = exports.experimental_makeDockerRuntime = exports.experimental_dockerRuntime = exports.experimental_withServices = exports.experimental_startServices = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* `vigiles/experimental` — ⚠️ EXPERIMENTAL, UNSTABLE public surface.
|
|
6
6
|
*
|
|
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "experimental_startServices", { enumerable: true,
|
|
|
36
36
|
Object.defineProperty(exports, "experimental_withServices", { enumerable: true, get: function () { return services_js_1.experimental_withServices; } });
|
|
37
37
|
var services_docker_js_1 = require("./services-docker.js");
|
|
38
38
|
Object.defineProperty(exports, "experimental_dockerRuntime", { enumerable: true, get: function () { return services_docker_js_1.experimental_dockerRuntime; } });
|
|
39
|
-
Object.defineProperty(exports, "
|
|
39
|
+
Object.defineProperty(exports, "experimental_makeDockerRuntime", { enumerable: true, get: function () { return services_docker_js_1.experimental_makeDockerRuntime; } });
|
|
40
40
|
var experimental_emit_js_1 = require("./experimental-emit.js");
|
|
41
41
|
Object.defineProperty(exports, "experimental_emitTool", { enumerable: true, get: function () { return experimental_emit_js_1.experimental_emitTool; } });
|
|
42
42
|
Object.defineProperty(exports, "experimental_parseEmitted", { enumerable: true, get: function () { return experimental_emit_js_1.experimental_parseEmitted; } });
|
package/dist/harness-assert.d.ts
CHANGED
|
@@ -309,7 +309,7 @@ interface MatcherOutput {
|
|
|
309
309
|
* Custom matchers compatible with both vitest and jest. Register once:
|
|
310
310
|
*
|
|
311
311
|
* import { expect } from "vitest"; // or "@jest/globals"
|
|
312
|
-
* import { vigilesMatchers } from "vigiles/
|
|
312
|
+
* import { vigilesMatchers } from "vigiles/testing";
|
|
313
313
|
* expect.extend(vigilesMatchers);
|
|
314
314
|
*
|
|
315
315
|
* expect(result).toHaveCreated("RESULT");
|
package/dist/harness-assert.js
CHANGED
|
@@ -69,7 +69,8 @@ const agent_result_js_1 = require("./adapters/claude-code/agent-result.js");
|
|
|
69
69
|
const stats_js_1 = require("./stats.js");
|
|
70
70
|
const eval_baseline_js_1 = require("./eval-baseline.js");
|
|
71
71
|
// Re-export the significance primitives so the whole eval-analysis surface lives
|
|
72
|
-
//
|
|
72
|
+
// re-exported from `vigiles/testing` (there is no `vigiles/harness-assert` entry
|
|
73
|
+
// point — it was advertised in this very comment and never existed).
|
|
73
74
|
var stats_js_2 = require("./stats.js");
|
|
74
75
|
Object.defineProperty(exports, "compareArms", { enumerable: true, get: function () { return stats_js_2.compareArms; } });
|
|
75
76
|
var eval_baseline_js_2 = require("./eval-baseline.js");
|
|
@@ -689,7 +690,7 @@ function assertTriggerRate(report, opts) {
|
|
|
689
690
|
* Custom matchers compatible with both vitest and jest. Register once:
|
|
690
691
|
*
|
|
691
692
|
* import { expect } from "vitest"; // or "@jest/globals"
|
|
692
|
-
* import { vigilesMatchers } from "vigiles/
|
|
693
|
+
* import { vigilesMatchers } from "vigiles/testing";
|
|
693
694
|
* expect.extend(vigilesMatchers);
|
|
694
695
|
*
|
|
695
696
|
* expect(result).toHaveCreated("RESULT");
|
|
@@ -34,7 +34,7 @@ export declare function parseDockerPort(output: string): number | undefined;
|
|
|
34
34
|
*
|
|
35
35
|
* @experimental
|
|
36
36
|
*/
|
|
37
|
-
export declare function
|
|
37
|
+
export declare function experimental_makeDockerRuntime(deps?: {
|
|
38
38
|
exec?: DockerExec;
|
|
39
39
|
netProbe?: NetProbe;
|
|
40
40
|
sleep?: (ms: number) => Promise<void>;
|
package/dist/services-docker.js
CHANGED
|
@@ -6,7 +6,7 @@ exports.dockerRunArgs = dockerRunArgs;
|
|
|
6
6
|
exports.dockerExecArgs = dockerExecArgs;
|
|
7
7
|
exports.publishedPorts = publishedPorts;
|
|
8
8
|
exports.parseDockerPort = parseDockerPort;
|
|
9
|
-
exports.
|
|
9
|
+
exports.experimental_makeDockerRuntime = experimental_makeDockerRuntime;
|
|
10
10
|
/**
|
|
11
11
|
* vigiles — a Docker-backed {@link ContainerRuntime} for the R3 disposable-service
|
|
12
12
|
* tier (⚠️ EXPERIMENTAL / UNSTABLE — see src/services.ts and `vigiles/experimental`).
|
|
@@ -166,7 +166,7 @@ function numericNow() {
|
|
|
166
166
|
*
|
|
167
167
|
* @experimental
|
|
168
168
|
*/
|
|
169
|
-
function
|
|
169
|
+
function experimental_makeDockerRuntime(deps = {}) {
|
|
170
170
|
const exec = deps.exec ?? realDockerExec;
|
|
171
171
|
const netProbe = deps.netProbe ?? realNetProbe;
|
|
172
172
|
const sleep = deps.sleep ?? delay;
|
|
@@ -230,5 +230,5 @@ const containerNames = new WeakMap();
|
|
|
230
230
|
*
|
|
231
231
|
* @experimental
|
|
232
232
|
*/
|
|
233
|
-
exports.experimental_dockerRuntime =
|
|
233
|
+
exports.experimental_dockerRuntime = experimental_makeDockerRuntime();
|
|
234
234
|
//# sourceMappingURL=services-docker.js.map
|
package/dist/skill-test.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vigiles — Skill testing: deterministic tests for a skill's action sequence.
|
|
3
3
|
*
|
|
4
|
+
* PARKED (2026-08-16) — this drives the generator form, so it is parked with it;
|
|
5
|
+
* see the header of `src/core/compile-generator.ts`. `runSkill` is reachable
|
|
6
|
+
* from no package subpath. `scriptModel` is NOT parked: it is public from
|
|
7
|
+
* `vigiles/claude-code` and serves the harness testing API independently.
|
|
8
|
+
*
|
|
4
9
|
* A thin wrapper over the generator driver (`driveSkill`) for use inside an
|
|
5
10
|
* ordinary `node:test` / Vitest `test()` — no custom runner, no DSL. You script
|
|
6
11
|
* the *model* (the non-deterministic part) and assert the deterministic spine:
|
package/dist/skill-test.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* vigiles — Skill testing: deterministic tests for a skill's action sequence.
|
|
4
4
|
*
|
|
5
|
+
* PARKED (2026-08-16) — this drives the generator form, so it is parked with it;
|
|
6
|
+
* see the header of `src/core/compile-generator.ts`. `runSkill` is reachable
|
|
7
|
+
* from no package subpath. `scriptModel` is NOT parked: it is public from
|
|
8
|
+
* `vigiles/claude-code` and serves the harness testing API independently.
|
|
9
|
+
*
|
|
5
10
|
* A thin wrapper over the generator driver (`driveSkill`) for use inside an
|
|
6
11
|
* ordinary `node:test` / Vitest `test()` — no custom runner, no DSL. You script
|
|
7
12
|
* the *model* (the non-deterministic part) and assert the deterministic spine:
|
package/dist/testing.d.ts
CHANGED
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
* the three tiers — `runHook` (unit), `runHarnessTest` (deterministic), `runEval`
|
|
4
4
|
* (eval) — plus the runner-agnostic predicates/assertions. Kept deliberately
|
|
5
5
|
* separate from `vigiles/claude-code` so this surface can stay harness-agnostic as
|
|
6
|
-
* more harnesses are added.
|
|
6
|
+
* more harnesses are added.
|
|
7
|
+
*
|
|
8
|
+
* 🔴 THIS LINE USED TO SAY the granular paths (`vigiles/run-hook`, etc.) «still
|
|
9
|
+
* work». MEASURED 2026-08-16: they do not. `vigiles/run-hook`, `vigiles/eval`,
|
|
10
|
+
* `vigiles/harness-test`, `vigiles/harness-assert`, `vigiles/skill` and
|
|
11
|
+
* `vigiles/skill-test` all raise ERR_PACKAGE_PATH_NOT_EXPORTED — six paths this
|
|
12
|
+
* package advertised to its own readers and does not serve. A promise that an
|
|
13
|
+
* import keeps working is exactly the kind a reader has no reason to re-check.
|
|
7
14
|
*
|
|
8
15
|
* It re-exports the composition-root runner modules (which do the Claude-Code
|
|
9
16
|
* default-wiring), never an adapter directly — the `agnostic-surface` eslint
|
|
@@ -11,7 +18,6 @@
|
|
|
11
18
|
* `research/adapter-api-design.md`.
|
|
12
19
|
*/
|
|
13
20
|
export { recordCheck } from "./check-count.js";
|
|
14
|
-
export { runMutations, formatMutationReport, type MutationCase, type MutationEdit, type MutationOutcome, type MutationReport, type MutationVerdict, type RunMutationsOptions, } from "./mutations.js";
|
|
15
21
|
export { runScript } from "./run-script.js";
|
|
16
22
|
export type { RunScriptOptions, ScriptRunResult } from "./run-script.js";
|
|
17
23
|
export { runHook, propertyHook, fileToolEvents } from "./run-hook.js";
|
package/dist/testing.js
CHANGED
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
* the three tiers — `runHook` (unit), `runHarnessTest` (deterministic), `runEval`
|
|
5
5
|
* (eval) — plus the runner-agnostic predicates/assertions. Kept deliberately
|
|
6
6
|
* separate from `vigiles/claude-code` so this surface can stay harness-agnostic as
|
|
7
|
-
* more harnesses are added.
|
|
7
|
+
* more harnesses are added.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 THIS LINE USED TO SAY the granular paths (`vigiles/run-hook`, etc.) «still
|
|
10
|
+
* work». MEASURED 2026-08-16: they do not. `vigiles/run-hook`, `vigiles/eval`,
|
|
11
|
+
* `vigiles/harness-test`, `vigiles/harness-assert`, `vigiles/skill` and
|
|
12
|
+
* `vigiles/skill-test` all raise ERR_PACKAGE_PATH_NOT_EXPORTED — six paths this
|
|
13
|
+
* package advertised to its own readers and does not serve. A promise that an
|
|
14
|
+
* import keeps working is exactly the kind a reader has no reason to re-check.
|
|
8
15
|
*
|
|
9
16
|
* It re-exports the composition-root runner modules (which do the Claude-Code
|
|
10
17
|
* default-wiring), never an adapter directly — the `agnostic-surface` eslint
|
|
@@ -30,7 +37,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
30
37
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
31
38
|
};
|
|
32
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.formatContainment = exports.compareContainment = exports.skillContract = exports.mustNotInclude = exports.mustInclude = exports.commandsIn = exports.runHarness = exports.runHarnessTest = exports.judge = exports.hookFired = exports.loadHook = exports.stubSkillBody = exports.parseClaudeRun = exports.claudeEvalDriver = exports.formatTriggerRateReport = exports.formatEvalReport = exports.formatCheckReport = exports.checkReportToJUnit = exports.checkPromptDiversity = exports.assertPromptDiversity = exports.assertRates = exports.measureTriggerRate = exports.measureArms = exports.measure = exports.runEval = exports.fileToolEvents = exports.propertyHook = exports.runHook = exports.runScript = exports.
|
|
40
|
+
exports.formatContainment = exports.compareContainment = exports.skillContract = exports.mustNotInclude = exports.mustInclude = exports.commandsIn = exports.runHarness = exports.runHarnessTest = exports.judge = exports.hookFired = exports.loadHook = exports.stubSkillBody = exports.parseClaudeRun = exports.claudeEvalDriver = exports.formatTriggerRateReport = exports.formatEvalReport = exports.formatCheckReport = exports.checkReportToJUnit = exports.checkPromptDiversity = exports.assertPromptDiversity = exports.assertRates = exports.measureTriggerRate = exports.measureArms = exports.measure = exports.runEval = exports.fileToolEvents = exports.propertyHook = exports.runHook = exports.runScript = exports.recordCheck = void 0;
|
|
34
41
|
// --- reporting: how much did this script actually do? ---
|
|
35
42
|
// `vigiles test` can otherwise see only an exit code, so a file that runs NOTHING
|
|
36
43
|
// prints the same `✓` as one that ran and passed (measured 2026-08-08 on a file
|
|
@@ -39,14 +46,6 @@ exports.formatContainment = exports.compareContainment = exports.skillContract =
|
|
|
39
46
|
// vitest's `expect` — so those are visible to the runner too. See check-count.ts.
|
|
40
47
|
var check_count_js_1 = require("./check-count.js");
|
|
41
48
|
Object.defineProperty(exports, "recordCheck", { enumerable: true, get: function () { return check_count_js_1.recordCheck; } });
|
|
42
|
-
// --- the tier above the tiers: is a passing test PROVING anything? ---
|
|
43
|
-
// Every tier below reports that a check passed. None can tell a watched assertion
|
|
44
|
-
// from a vacuous one — both print `✓`. `runMutations` plants a defect, runs the
|
|
45
|
-
// test that owns it, and requires the test to fail with the message that NAMES
|
|
46
|
-
// it, so "green" stops being the strongest claim a suite can make about itself.
|
|
47
|
-
var mutations_js_1 = require("./mutations.js");
|
|
48
|
-
Object.defineProperty(exports, "runMutations", { enumerable: true, get: function () { return mutations_js_1.runMutations; } });
|
|
49
|
-
Object.defineProperty(exports, "formatMutationReport", { enumerable: true, get: function () { return mutations_js_1.formatMutationReport; } });
|
|
50
49
|
// --- unit tier: runScript (the primitive) + runHook (it, plus a decision) ---
|
|
51
50
|
// `runScript` runs any program and reports what it DID (exit, both streams,
|
|
52
51
|
// writes, egress). `runHook` is that plus the hook protocol: event to stdin,
|
package/dist/unit.d.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `vigiles/unit` — the **no-capability** harness-testing surface.
|
|
3
3
|
*
|
|
4
|
-
* Importing from here is a contract: this test needs
|
|
5
|
-
* bubblewrap
|
|
4
|
+
* Importing from here is MOSTLY a contract: this test needs no `claude`, no
|
|
5
|
+
* bubblewrap and no network — only a process. It is the cheap base of the pyramid:
|
|
6
|
+
*
|
|
7
|
+
* 🔴 «NO MODEL» IS NOT TRUE TODAY, AND SAYING SO WAS THE WORST OF THE FOUR
|
|
8
|
+
* MISSTATEMENTS ON THIS SURFACE, because it reads as a guarantee rather than a
|
|
9
|
+
* description. `judged` is re-exported here and its default judge is a REAL model
|
|
10
|
+
* call — `check.ts` resolves `opts.judge ?? ((o) => runJudge(o))`. So a test that
|
|
11
|
+
* imports only from this barrel can still spend money, and the one line a reader
|
|
12
|
+
* would rely on to know it cannot is this one.
|
|
13
|
+
*
|
|
14
|
+
* The honest fix is to MOVE `judged` off this barrel, not to reword the sentence —
|
|
15
|
+
* but that changes the public surface, and the surface is being reorganised
|
|
16
|
+
* separately (the measured proposal splits by COST: everything free on one path,
|
|
17
|
+
* everything that spends money on another, where this defect becomes unexpressible
|
|
18
|
+
* rather than merely documented). Until that lands, the claim is corrected here
|
|
19
|
+
* rather than left standing.
|
|
20
|
+
*
|
|
6
21
|
* `runHook` (pipe a synthesized event to a hook and read the block/allow
|
|
7
22
|
* decision), the bare `Trace` predicates, the `assert*` helpers, and the pure
|
|
8
23
|
* parsers. A `*.test.ts` should import **only** from here (enforced by lint).
|
package/dist/unit.js
CHANGED
|
@@ -18,8 +18,23 @@ exports.formatGuardrailReport = exports.assertBlocksDisasters = exports.unblocke
|
|
|
18
18
|
/**
|
|
19
19
|
* `vigiles/unit` — the **no-capability** harness-testing surface.
|
|
20
20
|
*
|
|
21
|
-
* Importing from here is a contract: this test needs
|
|
22
|
-
* bubblewrap
|
|
21
|
+
* Importing from here is MOSTLY a contract: this test needs no `claude`, no
|
|
22
|
+
* bubblewrap and no network — only a process. It is the cheap base of the pyramid:
|
|
23
|
+
*
|
|
24
|
+
* 🔴 «NO MODEL» IS NOT TRUE TODAY, AND SAYING SO WAS THE WORST OF THE FOUR
|
|
25
|
+
* MISSTATEMENTS ON THIS SURFACE, because it reads as a guarantee rather than a
|
|
26
|
+
* description. `judged` is re-exported here and its default judge is a REAL model
|
|
27
|
+
* call — `check.ts` resolves `opts.judge ?? ((o) => runJudge(o))`. So a test that
|
|
28
|
+
* imports only from this barrel can still spend money, and the one line a reader
|
|
29
|
+
* would rely on to know it cannot is this one.
|
|
30
|
+
*
|
|
31
|
+
* The honest fix is to MOVE `judged` off this barrel, not to reword the sentence —
|
|
32
|
+
* but that changes the public surface, and the surface is being reorganised
|
|
33
|
+
* separately (the measured proposal splits by COST: everything free on one path,
|
|
34
|
+
* everything that spends money on another, where this defect becomes unexpressible
|
|
35
|
+
* rather than merely documented). Until that lands, the claim is corrected here
|
|
36
|
+
* rather than left standing.
|
|
37
|
+
*
|
|
23
38
|
* `runHook` (pipe a synthesized event to a hook and read the block/allow
|
|
24
39
|
* decision), the bare `Trace` predicates, the `assert*` helpers, and the pure
|
|
25
40
|
* parsers. A `*.test.ts` should import **only** from here (enforced by lint).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.4.0",
|
|
4
4
|
"description": "Audit, test and measure the harness your AI agent runs on — grade your CLAUDE.md / AGENTS.md, skills, subagents and hooks, run them against a scripted model, and measure whether they actually fire.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -92,6 +92,8 @@
|
|
|
92
92
|
"test:types": "npm run build && tsc --noEmit -p test/types/tsconfig.json",
|
|
93
93
|
"api:report": "npm run build && node scripts/api-extractor.mjs --local",
|
|
94
94
|
"api:check": "npm run build && node scripts/api-extractor.mjs",
|
|
95
|
+
"docs:check": "npm run build && node scripts/check-doc-imports.mjs . docs README.md",
|
|
96
|
+
"experimental:check": "npm run api:check && node scripts/check-experimental-naming.mjs",
|
|
95
97
|
"docs:api": "typedoc"
|
|
96
98
|
},
|
|
97
99
|
"devDependencies": {
|
package/dist/mutations.d.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/** One edit: the file, the exact substring to find, and what replaces it. */
|
|
2
|
-
export type MutationEdit = readonly [
|
|
3
|
-
file: string,
|
|
4
|
-
find: string,
|
|
5
|
-
replace: string
|
|
6
|
-
];
|
|
7
|
-
/** One planted defect and the assertion that must catch it. */
|
|
8
|
-
export interface MutationCase {
|
|
9
|
-
/** Short id, printed in the report. */
|
|
10
|
-
readonly name: string;
|
|
11
|
-
/** What the mutation takes away, in words — the column a reader scans. */
|
|
12
|
-
readonly disables: string;
|
|
13
|
-
/**
|
|
14
|
-
* The edits that plant it. A LIST, because some defects are only expressible as more than one:
|
|
15
|
-
* removing a guard AND the vocabulary check that would otherwise throw for an unrelated reason.
|
|
16
|
-
* Every edit must match its `find` EXACTLY ONCE, or the case is reported `not-applied`.
|
|
17
|
-
*/
|
|
18
|
-
readonly edits: readonly MutationEdit[];
|
|
19
|
-
/** The test file that must go red. Its absence is refused, loudly, before anything is touched. */
|
|
20
|
-
readonly test: string;
|
|
21
|
-
/** A substring the test's complaint must contain, so a kill by a NEIGHBOUR is not counted. */
|
|
22
|
-
readonly expect: string;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* - `killed` — the test went red AND printed `expect`. The only outcome that counts as proof.
|
|
26
|
-
* - `wrong-assertion` — red, but not with this case's message: two defects share one assertion and
|
|
27
|
-
* neither is really watched.
|
|
28
|
-
* - `survived` — the test stayed green. The assertion is vacuous, or absent.
|
|
29
|
-
* - `unjudgeable` — the test was ALREADY red before the run, and `expect` never printed, so "red"
|
|
30
|
-
* carries no information about this mutation. Not a pass and not a failure of the assertion.
|
|
31
|
-
* - `not-applied` — the edit did not land: `find` matched zero or several times, or the
|
|
32
|
-
* replacement equalled the original.
|
|
33
|
-
*/
|
|
34
|
-
export type MutationVerdict = "killed" | "wrong-assertion" | "survived" | "unjudgeable" | "not-applied";
|
|
35
|
-
/** What one case did. */
|
|
36
|
-
export interface MutationOutcome {
|
|
37
|
-
readonly name: string;
|
|
38
|
-
readonly disables: string;
|
|
39
|
-
readonly verdict: MutationVerdict;
|
|
40
|
-
/** Human-readable specifics — which file, how many matches, whether a retry was needed. */
|
|
41
|
-
readonly detail: string;
|
|
42
|
-
}
|
|
43
|
-
/** What a whole run did. */
|
|
44
|
-
export interface MutationReport {
|
|
45
|
-
readonly outcomes: readonly MutationOutcome[];
|
|
46
|
-
/** Cases with verdict `killed`. */
|
|
47
|
-
readonly killed: number;
|
|
48
|
-
/** Test files that were red BEFORE any mutation, so they can testify about nothing. */
|
|
49
|
-
readonly alreadyRed: readonly string[];
|
|
50
|
-
/**
|
|
51
|
-
* Whether every test that was green before the run is green again after it. `false` means the
|
|
52
|
-
* restore failed and the working tree is not what it was — the one outcome worth interrupting for.
|
|
53
|
-
*/
|
|
54
|
-
readonly restored: boolean;
|
|
55
|
-
}
|
|
56
|
-
export interface RunMutationsOptions {
|
|
57
|
-
/** Working directory for the test runs; every path in `edits` and `test` is absolute. */
|
|
58
|
-
readonly cwd: string;
|
|
59
|
-
readonly cases: readonly MutationCase[];
|
|
60
|
-
/**
|
|
61
|
-
* Extra environment for each test run, merged over `process.env`.
|
|
62
|
-
* Use it to hand the test the same variables its normal runner would.
|
|
63
|
-
*/
|
|
64
|
-
readonly env?: NodeJS.ProcessEnv;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Plant each case's defect, run the test that owns it, restore, and report what happened.
|
|
68
|
-
*
|
|
69
|
-
* ```ts
|
|
70
|
-
* const report = runMutations({
|
|
71
|
-
* cwd: repoRoot,
|
|
72
|
-
* cases: [{
|
|
73
|
-
* name: "year",
|
|
74
|
-
* disables: "the year comparison",
|
|
75
|
-
* edits: [[checker, "rec.year !== ourYear", "false"]],
|
|
76
|
-
* test: harness,
|
|
77
|
-
* expect: "a wrong year was not reported",
|
|
78
|
-
* }],
|
|
79
|
-
* });
|
|
80
|
-
* console.log(formatMutationReport(report));
|
|
81
|
-
* process.exit(report.killed === report.outcomes.length && report.restored ? 0 : 1);
|
|
82
|
-
* ```
|
|
83
|
-
*
|
|
84
|
-
* 🔴 THIS REWRITES THE FILES NAMED IN `edits` AND RESTORES THEM. The restore runs in a `finally`
|
|
85
|
-
* AND on SIGINT/SIGTERM, because a run killed midway would otherwise leave a neutered checker on
|
|
86
|
-
* disk — and the next run would then measure an already-broken repo and call it healthy. Commit
|
|
87
|
-
* before running. (Do not reach for `git stash` to clear the tree: the stash is repository-global,
|
|
88
|
-
* so a second agent working in another worktree of the same repo will pop your entries.)
|
|
89
|
-
*
|
|
90
|
-
* @throws if `cases` is empty, or if any named test file does not exist — both BEFORE any file is
|
|
91
|
-
* touched, so the message arrives with a clean working tree.
|
|
92
|
-
*/
|
|
93
|
-
export declare function runMutations(o: RunMutationsOptions): MutationReport;
|
|
94
|
-
/** Render a report the way the CLI-style runners in this package render theirs. */
|
|
95
|
-
export declare function formatMutationReport(report: MutationReport): string;
|
|
96
|
-
//# sourceMappingURL=mutations.d.ts.map
|
package/dist/mutations.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runMutations = runMutations;
|
|
4
|
-
exports.formatMutationReport = formatMutationReport;
|
|
5
|
-
/**
|
|
6
|
-
* `runMutations` — prove a test can FAIL, by breaking the thing it watches.
|
|
7
|
-
*
|
|
8
|
-
* A green test says the checker passed. It does not say the test would notice if the check were
|
|
9
|
-
* deleted: an assertion can be vacuous, a fixture can be wrong in the same direction as the code,
|
|
10
|
-
* and both look exactly like a pass. The only way to tell is to plant a defect and require the
|
|
11
|
-
* test to go red — with the message that NAMES that defect, not merely with something red.
|
|
12
|
-
*
|
|
13
|
-
* ## Why this is in vigiles rather than in a repo's own scripts
|
|
14
|
-
*
|
|
15
|
-
* It arrived as ten hand-written copies of one driver in a dogfooding repo (1799 lines), and the
|
|
16
|
-
* copies had DRIFTED. Counted against the committed versions on 2026-08-14:
|
|
17
|
-
*
|
|
18
|
-
* - the NO-OP guard (a replacement equal to the original leaves a green test proving nothing)
|
|
19
|
-
* was in 4 of 10;
|
|
20
|
-
* - the RETRY (a non-kill re-run once before it is believed) was in 1 of 10;
|
|
21
|
-
* - the strict rule — killed by its OWN named assertion, not merely by something going red —
|
|
22
|
-
* was in 1 of 10.
|
|
23
|
-
*
|
|
24
|
-
* Every one of those was written AFTER it caught something, in whichever copy happened to catch
|
|
25
|
-
* it, and never travelled to the other nine. Two copies also named a test file that a refactor had
|
|
26
|
-
* deleted; the runner they used exits 0 on a path matching nothing, so those cases reported
|
|
27
|
-
* SURVIVED on every run for three days. That is the argument for one engine: not less code, but
|
|
28
|
-
* one place where each of those becomes impossible again.
|
|
29
|
-
*
|
|
30
|
-
* ## What this is NOT
|
|
31
|
-
*
|
|
32
|
-
* Not Stryker/mutmut/PIT. Those GENERATE mutants from operators (flip a `<`, drop a `return`) over
|
|
33
|
-
* production code and score a suite by kill ratio. Here the mutations are HAND-AUTHORED and each
|
|
34
|
-
* one names the assertion that must catch it, because the subject is usually not general-purpose
|
|
35
|
-
* code — it is a checker, a hook, an instruction file — where "flip an operator" produces mostly
|
|
36
|
-
* unreachable nonsense and a kill ratio measures nothing. The generated-operator approach is the
|
|
37
|
-
* better tool when it applies; reach for it there.
|
|
38
|
-
*
|
|
39
|
-
* ## The contract, in one sentence
|
|
40
|
-
*
|
|
41
|
-
* Plant one defect, run the test that owns it, and require the test to fail with the message that
|
|
42
|
-
* names it — anything else is reported as a finding, never as a pass.
|
|
43
|
-
*
|
|
44
|
-
* @module
|
|
45
|
-
*/
|
|
46
|
-
const node_fs_1 = require("node:fs");
|
|
47
|
-
const node_child_process_1 = require("node:child_process");
|
|
48
|
-
const node_path_1 = require("node:path");
|
|
49
|
-
const run_scripts_js_1 = require("./adapters/claude-code/run-scripts.js");
|
|
50
|
-
function runTest(file, o) {
|
|
51
|
-
const argv = (0, run_scripts_js_1.interpreterArgs)(file, (0, run_scripts_js_1.detectNodeCaps)(o.cwd));
|
|
52
|
-
const r = (0, node_child_process_1.spawnSync)("node", argv, {
|
|
53
|
-
cwd: o.cwd,
|
|
54
|
-
encoding: "utf8",
|
|
55
|
-
env: { ...process.env, ...o.env },
|
|
56
|
-
});
|
|
57
|
-
return { failed: r.status !== 0, out: (r.stdout ?? "") + (r.stderr ?? "") };
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Plant each case's defect, run the test that owns it, restore, and report what happened.
|
|
61
|
-
*
|
|
62
|
-
* ```ts
|
|
63
|
-
* const report = runMutations({
|
|
64
|
-
* cwd: repoRoot,
|
|
65
|
-
* cases: [{
|
|
66
|
-
* name: "year",
|
|
67
|
-
* disables: "the year comparison",
|
|
68
|
-
* edits: [[checker, "rec.year !== ourYear", "false"]],
|
|
69
|
-
* test: harness,
|
|
70
|
-
* expect: "a wrong year was not reported",
|
|
71
|
-
* }],
|
|
72
|
-
* });
|
|
73
|
-
* console.log(formatMutationReport(report));
|
|
74
|
-
* process.exit(report.killed === report.outcomes.length && report.restored ? 0 : 1);
|
|
75
|
-
* ```
|
|
76
|
-
*
|
|
77
|
-
* 🔴 THIS REWRITES THE FILES NAMED IN `edits` AND RESTORES THEM. The restore runs in a `finally`
|
|
78
|
-
* AND on SIGINT/SIGTERM, because a run killed midway would otherwise leave a neutered checker on
|
|
79
|
-
* disk — and the next run would then measure an already-broken repo and call it healthy. Commit
|
|
80
|
-
* before running. (Do not reach for `git stash` to clear the tree: the stash is repository-global,
|
|
81
|
-
* so a second agent working in another worktree of the same repo will pop your entries.)
|
|
82
|
-
*
|
|
83
|
-
* @throws if `cases` is empty, or if any named test file does not exist — both BEFORE any file is
|
|
84
|
-
* touched, so the message arrives with a clean working tree.
|
|
85
|
-
*/
|
|
86
|
-
function runMutations(o) {
|
|
87
|
-
if (o.cases.length === 0) {
|
|
88
|
-
throw new Error("runMutations: no cases. A mutation run with nothing to run reports success, which is the exact claim this API exists to make impossible.");
|
|
89
|
-
}
|
|
90
|
-
const tests = [...new Set(o.cases.map((c) => c.test))];
|
|
91
|
-
// A test path that resolves to nothing is the defect that hid for three days in the corpus this
|
|
92
|
-
// came from: the runner exits 0 on "no files matched", so every case naming it reported SURVIVED
|
|
93
|
-
// and sent the reader hunting for an assertion that was never reached.
|
|
94
|
-
const missing = tests.filter((t) => !(0, node_fs_1.existsSync)(t));
|
|
95
|
-
if (missing.length > 0) {
|
|
96
|
-
throw new Error(`runMutations: test file(s) do not exist, so no case naming them could ever be judged:\n ${missing.join("\n ")}`);
|
|
97
|
-
}
|
|
98
|
-
// Baselined BEFORE anything is touched. Without this, a test that is red on purpose (an open
|
|
99
|
-
// finding it is meant to report) makes every run announce "the restore failed" about a working
|
|
100
|
-
// restore, and makes every case against it look killed.
|
|
101
|
-
const alreadyRed = tests.filter((t) => runTest(t, o).failed);
|
|
102
|
-
const redBefore = new Set(alreadyRed);
|
|
103
|
-
const targets = [...new Set(o.cases.flatMap((c) => c.edits.map(([f]) => f)))];
|
|
104
|
-
const originals = new Map(targets.map((f) => [f, (0, node_fs_1.readFileSync)(f, "utf8")]));
|
|
105
|
-
const restore = () => {
|
|
106
|
-
for (const [f, text] of originals)
|
|
107
|
-
(0, node_fs_1.writeFileSync)(f, text);
|
|
108
|
-
};
|
|
109
|
-
const onSignal = (sig) => {
|
|
110
|
-
restore();
|
|
111
|
-
process.exit(sig === "SIGINT" ? 130 : 143);
|
|
112
|
-
};
|
|
113
|
-
process.on("SIGINT", onSignal);
|
|
114
|
-
process.on("SIGTERM", onSignal);
|
|
115
|
-
const outcomes = [];
|
|
116
|
-
try {
|
|
117
|
-
for (const c of o.cases) {
|
|
118
|
-
const applied = apply(c);
|
|
119
|
-
if (applied) {
|
|
120
|
-
outcomes.push({
|
|
121
|
-
name: c.name,
|
|
122
|
-
disables: c.disables,
|
|
123
|
-
verdict: "not-applied",
|
|
124
|
-
detail: applied,
|
|
125
|
-
});
|
|
126
|
-
restore();
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
// A non-kill is retried ONCE before it is believed. Observed on a real corpus: a row killed
|
|
130
|
-
// as named came back as a neighbour's assertion in a later full run and reproduced as a clean
|
|
131
|
-
// kill when replayed alone. Reporting a flake as a survivor sends the next reader to rewrite
|
|
132
|
-
// a working assertion, which is worse than one extra run.
|
|
133
|
-
let judged = judge(c, o, redBefore);
|
|
134
|
-
if (judged.verdict !== "killed") {
|
|
135
|
-
const second = judge(c, o, redBefore);
|
|
136
|
-
if (second.verdict === "killed")
|
|
137
|
-
judged = {
|
|
138
|
-
verdict: "killed",
|
|
139
|
-
detail: "killed on retry (the first run was a flake)",
|
|
140
|
-
};
|
|
141
|
-
else
|
|
142
|
-
judged = second;
|
|
143
|
-
}
|
|
144
|
-
outcomes.push({ name: c.name, disables: c.disables, ...judged });
|
|
145
|
-
restore();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
finally {
|
|
149
|
-
restore();
|
|
150
|
-
process.off("SIGINT", onSignal);
|
|
151
|
-
process.off("SIGTERM", onSignal);
|
|
152
|
-
}
|
|
153
|
-
// Only a test that was GREEN before can testify about the restore.
|
|
154
|
-
const restored = tests
|
|
155
|
-
.filter((t) => !redBefore.has(t))
|
|
156
|
-
.every((t) => !runTest(t, o).failed);
|
|
157
|
-
return {
|
|
158
|
-
outcomes,
|
|
159
|
-
killed: outcomes.filter((r) => r.verdict === "killed").length,
|
|
160
|
-
alreadyRed,
|
|
161
|
-
restored,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
/** Plants one case's edits. Returns a reason string when it could not be planted, else null. */
|
|
165
|
-
function apply(c) {
|
|
166
|
-
for (const [file, find, replace] of c.edits) {
|
|
167
|
-
const src = (0, node_fs_1.readFileSync)(file, "utf8");
|
|
168
|
-
const n = src.split(find).length - 1;
|
|
169
|
-
if (n !== 1) {
|
|
170
|
-
return n === 0
|
|
171
|
-
? `"find" matched nothing in ${(0, node_path_1.basename)(file)} — the source moved under the case`
|
|
172
|
-
: `"find" matched ${String(n)} times in ${(0, node_path_1.basename)(file)}; an edit must be unambiguous`;
|
|
173
|
-
}
|
|
174
|
-
const next = src.replace(find, replace);
|
|
175
|
-
// The mutation-that-does-not-mutate, caught against the BYTES rather than the intent: a
|
|
176
|
-
// replacement equal to the original leaves a green test that reads exactly like a kill.
|
|
177
|
-
if (next === src)
|
|
178
|
-
return `the replacement equals the original in ${(0, node_path_1.basename)(file)}`;
|
|
179
|
-
(0, node_fs_1.writeFileSync)(file, next);
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
function judge(c, o, redBefore) {
|
|
184
|
-
const { failed, out } = runTest(c.test, o);
|
|
185
|
-
if (!failed)
|
|
186
|
-
return {
|
|
187
|
-
verdict: "survived",
|
|
188
|
-
detail: `${(0, node_path_1.basename)(c.test)} stayed green with the defect planted`,
|
|
189
|
-
};
|
|
190
|
-
if (out.includes(c.expect))
|
|
191
|
-
return { verdict: "killed", detail: `named by "${c.expect}"` };
|
|
192
|
-
// When the test was ALREADY red, "red" carries no information — but the MESSAGE still does,
|
|
193
|
-
// because a runner that aborts at the first failure never reaches a later assertion. Absent it,
|
|
194
|
-
// the two causes are indistinguishable, and calling it a wrong assertion would be a guess.
|
|
195
|
-
return redBefore.has(c.test)
|
|
196
|
-
? {
|
|
197
|
-
verdict: "unjudgeable",
|
|
198
|
-
detail: `${(0, node_path_1.basename)(c.test)} was red before the run and "${c.expect}" never printed`,
|
|
199
|
-
}
|
|
200
|
-
: {
|
|
201
|
-
verdict: "wrong-assertion",
|
|
202
|
-
detail: `red, but "${c.expect}" never printed — a neighbour's assertion caught it`,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
/** Render a report the way the CLI-style runners in this package render theirs. */
|
|
206
|
-
function formatMutationReport(report) {
|
|
207
|
-
const lines = [];
|
|
208
|
-
if (report.alreadyRed.length > 0) {
|
|
209
|
-
lines.push(`ℹ️ already red before any mutation: ${report.alreadyRed.map((t) => (0, node_path_1.basename)(t)).join(", ")} — excluded from the restore check; a case naming one is reported unjudgeable.`, "");
|
|
210
|
-
}
|
|
211
|
-
const width = Math.max(...report.outcomes.map((r) => r.name.length));
|
|
212
|
-
const mark = {
|
|
213
|
-
killed: "✓ killed",
|
|
214
|
-
"wrong-assertion": "🔴 wrong assertion",
|
|
215
|
-
survived: "🔴 SURVIVED",
|
|
216
|
-
unjudgeable: "🔴 unjudgeable",
|
|
217
|
-
"not-applied": "🔴 not applied",
|
|
218
|
-
};
|
|
219
|
-
for (const r of report.outcomes) {
|
|
220
|
-
lines.push(`${r.name.padEnd(width)} ${mark[r.verdict].padEnd(20)} ${r.disables}`);
|
|
221
|
-
if (r.verdict !== "killed")
|
|
222
|
-
lines.push(`${" ".repeat(width + 2)} ${r.detail}`);
|
|
223
|
-
}
|
|
224
|
-
lines.push("");
|
|
225
|
-
lines.push(report.restored
|
|
226
|
-
? "restored: every test that was green before the run is green again"
|
|
227
|
-
: "🔴 RESTORE FAILED — a test that was green before the run is red now. The working tree is not what it was.");
|
|
228
|
-
const bad = report.outcomes.length - report.killed;
|
|
229
|
-
lines.push(bad === 0
|
|
230
|
-
? `✓ all ${String(report.outcomes.length)} mutations killed, each at its own assertion`
|
|
231
|
-
: `🔴 ${String(bad)} of ${String(report.outcomes.length)} not killed as named: ${report.outcomes
|
|
232
|
-
.filter((r) => r.verdict !== "killed")
|
|
233
|
-
.map((r) => r.name)
|
|
234
|
-
.join(", ")}`);
|
|
235
|
-
return lines.join("\n");
|
|
236
|
-
}
|
|
237
|
-
//# sourceMappingURL=mutations.js.map
|