agentfootprint 9.76.1 → 9.78.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/CHANGELOG.md +188 -0
- package/CLAUDE.md +2 -0
- package/dist/core/Agent.js +86 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/integrityFindings.js +47 -0
- package/dist/core/agent/integrityFindings.js.map +1 -0
- package/dist/core/agent/stages/callLLM.js +28 -29
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +184 -0
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/tools.js +9 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +10 -0
- package/dist/esm/core/Agent.js +86 -0
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/integrityFindings.d.ts +27 -0
- package/dist/esm/core/agent/integrityFindings.js +43 -0
- package/dist/esm/core/agent/integrityFindings.js.map +1 -0
- package/dist/esm/core/agent/stages/callLLM.d.ts +25 -0
- package/dist/esm/core/agent/stages/callLLM.js +25 -26
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +43 -0
- package/dist/esm/core/agent/stages/toolCalls.js +185 -1
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +112 -0
- package/dist/esm/core/tools.d.ts +63 -0
- package/dist/esm/core/tools.js +17 -0
- package/dist/esm/core/tools.js.map +1 -1
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/integrity/argumentLeaves.d.ts +33 -0
- package/dist/esm/integrity/argumentLeaves.js +49 -0
- package/dist/esm/integrity/argumentLeaves.js.map +1 -0
- package/dist/esm/integrity/column-types/check.d.ts +156 -0
- package/dist/esm/integrity/column-types/check.js +363 -0
- package/dist/esm/integrity/column-types/check.js.map +1 -0
- package/dist/esm/integrity/column-types/types.d.ts +121 -0
- package/dist/esm/integrity/column-types/types.js +107 -0
- package/dist/esm/integrity/column-types/types.js.map +1 -0
- package/dist/esm/integrity/disposition/lifecycle.d.ts +31 -1
- package/dist/esm/integrity/disposition/lifecycle.js +54 -1
- package/dist/esm/integrity/disposition/lifecycle.js.map +1 -1
- package/dist/esm/integrity/empty-lookup/check.d.ts +140 -0
- package/dist/esm/integrity/empty-lookup/check.js +212 -0
- package/dist/esm/integrity/empty-lookup/check.js.map +1 -0
- package/dist/esm/integrity/finding/types.d.ts +23 -2
- package/dist/esm/integrity/finding/types.js.map +1 -1
- package/dist/esm/integrity/unsupported-argument/check.js +11 -27
- package/dist/esm/integrity/unsupported-argument/check.js.map +1 -1
- package/dist/esm/lib/mcp/toolExtras.d.ts +17 -2
- package/dist/esm/lib/mcp/toolExtras.js +5 -2
- package/dist/esm/lib/mcp/toolExtras.js.map +1 -1
- package/dist/esm/lib/trace-toolpack/traceToolpack.js +8 -4
- package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
- package/dist/index.js +26 -3
- package/dist/index.js.map +1 -1
- package/dist/integrity/argumentLeaves.js +54 -0
- package/dist/integrity/argumentLeaves.js.map +1 -0
- package/dist/integrity/column-types/check.js +368 -0
- package/dist/integrity/column-types/check.js.map +1 -0
- package/dist/integrity/column-types/types.js +112 -0
- package/dist/integrity/column-types/types.js.map +1 -0
- package/dist/integrity/disposition/lifecycle.js +54 -1
- package/dist/integrity/disposition/lifecycle.js.map +1 -1
- package/dist/integrity/empty-lookup/check.js +217 -0
- package/dist/integrity/empty-lookup/check.js.map +1 -0
- package/dist/integrity/finding/types.js.map +1 -1
- package/dist/integrity/unsupported-argument/check.js +13 -29
- package/dist/integrity/unsupported-argument/check.js.map +1 -1
- package/dist/lib/mcp/toolExtras.js +4 -1
- package/dist/lib/mcp/toolExtras.js.map +1 -1
- package/dist/lib/trace-toolpack/traceToolpack.js +8 -4
- package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
- package/dist/types/core/Agent.d.ts +10 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/integrityFindings.d.ts +28 -0
- package/dist/types/core/agent/integrityFindings.d.ts.map +1 -0
- package/dist/types/core/agent/stages/callLLM.d.ts +25 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +43 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +112 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/tools.d.ts +63 -0
- package/dist/types/core/tools.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/integrity/argumentLeaves.d.ts +34 -0
- package/dist/types/integrity/argumentLeaves.d.ts.map +1 -0
- package/dist/types/integrity/column-types/check.d.ts +157 -0
- package/dist/types/integrity/column-types/check.d.ts.map +1 -0
- package/dist/types/integrity/column-types/types.d.ts +122 -0
- package/dist/types/integrity/column-types/types.d.ts.map +1 -0
- package/dist/types/integrity/disposition/lifecycle.d.ts +31 -1
- package/dist/types/integrity/disposition/lifecycle.d.ts.map +1 -1
- package/dist/types/integrity/empty-lookup/check.d.ts +141 -0
- package/dist/types/integrity/empty-lookup/check.d.ts.map +1 -0
- package/dist/types/integrity/finding/types.d.ts +23 -2
- package/dist/types/integrity/finding/types.d.ts.map +1 -1
- package/dist/types/integrity/unsupported-argument/check.d.ts.map +1 -1
- package/dist/types/lib/mcp/toolExtras.d.ts +17 -2
- package/dist/types/lib/mcp/toolExtras.d.ts.map +1 -1
- package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* argumentLeaves — what "a string argument" MEANS, in one place.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: a pure generator leaf, zero imports.
|
|
6
|
+
* Role: two checks now read the arguments of a tool call — the choice
|
|
7
|
+
* seam's `unsupported-argument` (did the value come from what the run
|
|
8
|
+
* served?) and the write seam's `empty-lookup` (the run produced this
|
|
9
|
+
* value, and the lookup for it came back empty). They must agree,
|
|
10
|
+
* exactly, about which leaves of an arguments object are candidates
|
|
11
|
+
* and what their dot-paths are: the second check's whole job is to
|
|
12
|
+
* notice something about a value the first one already excused, and
|
|
13
|
+
* two spellings of "every string leaf" would eventually disagree
|
|
14
|
+
* about which value that was.
|
|
15
|
+
*
|
|
16
|
+
* The dot-path is the finding's `predicate` on both sides (`machine`,
|
|
17
|
+
* `filter.hosts.0`), so it is identity-bearing and not a convenience.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.clipValue = exports.MAX_QUOTED_CHARS = exports.MIN_CHECKED_LENGTH = exports.stringLeaves = void 0;
|
|
21
|
+
/** Every string leaf of an arguments object, with its dot-path. */
|
|
22
|
+
function* stringLeaves(node, path) {
|
|
23
|
+
if (typeof node === 'string') {
|
|
24
|
+
yield { path, value: node };
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(node)) {
|
|
28
|
+
for (let i = 0; i < node.length; i++) {
|
|
29
|
+
yield* stringLeaves(node[i], path === '' ? String(i) : `${path}.${String(i)}`);
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (node !== null && typeof node === 'object') {
|
|
34
|
+
for (const [key, value] of Object.entries(node)) {
|
|
35
|
+
yield* stringLeaves(value, path === '' ? key : `${path}.${key}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.stringLeaves = stringLeaves;
|
|
40
|
+
/**
|
|
41
|
+
* Below this, substring matching says nothing. Shared for the same reason the
|
|
42
|
+
* walk is: 'up' and 'a1' land inside unrelated words in any corpus, and a
|
|
43
|
+
* fence that moved on one check and not the other would leave a value one
|
|
44
|
+
* check judges and the other silently skips.
|
|
45
|
+
*/
|
|
46
|
+
exports.MIN_CHECKED_LENGTH = 4;
|
|
47
|
+
/** Longest a single value is quoted at inside a finding's message. */
|
|
48
|
+
exports.MAX_QUOTED_CHARS = 80;
|
|
49
|
+
/** One value, clipped to quoting length. */
|
|
50
|
+
function clipValue(value) {
|
|
51
|
+
return value.length <= exports.MAX_QUOTED_CHARS ? value : `${value.slice(0, exports.MAX_QUOTED_CHARS - 1)}…`;
|
|
52
|
+
}
|
|
53
|
+
exports.clipValue = clipValue;
|
|
54
|
+
//# sourceMappingURL=argumentLeaves.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"argumentLeaves.js","sourceRoot":"","sources":["../../src/integrity/argumentLeaves.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAEH,mEAAmE;AACnE,QAAe,CAAC,CAAC,YAAY,CAC3B,IAAa,EACb,IAAY;IAEZ,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,KAAK,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,OAAO;IACT,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YAC3E,KAAK,CAAC,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;AACH,CAAC;AAnBD,oCAmBC;AAED;;;;;GAKG;AACU,QAAA,kBAAkB,GAAG,CAAC,CAAC;AAEpC,sEAAsE;AACzD,QAAA,gBAAgB,GAAG,EAAE,CAAC;AAEnC,4CAA4C;AAC5C,SAAgB,SAAS,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,MAAM,IAAI,wBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAgB,GAAG,CAAC,CAAC,GAAG,CAAC;AAC/F,CAAC;AAFD,8BAEC"}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* column-types — the tool declared what its rows contain, and the rows say
|
|
4
|
+
* otherwise. Filed at the WRITE seam, at the moment the tool answers.
|
|
5
|
+
*
|
|
6
|
+
* Pattern: pure function over (one declaration, one finished rowset); the
|
|
7
|
+
* SHAPE check, one storey below `empty-lookup` — that one asks
|
|
8
|
+
* whether the answer had any rows, this one asks what is in them.
|
|
9
|
+
* Role: the decidable fragment of "is this rowset the rowset it claims to
|
|
10
|
+
* be?".
|
|
11
|
+
*
|
|
12
|
+
* ── THE MEASURED FAILURES. Three, and they are one shape ────────────────
|
|
13
|
+
*
|
|
14
|
+
* 1. A mapping report wrote `str(m.get("logical_unit_number") or "")`. LUN 0
|
|
15
|
+
* is falsy, so on 2,094 mappings LUN 0 was stored as an EMPTY STRING —
|
|
16
|
+
* and a host group missing the LUN an initiator probes first became
|
|
17
|
+
* indistinguishable from one that had it. The column was numeric; the
|
|
18
|
+
* value was `''`; nothing anywhere disagreed.
|
|
19
|
+
* 2. A capacity view rendered `round(mib / 1024, 1)`, so an 8 MiB disk came
|
|
20
|
+
* out as `0.0 GB` — which reads as NO DISK, i.e. a provisioning failure,
|
|
21
|
+
* during a live desktop-fleet incident.
|
|
22
|
+
* 3. Earlier in the same application, a whole family of tools returned their
|
|
23
|
+
* numbers as quoted strings (`"1240"`). Every chart silently went blank,
|
|
24
|
+
* because nothing downstream could tell a measure from a label.
|
|
25
|
+
*
|
|
26
|
+
* All three are "a number became something else, and nothing noticed at the
|
|
27
|
+
* seam". The library already lets a tool declare what its result IS
|
|
28
|
+
* (`resultKind`, 9.70.0). It did not let a tool declare what its result
|
|
29
|
+
* CONTAINS, so there was nothing for a rowset to be wrong against.
|
|
30
|
+
*
|
|
31
|
+
* ── THE CEILING, and case 2 is the whole argument for stating it ────────
|
|
32
|
+
* This judges TYPE, never MEANING. It can see that a column declared
|
|
33
|
+
* `number` holds a string. It can NEVER see that the string should have been
|
|
34
|
+
* `0`, or that `0.0` should have been `0.0078`. Case 2 above passes this
|
|
35
|
+
* check cleanly — `0.0` is a perfectly good number — and the check says so
|
|
36
|
+
* out loud rather than letting a green row imply otherwise. The ceiling ships
|
|
37
|
+
* as {@link COLUMN_TYPE_CEILING} and is quoted verbatim into every finding,
|
|
38
|
+
* the `EMPTY_LOOKUP_CEILING` law: one owner for the bound, so it cannot drift
|
|
39
|
+
* out of a message and leave a reader believing the library knows more than
|
|
40
|
+
* it does.
|
|
41
|
+
*
|
|
42
|
+
* ── TWO FINDINGS, because the field bug turned on the difference ────────
|
|
43
|
+
* • `column-type-mismatch` — the column is THERE and holds the wrong thing.
|
|
44
|
+
* Cases 1 and 3.
|
|
45
|
+
* • `missing-column` — the column the author declared is in NO row of the
|
|
46
|
+
* result. Nothing to type-check; the promise was broken one level up.
|
|
47
|
+
* Collapsing them would recreate the exact ambiguity the LUN report died of:
|
|
48
|
+
* "the value is not what it should be" and "the value is not there" send a
|
|
49
|
+
* person to two different files, and a checker that says only "something is
|
|
50
|
+
* off with logical_unit_number" has helped with neither.
|
|
51
|
+
*
|
|
52
|
+
* ── DECLARED, NEVER INFERRED ────────────────────────────────────────────
|
|
53
|
+
* A tool is this check's subject only because its author wrote
|
|
54
|
+
* `resultColumns`. Nothing here sniffs a type off the data — sniffing is
|
|
55
|
+
* precisely what the consumers do today, and precisely what produced the
|
|
56
|
+
* failures above: one stray `''` demotes a numeric column to text, and the
|
|
57
|
+
* demotion is silent.
|
|
58
|
+
*
|
|
59
|
+
* ── WHAT IS NOT JUDGED ──────────────────────────────────────────────────
|
|
60
|
+
* See {@link readRowset}. A result is read only when it is an array of plain
|
|
61
|
+
* objects with at least one row. Everything else — prose, a `null`, a bespoke
|
|
62
|
+
* `{ rows: [...] }` wrapper, a claim ticket, AND the zero-row result — is
|
|
63
|
+
* `not-applicable`, filed as a ROW. The zero-row case belongs to the
|
|
64
|
+
* neighbour (`empty-lookup`) and is deliberately not stolen: an empty result
|
|
65
|
+
* has no columns to be wrong about, and filing `missing-column` for every
|
|
66
|
+
* declared column of an empty answer would turn one honest emptiness into a
|
|
67
|
+
* pile of false accusations.
|
|
68
|
+
*/
|
|
69
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
|
+
exports.columnTypesOf = exports.readRowset = exports.COLUMN_TYPE_CEILING = void 0;
|
|
71
|
+
const argumentLeaves_js_1 = require("../argumentLeaves.js");
|
|
72
|
+
const types_js_1 = require("./types.js");
|
|
73
|
+
/**
|
|
74
|
+
* THE CEILING, as one string with one owner.
|
|
75
|
+
*
|
|
76
|
+
* Quoted verbatim into every finding's message, into this folder's README and
|
|
77
|
+
* into the docs page, so the bound cannot drift out of one of them.
|
|
78
|
+
*/
|
|
79
|
+
exports.COLUMN_TYPE_CEILING = 'This judges TYPE, never MEANING — it can see that a column declared `number` holds a ' +
|
|
80
|
+
'string, and it can never see that the string should have been 0, or that a 0.0 should ' +
|
|
81
|
+
'have been an 8; a column whose every value has its declared type passes here and can ' +
|
|
82
|
+
'still be wrong.';
|
|
83
|
+
/**
|
|
84
|
+
* READ a finished result as a rowset, or decline to.
|
|
85
|
+
*
|
|
86
|
+
* The one readable shape, and why only this one: an ARRAY OF PLAIN OBJECTS
|
|
87
|
+
* with at least one row. That is what a rowset is on this wire, it is what
|
|
88
|
+
* every consumer named in the docs page already expects, and it is the same
|
|
89
|
+
* `Array.isArray` law the neighbouring check reads by — the two must never
|
|
90
|
+
* disagree about what a rowset is.
|
|
91
|
+
*
|
|
92
|
+
* `undefined` (⇒ `not-applicable`, a ROW) for everything else:
|
|
93
|
+
* • a non-array — prose, a `null`, a `{ rows: [...] }` wrapper, a ticket;
|
|
94
|
+
* • an array holding anything that is not a plain object — a list of
|
|
95
|
+
* strings has no columns, and inventing some is how a checker starts
|
|
96
|
+
* lying;
|
|
97
|
+
* • an array of ZERO rows — an empty answer has no columns to be wrong
|
|
98
|
+
* about, and it is the neighbour's subject, not this one's.
|
|
99
|
+
*/
|
|
100
|
+
function readRowset(value) {
|
|
101
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
102
|
+
return undefined;
|
|
103
|
+
for (const row of value) {
|
|
104
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row))
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return { rows: value };
|
|
108
|
+
}
|
|
109
|
+
exports.readRowset = readRowset;
|
|
110
|
+
/** Most columns named in one refusal sentence — a 200-column declaration
|
|
111
|
+
* must not be able to write the context window. */
|
|
112
|
+
const MAX_NAMED_IN_REFUSAL = 5;
|
|
113
|
+
/**
|
|
114
|
+
* Judge one finished call to a tool that declared its result's columns.
|
|
115
|
+
*
|
|
116
|
+
* @param call the call, the declaration, and what the library could read of
|
|
117
|
+
* the result. The caller has already established the tool declared
|
|
118
|
+
* `resultColumns` and that the dial is on; nothing here re-decides arming.
|
|
119
|
+
* @param epoch the run iteration, stamped on every witness.
|
|
120
|
+
*/
|
|
121
|
+
function columnTypesOf(call, epoch) {
|
|
122
|
+
// A shape the library cannot read as a rowset is not a pass and not a
|
|
123
|
+
// fail. It is the check meeting a subject that is out of its scope BY
|
|
124
|
+
// RULE, which is what `not-applicable` was minted for.
|
|
125
|
+
if (call.reading === undefined)
|
|
126
|
+
return { findings: [], disposition: 'not-applicable' };
|
|
127
|
+
const rows = call.reading.rows;
|
|
128
|
+
const subject = { kind: 'tool', id: call.toolName };
|
|
129
|
+
const findings = [];
|
|
130
|
+
const violations = [];
|
|
131
|
+
const missing = [];
|
|
132
|
+
for (const column of (0, types_js_1.normalizeColumns)(call.columns)) {
|
|
133
|
+
// ABSENT FROM EVERY ROW is its own finding, and it is checked FIRST so a
|
|
134
|
+
// column nobody delivered can never also be reported as mistyped. The
|
|
135
|
+
// two would be counting the same silence twice, and a reader chasing
|
|
136
|
+
// both would find one bug.
|
|
137
|
+
if (rows.every((row) => !(column.name in row))) {
|
|
138
|
+
missing.push(column.name);
|
|
139
|
+
findings.push({
|
|
140
|
+
kind: 'missing-column',
|
|
141
|
+
seam: 'write',
|
|
142
|
+
subjects: [subject],
|
|
143
|
+
// The column name IS the discriminator: two columns of one result
|
|
144
|
+
// are two findings, exactly as two arguments of one call are at the
|
|
145
|
+
// choice seam.
|
|
146
|
+
predicate: column.name,
|
|
147
|
+
witnesses: [
|
|
148
|
+
{
|
|
149
|
+
subject,
|
|
150
|
+
predicate: column.name,
|
|
151
|
+
value: `declared ${column.type}`,
|
|
152
|
+
epoch,
|
|
153
|
+
stratum: 'asserted',
|
|
154
|
+
provenance: `Tool.resultColumns on '${call.toolName}', as its author wrote it`,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
subject,
|
|
158
|
+
predicate: column.name,
|
|
159
|
+
value: 'present in no row',
|
|
160
|
+
epoch,
|
|
161
|
+
stratum: 'asserted',
|
|
162
|
+
provenance: `tool call ${call.toolCallId}: the result, as the tool returned it (${rows.length} row${rows.length === 1 ? '' : 's'})`,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
epoch,
|
|
166
|
+
message: `'${call.toolName}' declares a column '${column.name}' (${column.type}) that is in ` +
|
|
167
|
+
`NONE of the ${rows.length} row${rows.length === 1 ? '' : 's'} it returned. This is ` +
|
|
168
|
+
`not a mistyped value — the column is simply not there, so anything downstream that ` +
|
|
169
|
+
`keys on it reads a rowset that cannot answer, and a row missing the column is ` +
|
|
170
|
+
`indistinguishable from a row whose value is nothing. ${exports.COLUMN_TYPE_CEILING} ` +
|
|
171
|
+
`Call id ${call.toolCallId}. ${outcomeClause(call.mode)}`,
|
|
172
|
+
});
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const violation = judgeColumn(rows, column.name, column.type, column.nullable);
|
|
176
|
+
if (violation === undefined)
|
|
177
|
+
continue;
|
|
178
|
+
violations.push(violation);
|
|
179
|
+
findings.push({
|
|
180
|
+
kind: 'column-type-mismatch',
|
|
181
|
+
seam: 'write',
|
|
182
|
+
subjects: [subject],
|
|
183
|
+
predicate: column.name,
|
|
184
|
+
witnesses: [
|
|
185
|
+
{
|
|
186
|
+
subject,
|
|
187
|
+
predicate: column.name,
|
|
188
|
+
value: `declared ${column.type}${column.nullable ? ' (nullable)' : ''}`,
|
|
189
|
+
epoch,
|
|
190
|
+
stratum: 'asserted',
|
|
191
|
+
provenance: `Tool.resultColumns on '${call.toolName}', as its author wrote it`,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
subject,
|
|
195
|
+
predicate: column.name,
|
|
196
|
+
value: `${violation.rows} of ${violation.ofRows} rows hold ${violation.got} — first: ${violation.sample}`,
|
|
197
|
+
epoch,
|
|
198
|
+
stratum: 'asserted',
|
|
199
|
+
provenance: `tool call ${call.toolCallId}: the result, as the tool returned it`,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
epoch,
|
|
203
|
+
message: `'${call.toolName}' declares column '${column.name}' as ${column.type}, and ` +
|
|
204
|
+
`${violation.rows} of ${violation.ofRows} row${violation.ofRows === 1 ? '' : 's'} hold ` +
|
|
205
|
+
`something else — the first is ${violation.sample} (${violation.got}). ` +
|
|
206
|
+
`${nullableHint(column.nullable, violation.got)}${exports.COLUMN_TYPE_CEILING} ` +
|
|
207
|
+
`Call id ${call.toolCallId}. ${outcomeClause(call.mode)}`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (findings.length === 0)
|
|
211
|
+
return { findings, disposition: 'checked-pass' };
|
|
212
|
+
return {
|
|
213
|
+
findings,
|
|
214
|
+
disposition: 'checked-fail',
|
|
215
|
+
...(call.mode === 'enforce' && {
|
|
216
|
+
refusal: refusalSentence(call.toolName, violations, missing),
|
|
217
|
+
}),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
exports.columnTypesOf = columnTypesOf;
|
|
221
|
+
// ─── Judging one column ────────────────────────────────────────────
|
|
222
|
+
/**
|
|
223
|
+
* Walk every row of one declared column and answer with the disagreement, or
|
|
224
|
+
* `undefined` when there is none.
|
|
225
|
+
*
|
|
226
|
+
* COUNTED, not sampled: the row count is what turns "somebody's data is odd"
|
|
227
|
+
* into "2,094 mappings are wrong", which is the sentence that got the field
|
|
228
|
+
* bug fixed. The value ECHOED is the first offender only — one is enough to
|
|
229
|
+
* recognize the shape, and echoing every one would put a tool's whole result
|
|
230
|
+
* into a finding message.
|
|
231
|
+
*/
|
|
232
|
+
function judgeColumn(rows, name, type, nullable) {
|
|
233
|
+
let count = 0;
|
|
234
|
+
let sample;
|
|
235
|
+
let got = '';
|
|
236
|
+
for (const row of rows) {
|
|
237
|
+
const present = name in row;
|
|
238
|
+
const value = present ? row[name] : undefined;
|
|
239
|
+
// "No value" is one idea with three spellings, and they are judged
|
|
240
|
+
// identically: an absent key, a `null` and an `undefined` all say the
|
|
241
|
+
// row has nothing here. `nullable` is the author's word for "that is
|
|
242
|
+
// fine"; without it, nothing is a violation like any other, because the
|
|
243
|
+
// recorded failure was a value that went missing and left a placeholder.
|
|
244
|
+
const empty = !present || value === null || value === undefined;
|
|
245
|
+
if (empty) {
|
|
246
|
+
if (nullable)
|
|
247
|
+
continue;
|
|
248
|
+
count += 1;
|
|
249
|
+
if (sample === undefined) {
|
|
250
|
+
sample = present ? String(value) : 'no such key';
|
|
251
|
+
got = present ? (value === null ? 'null' : 'undefined') : 'missing';
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (matchesType(value, type))
|
|
256
|
+
continue;
|
|
257
|
+
count += 1;
|
|
258
|
+
if (sample === undefined) {
|
|
259
|
+
sample = renderValue(value);
|
|
260
|
+
got = typeName(value);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (count === 0 || sample === undefined)
|
|
264
|
+
return undefined;
|
|
265
|
+
return { column: name, declared: type, rows: count, ofRows: rows.length, sample, got };
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Does one value have the declared type?
|
|
269
|
+
*
|
|
270
|
+
* The bias is the evidence gate's bias, for the evidence gate's reason: a
|
|
271
|
+
* missed mismatch is a miss, a false accusation costs a real person a real
|
|
272
|
+
* investigation. So `date` accepts anything `Date.parse` accepts, which is
|
|
273
|
+
* permissive enough that a `'2024'` passes — a known, stated false negative,
|
|
274
|
+
* and the right direction to be wrong in.
|
|
275
|
+
*/
|
|
276
|
+
function matchesType(value, type) {
|
|
277
|
+
switch (type) {
|
|
278
|
+
case 'number':
|
|
279
|
+
// FINITE. `NaN` and the infinities are numbers that mean "no number",
|
|
280
|
+
// they serialize to `null` over JSON, and an axis handed one draws
|
|
281
|
+
// nothing — which is failure 3's symptom exactly.
|
|
282
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
283
|
+
case 'string':
|
|
284
|
+
return typeof value === 'string';
|
|
285
|
+
case 'boolean':
|
|
286
|
+
return typeof value === 'boolean';
|
|
287
|
+
case 'date':
|
|
288
|
+
if (value instanceof Date)
|
|
289
|
+
return !Number.isNaN(value.getTime());
|
|
290
|
+
return typeof value === 'string' && !Number.isNaN(Date.parse(value));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/** What a value IS, in the words a person would use. */
|
|
294
|
+
function typeName(value) {
|
|
295
|
+
if (Array.isArray(value))
|
|
296
|
+
return 'array';
|
|
297
|
+
if (value instanceof Date)
|
|
298
|
+
return 'an invalid Date';
|
|
299
|
+
if (typeof value === 'number')
|
|
300
|
+
return Number.isFinite(value) ? 'number' : `the number ${value}`;
|
|
301
|
+
if (typeof value === 'object')
|
|
302
|
+
return 'object';
|
|
303
|
+
return typeof value;
|
|
304
|
+
}
|
|
305
|
+
/** One value, quoted the way a person would recognize it and clipped so a
|
|
306
|
+
* 5 MB cell cannot ride a finding into the record. */
|
|
307
|
+
function renderValue(value) {
|
|
308
|
+
if (typeof value === 'string')
|
|
309
|
+
return `"${(0, argumentLeaves_js_1.clipValue)(value)}"`;
|
|
310
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
311
|
+
return String(value);
|
|
312
|
+
if (value instanceof Date)
|
|
313
|
+
return `a Date`;
|
|
314
|
+
let text;
|
|
315
|
+
try {
|
|
316
|
+
text = JSON.stringify(value) ?? String(value);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
text = String(value);
|
|
320
|
+
}
|
|
321
|
+
return text.length > argumentLeaves_js_1.MAX_QUOTED_CHARS ? `${text.slice(0, argumentLeaves_js_1.MAX_QUOTED_CHARS - 1)}…` : text;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* The one-word fix, named in the message that reports the problem.
|
|
325
|
+
*
|
|
326
|
+
* Only when the offender is an ABSENCE — that is the case where the author
|
|
327
|
+
* may simply have meant "this column can be empty", and a finding that
|
|
328
|
+
* reports a legitimate null without naming the word that legitimizes it is
|
|
329
|
+
* the noise that gets a check switched off.
|
|
330
|
+
*/
|
|
331
|
+
function nullableHint(nullable, got) {
|
|
332
|
+
if (nullable)
|
|
333
|
+
return '';
|
|
334
|
+
if (got !== 'null' && got !== 'undefined' && got !== 'missing')
|
|
335
|
+
return '';
|
|
336
|
+
return ('If this column may legitimately carry no value, say so once — ' +
|
|
337
|
+
"`{ type: '…', nullable: true }` — and rows with nothing in them stop being violations. ");
|
|
338
|
+
}
|
|
339
|
+
/** What the boundary did, stated in the finding rather than assumed by it. */
|
|
340
|
+
function outcomeClause(mode) {
|
|
341
|
+
return mode === 'enforce'
|
|
342
|
+
? 'The result was REFUSED: the model reads a teaching sentence instead of these rows.'
|
|
343
|
+
: 'Nothing here blocked the call, changed the result, or retried anything — the model reads the rows exactly as the tool returned them.';
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* The teaching refusal, for `enforce`.
|
|
347
|
+
*
|
|
348
|
+
* The `applyResultCeiling` sentence shape, deliberately: what was wrong, what
|
|
349
|
+
* to do about it, and "No data was returned" — because a model that is handed
|
|
350
|
+
* a partial or truncated answer cannot tell the data ends where the cut
|
|
351
|
+
* happened, and fabricates from the part it saw.
|
|
352
|
+
*/
|
|
353
|
+
function refusalSentence(toolName, violations, missing) {
|
|
354
|
+
const parts = [];
|
|
355
|
+
for (const v of violations.slice(0, MAX_NAMED_IN_REFUSAL)) {
|
|
356
|
+
parts.push(`'${v.column}' is declared ${v.declared} and ${v.rows} of ${v.ofRows} rows hold ` +
|
|
357
|
+
`${v.got} (first: ${v.sample})`);
|
|
358
|
+
}
|
|
359
|
+
for (const name of missing.slice(0, Math.max(0, MAX_NAMED_IN_REFUSAL - parts.length))) {
|
|
360
|
+
parts.push(`'${name}' is declared but present in no row`);
|
|
361
|
+
}
|
|
362
|
+
const more = violations.length + missing.length - parts.length;
|
|
363
|
+
const tail = more > 0 ? `, and ${more} more` : '';
|
|
364
|
+
return (`Result rejected: ${toolName} returned rows that disagree with the columns it declares — ` +
|
|
365
|
+
`${parts.join('; ')}${tail}. Fix the tool so the column holds what it declares, or change ` +
|
|
366
|
+
`the declaration. No data was returned.`);
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/integrity/column-types/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The COLUMN-TYPE CONTRACT's vocabulary — what a tool may say about the
|
|
4
|
+
* columns of the rowset it returns, and the one rule that judges the saying.
|
|
5
|
+
*
|
|
6
|
+
* Pattern: closed vocabulary + definition-time assert (the `resultCeiling` /
|
|
7
|
+
* `resultClass` law: a declaration this library cannot honour fails
|
|
8
|
+
* at `defineTool`, never at the first row of the first run).
|
|
9
|
+
* Role: leaf. `src/integrity/` imports nothing outside itself, so the
|
|
10
|
+
* vocabulary lives HERE and `core/tools.ts` reaches in for it —
|
|
11
|
+
* never the other way round.
|
|
12
|
+
*
|
|
13
|
+
* ── Why the words are these words ───────────────────────────────────────
|
|
14
|
+
* They are NOT invented. `number | string | boolean | date` is the vocabulary
|
|
15
|
+
* this ecosystem's rowset consumers already speak — a column-type union with
|
|
16
|
+
* exactly these members is what the visualization layer sniffs its way to
|
|
17
|
+
* today. Declaring in the consumer's own words is the `resultKind` law
|
|
18
|
+
* (9.70.0) restated one level down: the tool's result in the CONSUMER's
|
|
19
|
+
* vocabulary, not the framework's.
|
|
20
|
+
*
|
|
21
|
+
* The one member deliberately NOT carried over is `unknown`. A sniffer needs
|
|
22
|
+
* that word — it is what "I looked at the values and could not tell" sounds
|
|
23
|
+
* like. A DECLARATION has no use for it: an author who does not know what a
|
|
24
|
+
* column holds should not name the column, and `columns: { x: 'unknown' }`
|
|
25
|
+
* would be a promise about nothing that the checker would then have to
|
|
26
|
+
* pretend to verify.
|
|
27
|
+
*
|
|
28
|
+
* ── Two spellings, one meaning ──────────────────────────────────────────
|
|
29
|
+
* A column maps to a bare type (`lun: 'number'`) or to an object form
|
|
30
|
+
* (`note: { type: 'string', nullable: true }`). That is this library's house
|
|
31
|
+
* pattern for exactly this shape — `CostBudget` takes a bare number or
|
|
32
|
+
* `{ usd, onExceed }`, `artifacts` takes a bare store or `{ store, placement }`
|
|
33
|
+
* — and it is normalized ONCE, here, so every reader downstream sees one
|
|
34
|
+
* shape and no downstream file learns that there were two.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.assertResultColumns = exports.normalizeColumns = exports.COLUMN_TYPES = void 0;
|
|
38
|
+
/** The closed set, in one place, for the assert and for its own error message. */
|
|
39
|
+
exports.COLUMN_TYPES = ['number', 'string', 'boolean', 'date'];
|
|
40
|
+
/**
|
|
41
|
+
* Both spellings into one list, in declaration order.
|
|
42
|
+
*
|
|
43
|
+
* Order is kept because it is the author's order, and a finding that names
|
|
44
|
+
* columns in the order the author wrote them is a finding they can scan
|
|
45
|
+
* against their own source.
|
|
46
|
+
*/
|
|
47
|
+
function normalizeColumns(columns) {
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const [name, declared] of Object.entries(columns)) {
|
|
50
|
+
if (typeof declared === 'string') {
|
|
51
|
+
out.push({ name, type: declared, nullable: false });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
out.push({ name, type: declared.type, nullable: declared.nullable === true });
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
exports.normalizeColumns = normalizeColumns;
|
|
59
|
+
/**
|
|
60
|
+
* Refuse a `resultColumns` this library cannot honour, at definition time —
|
|
61
|
+
* naming the tool, the column and the fix.
|
|
62
|
+
*
|
|
63
|
+
* Exported beside {@link ColumnType} and called from `defineTool`, so a
|
|
64
|
+
* misspelled type fails on the line that wrote it rather than at the first
|
|
65
|
+
* rowset of the first armed run. Also called by the MCP ingest
|
|
66
|
+
* (`readToolExtras`) on a bag from a server this process does not control —
|
|
67
|
+
* which is why every read below goes through a fallback: a `null`, a number
|
|
68
|
+
* or an array must reach the teaching refusal, never blow up on the way to
|
|
69
|
+
* it.
|
|
70
|
+
*/
|
|
71
|
+
function assertResultColumns(toolName, columns) {
|
|
72
|
+
if (columns === undefined)
|
|
73
|
+
return;
|
|
74
|
+
if (typeof columns !== 'object' || columns === null || Array.isArray(columns)) {
|
|
75
|
+
throw new Error(`defineTool: tool '${toolName}' declares resultColumns ${JSON.stringify(columns)}, which ` +
|
|
76
|
+
`is not a map of column name to type. Write it as ` +
|
|
77
|
+
`{ logical_unit_number: 'number', host_group: 'string' } — or omit the field, which ` +
|
|
78
|
+
`means this tool promises nothing about its columns and none are ever judged.`);
|
|
79
|
+
}
|
|
80
|
+
const entries = Object.entries(columns);
|
|
81
|
+
if (entries.length === 0) {
|
|
82
|
+
throw new Error(`defineTool: tool '${toolName}' declares an EMPTY resultColumns. A declaration that names ` +
|
|
83
|
+
`no column promises nothing, and omitting the field is how "nothing promised" is said ` +
|
|
84
|
+
`— the two must not be different spellings of the same silence.`);
|
|
85
|
+
}
|
|
86
|
+
for (const [name, declared] of entries) {
|
|
87
|
+
if (name.trim().length === 0) {
|
|
88
|
+
throw new Error(`defineTool: tool '${toolName}' declares a resultColumns entry with a blank column ` +
|
|
89
|
+
`name. A column name is what a finding points at, and a blank one points nowhere.`);
|
|
90
|
+
}
|
|
91
|
+
const type = typeof declared === 'string' ? declared : declared?.type;
|
|
92
|
+
if (typeof declared === 'object' && declared !== null && !Array.isArray(declared)) {
|
|
93
|
+
const nullable = declared.nullable;
|
|
94
|
+
if (nullable !== undefined && typeof nullable !== 'boolean') {
|
|
95
|
+
throw new Error(`defineTool: tool '${toolName}' declares resultColumns['${name}'].nullable = ` +
|
|
96
|
+
`${JSON.stringify(nullable)}, which is not a boolean. \`true\` says a row of this ` +
|
|
97
|
+
`column may carry no value; omit it (or \`false\`) and a missing value is a ` +
|
|
98
|
+
`violation like any other.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (typeof type !== 'string' || !exports.COLUMN_TYPES.includes(type)) {
|
|
102
|
+
throw new Error(`defineTool: tool '${toolName}' declares column '${name}' as ${JSON.stringify(type)}, ` +
|
|
103
|
+
`which is not a column type this library has. The types are: ` +
|
|
104
|
+
`${exports.COLUMN_TYPES.join(', ')} — a bare word ('number') or the object form ` +
|
|
105
|
+
`({ type: 'number', nullable: true }). There is deliberately no 'unknown': a column ` +
|
|
106
|
+
`whose type you do not know is a column to leave undeclared, and unlisted columns ` +
|
|
107
|
+
`are allowed and never judged.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.assertResultColumns = assertResultColumns;
|
|
112
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/integrity/column-types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
* compose-invariant: a mount kernel is configured; dangling: at
|
|
12
12
|
* least one tool declared `argumentsFrom`, which arms BOTH the
|
|
13
13
|
* compose-seam dangling-reference check and the choice-seam
|
|
14
|
-
* unsupported-argument check off the same declaration
|
|
14
|
+
* unsupported-argument check off the same declaration;
|
|
15
|
+
* empty-lookup: that same declaration AND the operator's
|
|
16
|
+
* `noticeEmptyLookups` dial — two halves, because an advisory that
|
|
17
|
+
* armed itself off a declaration made for something else would not
|
|
18
|
+
* be opt-in at all; column-types: `Tool.resultColumns` AND the
|
|
19
|
+
* operator's `checkColumnTypes` dial off `'off'` — the same two
|
|
20
|
+
* halves, arming BOTH `column-type-mismatch` and `missing-column`
|
|
21
|
+
* off the one declaration).
|
|
15
22
|
* Registration is what makes silence auditable — an unregistered
|
|
16
23
|
* check is honest
|
|
17
24
|
* absence, a registered one that never notes is the wiring rot
|
|
@@ -32,6 +39,8 @@ const wire_js_1 = require("../invariant-violation/wire.js");
|
|
|
32
39
|
const check_js_2 = require("../dangling-reference/check.js");
|
|
33
40
|
const check_js_3 = require("../unsupported-argument/check.js");
|
|
34
41
|
const check_js_4 = require("../unsupported-claim/check.js");
|
|
42
|
+
const check_js_5 = require("../empty-lookup/check.js");
|
|
43
|
+
const check_js_6 = require("../column-types/check.js");
|
|
35
44
|
/**
|
|
36
45
|
* Start one run's ledger: register the present checks and, in dev posture,
|
|
37
46
|
* prove each can still catch its own synthetic defect.
|
|
@@ -76,6 +85,9 @@ function beginIntegrityRun(present, posture) {
|
|
|
76
85
|
armed('dangling-reference', 'compose', present.dangling === true);
|
|
77
86
|
armed('unsupported-argument', 'choice', present.dangling === true);
|
|
78
87
|
armed('unsupported-claim', 'claim', present.claim === true);
|
|
88
|
+
armed('empty-lookup', 'write', present.emptyLookup === true);
|
|
89
|
+
armed('column-type-mismatch', 'write', present.columnTypes === true);
|
|
90
|
+
armed('missing-column', 'write', present.columnTypes === true);
|
|
79
91
|
if (posture !== 'dev')
|
|
80
92
|
return ledger;
|
|
81
93
|
// The canaries — one known-bad fixture per REGISTERED check, through the
|
|
@@ -145,6 +157,47 @@ function beginIntegrityRun(present, posture) {
|
|
|
145
157
|
if (caught.findings.length > 0)
|
|
146
158
|
ledger.noteSynthetic('unsupported-claim', 'claim', 'caught');
|
|
147
159
|
}
|
|
160
|
+
{
|
|
161
|
+
// The write-seam canary (9.77.0): a lookup whose argument the fixture's
|
|
162
|
+
// PRODUCER really did serve, answering with a zero-row rowset. Both
|
|
163
|
+
// halves of the join are deliberately load-bearing — remove the ground
|
|
164
|
+
// and the value is not the run's own; make the rowset non-empty and
|
|
165
|
+
// there is nothing to notice — so a check that finds nothing here has
|
|
166
|
+
// lost one of the two facts it exists to pair.
|
|
167
|
+
ledger.noteSynthetic('empty-lookup', 'write', 'minted');
|
|
168
|
+
const caught = (0, check_js_5.emptyLookupOf)({
|
|
169
|
+
toolName: 'canary_tool',
|
|
170
|
+
toolCallId: 'canary',
|
|
171
|
+
args: { id: 'canary-grounded-id' },
|
|
172
|
+
argumentsFrom: ['canary_ground'],
|
|
173
|
+
reading: (0, check_js_5.readLookupResult)([], false),
|
|
174
|
+
}, [{ toolName: 'canary_ground', text: 'canary: served canary-grounded-id' }], -1);
|
|
175
|
+
if (caught.findings.length > 0)
|
|
176
|
+
ledger.noteSynthetic('empty-lookup', 'write', 'caught');
|
|
177
|
+
}
|
|
178
|
+
{
|
|
179
|
+
// The column-type canaries (9.78.0). ONE fixture, deliberately, because
|
|
180
|
+
// one rowset carries both defects at once and that is the honest shape of
|
|
181
|
+
// the field failure: a numeric column arriving as text AND a declared
|
|
182
|
+
// column nobody delivered, in the same result. Each check reads its own
|
|
183
|
+
// findings out of the shared verdict, so a canary that catches one and
|
|
184
|
+
// not the other names exactly which half died.
|
|
185
|
+
ledger.noteSynthetic('column-type-mismatch', 'write', 'minted');
|
|
186
|
+
ledger.noteSynthetic('missing-column', 'write', 'minted');
|
|
187
|
+
const caught = (0, check_js_6.columnTypesOf)({
|
|
188
|
+
toolName: 'canary_tool',
|
|
189
|
+
toolCallId: 'canary',
|
|
190
|
+
columns: { canary_measure: 'number', canary_absent: 'string' },
|
|
191
|
+
reading: (0, check_js_6.readRowset)([{ canary_measure: '1240' }]),
|
|
192
|
+
mode: 'warn',
|
|
193
|
+
}, -1);
|
|
194
|
+
if (caught.findings.some((f) => f.kind === 'column-type-mismatch')) {
|
|
195
|
+
ledger.noteSynthetic('column-type-mismatch', 'write', 'caught');
|
|
196
|
+
}
|
|
197
|
+
if (caught.findings.some((f) => f.kind === 'missing-column')) {
|
|
198
|
+
ledger.noteSynthetic('missing-column', 'write', 'caught');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
148
201
|
return ledger;
|
|
149
202
|
}
|
|
150
203
|
exports.beginIntegrityRun = beginIntegrityRun;
|