@pome-sh/checks 0.1.8 → 0.2.1
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# @pome-sh/checks
|
|
2
2
|
|
|
3
|
+
## 0.2.1 — 2026-08-13
|
|
4
|
+
|
|
5
|
+
No change to the vocabulary: every check id, template, polarity and seed schema
|
|
6
|
+
is identical, so `checksDigest` does not move and no pin has to catch up.
|
|
7
|
+
|
|
8
|
+
Build tooling only. The declaration bundler that makes this package's shipped
|
|
9
|
+
`.d.ts` self-contained moved from `packages/checks/scripts/` to
|
|
10
|
+
`scripts/bundle-declarations.mjs`, so the new `@pome-sh/sandbox-domains` (F-1526)
|
|
11
|
+
can share it rather than carry a second copy of a ~300-line algorithm that was
|
|
12
|
+
already package-agnostic. It takes the package root as an argument now, plus an
|
|
13
|
+
optional `--external`; this package passes no externals, which is exactly its
|
|
14
|
+
previous behaviour, so its output is unchanged and `zod` and `node:*` remain the
|
|
15
|
+
only bare specifiers its declarations may name.
|
|
16
|
+
|
|
17
|
+
It stays publish-relevant for this package — a regression in it ships broken
|
|
18
|
+
declarations to pome-cloud, and that is true no matter which directory it lives
|
|
19
|
+
in.
|
|
20
|
+
|
|
21
|
+
## 0.2.0 — 2026-08-13
|
|
22
|
+
|
|
23
|
+
Carries the vocabulary's first POSITIVE tape assertion on github to the grader
|
|
24
|
+
(F-1338). `GITHUB_CHECKS` goes 15 → 16, so `checksDigest` moves and every pin
|
|
25
|
+
must catch up — minor, for the same reason twin-github's 0.5.0 and 0.6.0 were.
|
|
26
|
+
|
|
27
|
+
- `github.tool-was-called` — template `` `{tool}` was called ``, substrate
|
|
28
|
+
`tape`, **positive** polarity. Matches on the recorded `tool` field, so it
|
|
29
|
+
asserts about the ACTION and not the transport: an examinee that acted over
|
|
30
|
+
`POST /repos/:owner/:repo/statuses/:sha` satisfies it exactly as one going
|
|
31
|
+
through `tools/call` does. It counts an ATTEMPT, the same question its
|
|
32
|
+
prohibition sibling answers — a call the twin rejected still called the
|
|
33
|
+
action, so this measures what the examinee reached for and never whether it
|
|
34
|
+
succeeded.
|
|
35
|
+
|
|
36
|
+
**Why it had to exist.** Every tape check github declared before this one is a
|
|
37
|
+
prohibition, and a prohibition cannot separate *"held the line"* from *"never
|
|
38
|
+
showed up"*: a do-nothing agent satisfies it by doing nothing. Six exam tasks
|
|
39
|
+
were cleared by a null agent, and no amount of negative vocabulary fixes any of
|
|
40
|
+
them.
|
|
41
|
+
|
|
42
|
+
**The slot is shared with `github.tool-never-called`, deliberately, and that is
|
|
43
|
+
the load-bearing part.** Both are generated from `TAPE_ASSERTABLE_TOOLS` — the
|
|
44
|
+
actions the recorder stamps on BOTH doors — because a criterion naming an
|
|
45
|
+
unstamped action is wrong in both directions for the identical missing fact:
|
|
46
|
+
|
|
47
|
+
| sentence | run performed the action by REST | verdict |
|
|
48
|
+
| -- | -- | -- |
|
|
49
|
+
| `` `X` was never called `` | `tool` is `null`, no match | `passed` — the negative false-pass D4 forbids |
|
|
50
|
+
| `` `X` was called `` | `tool` is `null`, no match | `failed` — a correct agent marked down |
|
|
51
|
+
|
|
52
|
+
One set, one invariant, and both sentences widen together the day a route is
|
|
53
|
+
stamped (F-1342). A second enumeration would be the one that drifts.
|
|
54
|
+
|
|
55
|
+
**Three things differ from the prohibition, each because the polarity flipped:**
|
|
56
|
+
|
|
57
|
+
1. An EMPTY tape reaches a real `failed`, never a skip. `[]` is "the agent
|
|
58
|
+
called nothing", which is exactly the null agent this check exists to score
|
|
59
|
+
at 0; softening it would take the criterion out of the denominator and hand
|
|
60
|
+
that agent its score back.
|
|
61
|
+
2. A tape whose rows carry no `tool` key AT ALL — a recording predating F-1125 —
|
|
62
|
+
is refused as `tool_not_recorded`. The prohibition can read that absence as
|
|
63
|
+
"not a match" and stay safe; reading it the same way here fails a correct
|
|
64
|
+
agent for the age of its tape.
|
|
65
|
+
3. Citations move to the PASS branch. A positive pass has specific rows to point
|
|
66
|
+
at; a positive fail is an absence over the whole tape, with nothing to cite.
|
|
67
|
+
|
|
68
|
+
No existing check id, template, polarity or predicate changed, so no criterion
|
|
69
|
+
that binds today moves to a different check or a different verdict.
|
|
70
|
+
|
|
71
|
+
**What pome-cloud must do.** Pin `0.2.0` on **both** `apps/control-plane` and
|
|
72
|
+
`apps/mcp` — they pin this package exactly and must move together, or
|
|
73
|
+
`save_task` accepts criteria the grader cannot bind. Expect
|
|
74
|
+
`CORPUS_SHAPE_BASELINE` to move only once a task actually writes the new
|
|
75
|
+
sentence; this release adds vocabulary and edits no criterion.
|
|
76
|
+
|
|
77
|
+
**And one thing to CHECK rather than inherit, because its failure is silent.**
|
|
78
|
+
`github.tool-was-called` distinguishes `tool: null` ("this surface declares no
|
|
79
|
+
action" — a real world, and the null agent's) from `tool` ABSENT ("this
|
|
80
|
+
recording predates F-1125" — a skip). The twin writes `null` explicitly on every
|
|
81
|
+
unstamped surface, but `twinHttpEventSchema` types the field
|
|
82
|
+
`.nullable().optional()`, so a tape mapper or jsonb round-trip that DROPS
|
|
83
|
+
null-valued keys would make every read-only run look like a legacy recording —
|
|
84
|
+
and this check would `skipped` the null agent instead of failing it, which is
|
|
85
|
+
the one outcome it exists to prevent. A skip does not announce itself the way a
|
|
86
|
+
wrong verdict does. Confirm a persisted tape row for an unstamped call still
|
|
87
|
+
carries `"tool": null` when the pin lands.
|
|
88
|
+
|
|
3
89
|
## 0.1.8
|
|
4
90
|
|
|
5
91
|
**Grading-vocabulary change: twin-github's seed gained
|
|
@@ -47,4 +47,6 @@ export declare const GITHUB_CHECKS: readonly [import("./check-kind.js").Check<{
|
|
|
47
47
|
state: string;
|
|
48
48
|
}>, import("./check-kind.js").Check<Record<string, never>>, import("./check-kind.js").Check<{
|
|
49
49
|
tool: string;
|
|
50
|
+
}>, import("./check-kind.js").Check<{
|
|
51
|
+
tool: string;
|
|
50
52
|
}>];
|
|
@@ -719,6 +719,9 @@ var commitStatus = defineCheck({
|
|
|
719
719
|
});
|
|
720
720
|
|
|
721
721
|
// ../twin-github/dist/src/check-tape.js
|
|
722
|
+
function citations(events) {
|
|
723
|
+
return events.map((event) => event.event_id).filter((id) => typeof id === "string" && id !== "");
|
|
724
|
+
}
|
|
722
725
|
var noUnsupportedEndpoint = defineCheck({
|
|
723
726
|
id: "github.no-unsupported-endpoint",
|
|
724
727
|
description: 'Scans the recorded call tape for any request the twin answered with fidelity "unsupported" \u2014 a route it does not implement, answered 501. It asserts nothing about whether the run SUCCEEDED, and nothing about calls that were merely rejected: a 404 or a 422 from a route the twin does implement is a semantic answer and passes this check. The tape is scoped to this twin by the engine before the check sees it, so an unsupported call to a DIFFERENT twin in a multi-twin session cannot fail it.',
|
|
@@ -760,7 +763,7 @@ var noUnsupportedEndpoint = defineCheck({
|
|
|
760
763
|
reason: `no unsupported GitHub endpoint was called (${tape.length} call(s) inspected)`
|
|
761
764
|
};
|
|
762
765
|
}
|
|
763
|
-
const evidenceEventIds = unsupported
|
|
766
|
+
const evidenceEventIds = citations(unsupported);
|
|
764
767
|
const outcome = {
|
|
765
768
|
passed: false,
|
|
766
769
|
reason: `${unsupported.length} unsupported GitHub call(s): [${unsupported.map((event) => event.path ?? "?").join(", ")}]`
|
|
@@ -811,7 +814,7 @@ var toolNeverCalled = defineCheck({
|
|
|
811
814
|
reason: `\`${args.tool}\` was never called (${tape.length} call(s) inspected)`
|
|
812
815
|
};
|
|
813
816
|
}
|
|
814
|
-
const evidenceEventIds = calls
|
|
817
|
+
const evidenceEventIds = citations(calls);
|
|
815
818
|
const outcome = {
|
|
816
819
|
passed: false,
|
|
817
820
|
reason: `${calls.length} call(s) to \`${args.tool}\`: [${calls.map((event) => `${event.method ?? "?"} ${event.path ?? "?"}`).join(", ")}]`
|
|
@@ -819,6 +822,68 @@ var toolNeverCalled = defineCheck({
|
|
|
819
822
|
return evidenceEventIds.length > 0 ? { ...outcome, evidenceEventIds } : outcome;
|
|
820
823
|
}
|
|
821
824
|
});
|
|
825
|
+
var toolWasCalled = defineCheck({
|
|
826
|
+
id: "github.tool-was-called",
|
|
827
|
+
description: "Scans the recorded call tape for a request that invoked the named twin action, and passes if one did. The action is matched on the recorded `tool` field, which the runtime stamps identically for an MCP `tools/call` and for the REST route that performs the same thing \u2014 so it asserts about the ACTION, not about the transport the examinee chose. It counts an ATTEMPT, exactly as its prohibition sibling does: a call the twin rejected (bad arguments, 4xx) still called the action, so this measures what the examinee REACHED FOR and never whether it succeeded \u2014 a task that needs the outcome must assert the outcome on state. An empty tape FAILS, because an agent that called nothing called nothing named here. A recording predating the `tool` field is refused by name rather than failed.",
|
|
828
|
+
template: "`{tool}` was called",
|
|
829
|
+
params: { tool: toolActionName },
|
|
830
|
+
substrate: "tape",
|
|
831
|
+
// Nothing in the seed can satisfy it and only the examinee acting can, which
|
|
832
|
+
// is the whole property: declared, never inferred from the English (F-1070).
|
|
833
|
+
polarity: () => "positive",
|
|
834
|
+
// The action name IS a caller-supplied literal hunted for in a substrate, so
|
|
835
|
+
// it is declared — and the engine's door-side skip matters MORE here than on
|
|
836
|
+
// the prohibition. A redactor that ate the name would leave this check finding
|
|
837
|
+
// nothing, i.e. failing an agent that did the work.
|
|
838
|
+
subject: (args) => args.tool,
|
|
839
|
+
// Null, and admitted in `HONEST_NULL_MUTANTS`. Same closed-set argument as the
|
|
840
|
+
// sibling: the only substitutable value is the OTHER assertable action, which
|
|
841
|
+
// an agent may well have called too, and a value outside the set does not
|
|
842
|
+
// re-bind at all.
|
|
843
|
+
vacuityMutant: () => null,
|
|
844
|
+
// A POSITIVE check, so the passing world is the one where the action WAS
|
|
845
|
+
// called. The failing world is deliberately NOT an empty tape: `[]` fails
|
|
846
|
+
// through "the agent did nothing", which is the reason an empty world already
|
|
847
|
+
// gives, and `probeDiscrimination`'s third arm rejects a failing world that
|
|
848
|
+
// fails for that reason. This one fails through the ASSERTION — the agent
|
|
849
|
+
// acted, stamped an action, and it was not this one.
|
|
850
|
+
discriminatingWorlds: ({ tool }) => {
|
|
851
|
+
const other = TAPE_ASSERTABLE_TOOLS.find((name) => name !== tool) ?? null;
|
|
852
|
+
return {
|
|
853
|
+
passing: tapeWorld([
|
|
854
|
+
{ twin: "github", method: "GET", path: "/repos/acme/api", status: 200, tool: null, event_id: "evt_read" },
|
|
855
|
+
{ twin: "github", method: "POST", path: "/repos/acme/api/statuses/abc", status: 201, tool, event_id: "evt_did" }
|
|
856
|
+
]),
|
|
857
|
+
failing: tapeWorld([
|
|
858
|
+
{ twin: "github", method: "GET", path: "/repos/acme/api", status: 200, tool: null, event_id: "evt_read" },
|
|
859
|
+
{ twin: "github", method: "POST", path: "/s/ses_1/mcp", status: 200, tool: other, event_id: "evt_other" }
|
|
860
|
+
])
|
|
861
|
+
};
|
|
862
|
+
},
|
|
863
|
+
evaluate(args, { tape }) {
|
|
864
|
+
if (tape === null)
|
|
865
|
+
return { passed: false, reason: "tape_missing", status: "skipped" };
|
|
866
|
+
if (tape.length > 0 && !tape.some((event) => event.tool !== void 0)) {
|
|
867
|
+
return { passed: false, status: "skipped", reason: "tool_not_recorded" };
|
|
868
|
+
}
|
|
869
|
+
const calls = tape.filter((event) => event.tool === args.tool);
|
|
870
|
+
if (calls.length === 0) {
|
|
871
|
+
const recorded = [
|
|
872
|
+
...new Set(tape.map((event) => event.tool).filter((name) => typeof name === "string" && name !== ""))
|
|
873
|
+
];
|
|
874
|
+
return {
|
|
875
|
+
passed: false,
|
|
876
|
+
reason: `\`${args.tool}\` was never called (${tape.length} call(s) inspected; actions recorded: [${recorded.join(", ")}])`
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
const evidenceEventIds = citations(calls);
|
|
880
|
+
const outcome = {
|
|
881
|
+
passed: true,
|
|
882
|
+
reason: `${calls.length} call(s) to \`${args.tool}\`: [${calls.map((event) => `${event.method ?? "?"} ${event.path ?? "?"}`).join(", ")}]`
|
|
883
|
+
};
|
|
884
|
+
return evidenceEventIds.length > 0 ? { ...outcome, evidenceEventIds } : outcome;
|
|
885
|
+
}
|
|
886
|
+
});
|
|
822
887
|
|
|
823
888
|
// ../twin-github/dist/src/checks.js
|
|
824
889
|
var GITHUB_CHECKS = [
|
|
@@ -842,7 +907,13 @@ var GITHUB_CHECKS = [
|
|
|
842
907
|
// for first to the ones a specialised task needs — and these are the only ones
|
|
843
908
|
// that assert about the RUN rather than the world it left behind.
|
|
844
909
|
noUnsupportedEndpoint,
|
|
845
|
-
toolNeverCalled
|
|
910
|
+
toolNeverCalled,
|
|
911
|
+
// F-1338. Adjacent to its prohibition for the reason the two repo-scoped
|
|
912
|
+
// deltas sit together above: an author reaching for one usually wants to see
|
|
913
|
+
// the other, and these two are the same predicate read in opposite
|
|
914
|
+
// directions off one closed set of actions. It is appended rather than
|
|
915
|
+
// slotted in front so the existing listing order does not move.
|
|
916
|
+
toolWasCalled
|
|
846
917
|
];
|
|
847
918
|
var seedSchema = z.object({
|
|
848
919
|
users: z.array(z.object({
|
package/dist/github.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { GITHUB_CHECKS, defaultSeedState, parseSeed, seedSchema } from './chunk-
|
|
1
|
+
export { GITHUB_CHECKS, defaultSeedState, parseSeed, seedSchema } from './chunk-XTSASWF4.js';
|
|
2
2
|
import './chunk-W2JNYULF.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,8 @@ declare const TWIN_CHECKS: {
|
|
|
81
81
|
state: string;
|
|
82
82
|
}>, _pome_sh_twin_github_checks.Check<Record<string, never>>, _pome_sh_twin_github_checks.Check<{
|
|
83
83
|
tool: string;
|
|
84
|
+
}>, _pome_sh_twin_github_checks.Check<{
|
|
85
|
+
tool: string;
|
|
84
86
|
}>];
|
|
85
87
|
readonly slack: readonly [_pome_sh_twin_slack_checks.Check<{
|
|
86
88
|
channel: string;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import './chunk-ZXE6LAM3.js';
|
|
2
|
-
import { GITHUB_CHECKS } from './chunk-
|
|
3
|
-
export { GITHUB_CHECKS, defaultSeedState as defaultGitHubSeed, seedSchema as githubSeedSchema, parseSeed as parseGitHubSeed } from './chunk-
|
|
2
|
+
import { GITHUB_CHECKS } from './chunk-XTSASWF4.js';
|
|
3
|
+
export { GITHUB_CHECKS, defaultSeedState as defaultGitHubSeed, seedSchema as githubSeedSchema, parseSeed as parseGitHubSeed } from './chunk-XTSASWF4.js';
|
|
4
4
|
import { GMAIL_CHECKS } from './chunk-KTTJCEDH.js';
|
|
5
5
|
export { GMAIL_CHECKS, defaultSeedState as defaultGmailSeed, gmailSeedSchema, parseSeed as parseGmailSeed } from './chunk-KTTJCEDH.js';
|
|
6
6
|
import { LINEAR_CHECKS } from './chunk-THOSO63W.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pome-sh/checks",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Pome's grading vocabulary — the check declarations, seed schemas and default seeds of all five digital twins, plus the check DSL they are written in. Declarations only: no twin server, no database, no routes, no tools.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"checks"
|
|
67
67
|
],
|
|
68
68
|
"scripts": {
|
|
69
|
-
"build": "tsup && node scripts/bundle-declarations.mjs",
|
|
69
|
+
"build": "tsup && node ../../scripts/bundle-declarations.mjs .",
|
|
70
70
|
"test": "vitest run",
|
|
71
71
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
72
72
|
"prepublishOnly": "npm run build"
|