@pome-sh/checks 0.1.5 → 0.1.7
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 +86 -0
- package/dist/_types/twin-github/src/types.d.ts +4 -0
- package/dist/_types/twin-gmail/src/check-state.d.ts +38 -0
- package/dist/_types/wire/index.d.ts +28 -9
- package/dist/{chunk-SA23X6PB.js → chunk-KTTJCEDH.js} +10 -6
- package/dist/gmail.js +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# @pome-sh/checks
|
|
2
2
|
|
|
3
|
+
## 0.1.7
|
|
4
|
+
|
|
5
|
+
A section a check's verdict reads is now measured HERE, where the worlds are
|
|
6
|
+
authored, instead of being left to an instrument that cannot see it (F-1437).
|
|
7
|
+
|
|
8
|
+
pome-cloud's `findVacuousStateSectionReaders` derives its candidate sections
|
|
9
|
+
from what DIFFERS between a check's two discriminating worlds. That rule is what
|
|
10
|
+
stops it reporting every section a world carries for realism, and it is also a
|
|
11
|
+
blind spot it has documented rather than folded into its zero: a section
|
|
12
|
+
IDENTICAL in both worlds is never a candidate, so it is never deleted, so a
|
|
13
|
+
verdict that reads it vacuously is invisible there. No widening fixes that — with
|
|
14
|
+
both worlds carrying the same value there is no failing value to swap in, hence
|
|
15
|
+
no proof the verdict reads the section at all.
|
|
16
|
+
|
|
17
|
+
`test/section-read-sweep.test.ts` reaches it from the other side. It hands
|
|
18
|
+
`evaluate` a recording view of the state tree, so every top-level section the
|
|
19
|
+
predicate ASKS FOR is on the record whether or not the worlds disagree about it;
|
|
20
|
+
then, for exactly the sections the worlds agree on, it runs the detector's own
|
|
21
|
+
step 3 — delete from the passing world, re-evaluate — and a verdict that stays a
|
|
22
|
+
bare `passed` is named. Both trees, `seed` as well as `final`, because the
|
|
23
|
+
detector probes `final` only. Over the shipped vocabulary: 38 state
|
|
24
|
+
declarations and 34 agreed-on section reads (28 on `final`, 6 on `seed`), of
|
|
25
|
+
which 26 are discharged by measurement — 22 return `state_incomplete` and 4 fail
|
|
26
|
+
honestly — and 8 are declared exceptions where the twin reads an absent section
|
|
27
|
+
as a VALUE: the
|
|
28
|
+
`exportBounds` block, whose absence `isTruncated` deliberately answers as "not
|
|
29
|
+
truncated" so an export predating the cap is not skipped wholesale, and
|
|
30
|
+
`gmail.mailbox-label-count`'s `mailboxes`, where an absent collection means
|
|
31
|
+
"count by `mailboxEmail`" and the evidence collections are guarded by name one
|
|
32
|
+
line above. The exception list is pinned in both directions, so a row that stops
|
|
33
|
+
being needed fails the same way an unexplained finding does.
|
|
34
|
+
|
|
35
|
+
**ONE DECLARATION MOVED, AND IT IS A FIXTURE, NOT A VERDICT.**
|
|
36
|
+
`gmail.message-has-label`'s discriminating worlds built their user label as
|
|
37
|
+
`userLabel(label, label)` — an `id === name` shape only a SYSTEM label has. With
|
|
38
|
+
it, `labelIdsFor`'s bare-display-name fallback answered the join unaided, so
|
|
39
|
+
deleting `labels` from the passing world changed nothing and the verdict stayed
|
|
40
|
+
`passed`: a section the verdict demonstrably reads, proven unread by the only
|
|
41
|
+
world that speaks for it. The label now carries a minted id (`Label_1`) and the
|
|
42
|
+
`messageLabels` rows carry that id, so the deletion moves the verdict to
|
|
43
|
+
`failed` and the read is on the record.
|
|
44
|
+
|
|
45
|
+
Nothing in any `evaluate` changed. No check id, template, params, polarity,
|
|
46
|
+
subject or vacuity mutant moved, and no criterion changes from bound to unbound
|
|
47
|
+
or from passing to failing on any real export. `discriminatingWorlds` is a
|
|
48
|
+
fixture the probes run; a run's grade does not read it.
|
|
49
|
+
|
|
50
|
+
`labelIdsFor`'s comment counted TWO callers that must guard `state.labels`. There
|
|
51
|
+
are three — `messageCarriesLabel` in the same file is the third, reached by
|
|
52
|
+
`gmail.message-has-label`, and it answers `false` rather than refusing. That is
|
|
53
|
+
the safe direction for a check whose polarity is always positive, so it is left
|
|
54
|
+
alone and the comment now says which caller does which, with the sweep as the
|
|
55
|
+
thing that keeps it measured rather than assumed.
|
|
56
|
+
|
|
57
|
+
**What pome-cloud must do.** Pin `0.1.7` in `apps/control-plane` AND `apps/mcp`
|
|
58
|
+
— they must move together or `save_task` accepts criteria the grader cannot
|
|
59
|
+
bind — and restate the blind spot in `findVacuousStateSectionReaders`' doc
|
|
60
|
+
comment as a property of the declarations rather than of the detector, with
|
|
61
|
+
`docs/grading/a3-bucket-ledger.md` §5 item 9 following it. `declared-pin.test.ts`
|
|
62
|
+
needs no other edit: the detector's finding set over the new declarations is
|
|
63
|
+
byte-identical, because `gmail.message-has-label`'s worlds still differ on
|
|
64
|
+
`messageLabels` alone and deleting it still returns `state_incomplete`.
|
|
65
|
+
|
|
66
|
+
## 0.1.6
|
|
67
|
+
|
|
68
|
+
`gmail.mailbox-label-count` now refuses instead of scoring a free pass (F-1441)
|
|
69
|
+
— the same class as 0.1.5's `slack.no-reaction-added`, found live in twin-gmail
|
|
70
|
+
on a worse criterion. Its polarity flips NEGATIVE at count 0, so the vacuous
|
|
71
|
+
pass handed a point to an agent that did the forbidden thing.
|
|
72
|
+
|
|
73
|
+
`labelIdsFor` read `state.labels ?? []` with no absence guard. The bare display
|
|
74
|
+
name is always added to the id set so the join survives a capped collection —
|
|
75
|
+
but that only holds for SYSTEM labels, where `id === name`. A USER label's
|
|
76
|
+
minted id differs from its display name by construction (the default seed ships
|
|
77
|
+
`{ id: "Label_follow_up", name: "Follow Up" }`), so with `labels` absent the
|
|
78
|
+
lookup degraded to a name no `messageLabels` row carries, the total came out 0,
|
|
79
|
+
and `0 === 0` passed over an export in which the agent DID apply the label.
|
|
80
|
+
|
|
81
|
+
`labels` is now guarded for both absence and truncation alongside `messages` and
|
|
82
|
+
`messageLabels`, in `gmail.mailbox-label-count` and in the second `labelIdsFor`
|
|
83
|
+
consumer (`oneMessagePerRecipient`, positive polarity and fail-closed today —
|
|
84
|
+
guarded anyway, because safe-by-polarity is how this class survives review).
|
|
85
|
+
`labelIdsFor`'s own comment now states that its bare-id fallback is
|
|
86
|
+
system-labels-only and that callers must guard; `draftRecipients` carries the
|
|
87
|
+
written reason its ticket asked for.
|
|
88
|
+
|
|
3
89
|
## 0.1.5
|
|
4
90
|
|
|
5
91
|
`slack.no-reaction-added` now refuses instead of scoring a free pass (F-1159).
|
|
@@ -243,6 +243,10 @@ export type ReleaseRow = {
|
|
|
243
243
|
prerelease: 0 | 1;
|
|
244
244
|
author_login: string;
|
|
245
245
|
created_at: string;
|
|
246
|
+
/** F-1459 — GitHub's release `updated_at`. Equal to `created_at` on a release
|
|
247
|
+
* that has never been edited, which is every release this twin serves: it has
|
|
248
|
+
* no release-update route. */
|
|
249
|
+
updated_at: string;
|
|
246
250
|
published_at: string | null;
|
|
247
251
|
};
|
|
248
252
|
export type CheckRunRow = {
|
|
@@ -108,6 +108,36 @@ export declare function resolveMessage(state: GmailCheckState, id: string): Reso
|
|
|
108
108
|
* The bare `wanted` is always included so the join still works when the `labels`
|
|
109
109
|
* collection was capped away — a `messageLabels` row carries the bare id
|
|
110
110
|
* regardless.
|
|
111
|
+
*
|
|
112
|
+
* ⚠️ THAT FALLBACK IS SYSTEM-LABELS-ONLY, and the sentence above used to stop
|
|
113
|
+
* short of saying so, which is why the gap read as intentional (F-1441). It
|
|
114
|
+
* holds when `id === name` — `INBOX`, `UNREAD`, `STARRED`. A USER label's
|
|
115
|
+
* minted id differs from its display name by construction (the default seed
|
|
116
|
+
* ships `{ id: "Label_follow_up", name: "Follow Up" }`), so with `labels`
|
|
117
|
+
* absent or capped this degrades to a name no `messageLabels` row carries and
|
|
118
|
+
* the join silently returns nothing.
|
|
119
|
+
*
|
|
120
|
+
* So: this function cannot refuse — it returns a Set, and an empty Set is
|
|
121
|
+
* indistinguishable from "the label was never applied". **Every caller must
|
|
122
|
+
* decide what an absent or capped `labels` collection means before calling**.
|
|
123
|
+
*
|
|
124
|
+
* THERE ARE THREE CALLERS, not the two F-1441 counted, and the third is in this
|
|
125
|
+
* very file:
|
|
126
|
+
* * `gmail.mailbox-label-count` and `gmail.one-message-per-recipient` in
|
|
127
|
+
* check-messages.ts REFUSE — `state_incomplete` / `collection_truncated` —
|
|
128
|
+
* because the first flips NEGATIVE at count 0, where an empty Set is a free
|
|
129
|
+
* point for an agent that did the forbidden thing.
|
|
130
|
+
* * `messageCarriesLabel` below, reached by `gmail.message-has-label`, does
|
|
131
|
+
* NOT refuse: it answers `false`, so an absent `labels` costs that check's
|
|
132
|
+
* agent a point rather than gifting one. Its polarity is always positive, so
|
|
133
|
+
* the direction is safe — but safe-by-polarity is how this class survives
|
|
134
|
+
* review, so it is measured rather than assumed. `section-read-sweep.test.ts`
|
|
135
|
+
* in @pome-sh/checks deletes `labels` from that check's own passing world on
|
|
136
|
+
* every run and asserts the verdict MOVES; before F-1437 it did not, because
|
|
137
|
+
* that world minted the label with `id === name` and the fallback above
|
|
138
|
+
* answered the join unaided.
|
|
139
|
+
*
|
|
140
|
+
* A fourth caller must pick one of those two and say which.
|
|
111
141
|
*/
|
|
112
142
|
export declare function labelIdsFor(state: GmailCheckState, wanted: string): Set<string>;
|
|
113
143
|
/**
|
|
@@ -134,5 +164,13 @@ export declare function resolveLabelByName(state: GmailCheckState, name: string)
|
|
|
134
164
|
* alone would answer "no draft is addressed to anyone", always. A draft whose
|
|
135
165
|
* message did not survive the export contributes nothing rather than throwing;
|
|
136
166
|
* the caller decides whether that emptiness is answerable.
|
|
167
|
+
*
|
|
168
|
+
* ⚠️ Same shape as `labelIdsFor`, same rule, and the reason is written down
|
|
169
|
+
* rather than left to be rediscovered (F-1441): `state.messages ?? []` cannot
|
|
170
|
+
* tell an absent collection from one that holds no match, so this function
|
|
171
|
+
* cannot refuse either. It is safe TODAY only because its sole caller
|
|
172
|
+
* null-checks `final.messages` first (`check-drafts.ts:63`) — safe-by-caller,
|
|
173
|
+
* which is precisely how this class survives review. A second caller must
|
|
174
|
+
* guard, or this must be changed to return a `Resolved`.
|
|
137
175
|
*/
|
|
138
176
|
export declare function draftRecipients(state: GmailCheckState, draft: GmailCheckStateDraft): string[];
|
|
@@ -5,16 +5,35 @@
|
|
|
5
5
|
* and the CLI reads (`recorder-events.ts`), the OpenTelemetry-native extension
|
|
6
6
|
* of that union (`otel/`), and the secret redactor applied on the way to disk
|
|
7
7
|
* (`redaction.ts`). Every twin, the sdk, the adapter and the CLI depend on this
|
|
8
|
-
* package; NOTHING
|
|
9
|
-
* surface — those live in `cli/src/contract/` (F-942).
|
|
8
|
+
* package; NOTHING ON THIS BARREL knows about sessions, tasks, runs or the
|
|
9
|
+
* cloud REST surface — those live in `cli/src/contract/` (F-942).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
11
|
+
* F-1416 NARROWED THAT CLAIM ONCE, and this is the whole of the narrowing: the
|
|
12
|
+
* package now also ships `@pome-sh/wire/run-completeness`, four symbols that
|
|
13
|
+
* read two fields of a `criteria_results` row (`skipped`, `reason`) and return
|
|
14
|
+
* whether a finished run has a verdict to state. That is the one predicate
|
|
15
|
+
* pome-cloud's dashboard and control plane and this repo's CLI all have to
|
|
16
|
+
* agree on, and until F-1416 the CLI's agreement test kept a hand-written copy
|
|
17
|
+
* of it that went stale green. It is run-ADJACENT, so the sentence above would
|
|
18
|
+
* be false if it said "this package"; it is not run vocabulary, because it
|
|
19
|
+
* names no session, task, run id, REST route or column, imports nothing, and
|
|
20
|
+
* takes structural inputs so no cloud type crosses with it. Keeping it OFF this
|
|
21
|
+
* barrel is what keeps the sentence above enforceable rather than merely
|
|
22
|
+
* written down — see below.
|
|
23
|
+
*
|
|
24
|
+
* Three SUBPATH-ONLY surfaces are deliberately absent from this barrel.
|
|
25
|
+
* `@pome-sh/wire/otel/fixtures` (the golden-fixture corpus, a test/dev
|
|
26
|
+
* artifact) and `@pome-sh/wire/correlation` (F-950 — the agent-side
|
|
27
|
+
* AsyncLocalStorage + fetch-patching plumbing that stamps
|
|
28
|
+
* `x-pome-correlation-id`) are absent because only some consumers should pay to
|
|
29
|
+
* LOAD them: importing correlation constructs an AsyncLocalStorage, and no twin
|
|
30
|
+
* is the agent side of that protocol. `@pome-sh/wire/run-completeness` (F-1416)
|
|
31
|
+
* is absent for a different reason — SCOPE, not cost. The five twins, the sdk
|
|
32
|
+
* and the adapter import this barrel and not one of them has a run to ask
|
|
33
|
+
* about, so a symbol they cannot use has no business in their namespace, and an
|
|
34
|
+
* opt-in subpath is what makes the F-942 boundary above a thing CI can check.
|
|
35
|
+
* `test/export-surface.test.ts` pins all three: each subpath's own surface, and
|
|
36
|
+
* its absence from the snapshot below.
|
|
18
37
|
*
|
|
19
38
|
* This file is a THIN BARREL: it re-exports only.
|
|
20
39
|
*/
|
|
@@ -373,10 +373,14 @@ var messageHasLabel = defineCheck({
|
|
|
373
373
|
// lookup rather than the assertion. The label is the scanned literal.
|
|
374
374
|
vacuityMutant: (args) => ({ ...args, label: VACUITY_SENTINEL }),
|
|
375
375
|
discriminatingWorlds: ({ message: id2, label }) => {
|
|
376
|
-
const
|
|
376
|
+
const labelId = "Label_1";
|
|
377
|
+
const labels = [systemLabel("INBOX"), userLabel(labelId, label)];
|
|
377
378
|
const base = { messages: [message(id2)], labels };
|
|
378
379
|
return {
|
|
379
|
-
passing: finalWorld(gmailState({
|
|
380
|
+
passing: finalWorld(gmailState({
|
|
381
|
+
...base,
|
|
382
|
+
messageLabels: [messageLabel(id2, "INBOX"), messageLabel(id2, labelId)]
|
|
383
|
+
})),
|
|
380
384
|
failing: finalWorld(gmailState({ ...base, messageLabels: [messageLabel(id2, "INBOX")] }))
|
|
381
385
|
};
|
|
382
386
|
},
|
|
@@ -430,10 +434,10 @@ var mailboxLabelCount = defineCheck({
|
|
|
430
434
|
},
|
|
431
435
|
evaluate({ mailbox, count, label }, { final }) {
|
|
432
436
|
const wanted = Number(count);
|
|
433
|
-
if (final.messages == null || final.messageLabels == null) {
|
|
437
|
+
if (final.messages == null || final.messageLabels == null || final.labels == null) {
|
|
434
438
|
return { passed: false, status: "skipped", reason: "state_incomplete" };
|
|
435
439
|
}
|
|
436
|
-
if (isTruncated(final, "messages") || isTruncated(final, "messageLabels")) {
|
|
440
|
+
if (isTruncated(final, "messages") || isTruncated(final, "messageLabels") || isTruncated(final, "labels")) {
|
|
437
441
|
return { passed: false, status: "skipped", reason: "collection_truncated" };
|
|
438
442
|
}
|
|
439
443
|
if (final.mailboxes != null && !final.mailboxes.some((mb) => (mb.email ?? "").toLowerCase() === mailbox.toLowerCase())) {
|
|
@@ -490,10 +494,10 @@ var oneMessagePerRecipient = defineCheck({
|
|
|
490
494
|
},
|
|
491
495
|
evaluate({ label, count }, { final }) {
|
|
492
496
|
const wanted = parseCount(count);
|
|
493
|
-
if (final.messages == null || final.messageLabels == null) {
|
|
497
|
+
if (final.messages == null || final.messageLabels == null || final.labels == null) {
|
|
494
498
|
return { passed: false, status: "skipped", reason: "state_incomplete" };
|
|
495
499
|
}
|
|
496
|
-
if (isTruncated(final, "messages") || isTruncated(final, "messageLabels")) {
|
|
500
|
+
if (isTruncated(final, "messages") || isTruncated(final, "messageLabels") || isTruncated(final, "labels")) {
|
|
497
501
|
return { passed: false, status: "skipped", reason: "collection_truncated" };
|
|
498
502
|
}
|
|
499
503
|
const ids = labelIdsFor(final, label);
|
package/dist/gmail.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { GMAIL_CHECKS, defaultSeedState, gmailSeedSchema, parseSeed } from './chunk-
|
|
1
|
+
export { GMAIL_CHECKS, defaultSeedState, gmailSeedSchema, parseSeed } from './chunk-KTTJCEDH.js';
|
|
2
2
|
import './chunk-W2JNYULF.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import './chunk-ZXE6LAM3.js';
|
|
2
2
|
import { GITHUB_CHECKS } from './chunk-JVCGWAZQ.js';
|
|
3
3
|
export { GITHUB_CHECKS, defaultSeedState as defaultGitHubSeed, seedSchema as githubSeedSchema, parseSeed as parseGitHubSeed } from './chunk-JVCGWAZQ.js';
|
|
4
|
-
import { GMAIL_CHECKS } from './chunk-
|
|
5
|
-
export { GMAIL_CHECKS, defaultSeedState as defaultGmailSeed, gmailSeedSchema, parseSeed as parseGmailSeed } from './chunk-
|
|
4
|
+
import { GMAIL_CHECKS } from './chunk-KTTJCEDH.js';
|
|
5
|
+
export { GMAIL_CHECKS, defaultSeedState as defaultGmailSeed, gmailSeedSchema, parseSeed as parseGmailSeed } from './chunk-KTTJCEDH.js';
|
|
6
6
|
import { LINEAR_CHECKS } from './chunk-THOSO63W.js';
|
|
7
7
|
export { LINEAR_CHECKS, defaultSeedState as defaultLinearSeed, linearSeedSchema, parseSeed as parseLinearSeed } from './chunk-THOSO63W.js';
|
|
8
8
|
import { SLACK_CHECKS } from './chunk-MJOHK2NI.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pome-sh/checks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|