agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
97 lines • 4.27 kB
JavaScript
;
/**
* time/checkRecord — the record half of the result checks (time design
* § 9.2, § 9.6, step T8): the shapes a `period` row carries
* (`PeriodDiffers`, `ReadSource`), the checkpoint door's tests for them
* (`isPeriodDiffers`, `isShifted`), and the two reads over the turn's
* wall-clock sources (`distinctSources`, `clocksDiffer`).
*
* Pattern: pure shapes and guards; no conversion, no drift, no clock.
* Role: split from `check.ts` BY FILE for the synchronous doors — the
* checkpoint door's row test (`coverage/period.ts` ·
* `periodRowIsWellFormed`) is on every agent's graph, and a bundler
* places a whole file there — so this file imports only
* `instant.ts` and the range and zone types (the optional-family
* law, `test/trace-toolpack/browserGraph.test.ts`). `check.ts`
* re-exports every public name.
* Emits: N/A.
*
* @example
* ```ts
* clocksDiffer([
* { toolName: 'orders', zone: 'America/New_York' },
* { toolName: 'tickets', zone: 'Europe/Berlin' },
* ]); // { tools: ['orders', 'tickets'], zones: ['America/New_York', 'Europe/Berlin'] }
* ```
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isShifted = exports.isPeriodDiffers = exports.clocksDiffer = exports.distinctSources = void 0;
const instant_js_1 = require("./instant.js");
// ─── Clocks ──────────────────────────────────────────────────────────────
/**
* The wall-clock sources of one answer, one per tool and zone in the order
* met — the limits block names each. Fresh plain objects.
*/
function distinctSources(sources) {
const seen = new Set();
const out = [];
for (const s of sources) {
const key = JSON.stringify([s.toolName, s.zone]);
if (seen.has(key))
continue;
seen.add(key);
out.push({ toolName: s.toolName, zone: s.zone });
}
return out;
}
exports.distinctSources = distinctSources;
/**
* `clocks-differ` (§ 9.6) — the tools and zones when the wall-clock sources
* of one answer declare more than one zone; `undefined` otherwise. A label
* only: it changes no standing, and every comparison is made on instants.
*/
function clocksDiffer(sources) {
const distinct = distinctSources(sources);
const zones = [...new Set(distinct.map((s) => s.zone))];
if (zones.length < 2)
return undefined;
return { tools: [...new Set(distinct.map((s) => s.toolName))], zones };
}
exports.clocksDiffer = clocksDiffer;
// ─── The row arm ─────────────────────────────────────────────────────────
const isRange = (value) => {
if (typeof value !== 'object' || value === null)
return false;
const r = value;
const from = (0, instant_js_1.instantOf)(r.from, 'lenient')?.ms;
const to = (0, instant_js_1.instantOf)(r.to, 'lenient')?.ms;
return from !== undefined && to !== undefined && from < to;
};
const isRanges = (value) => Array.isArray(value) && value.every(isRange);
/** The checkpoint door's test for a row's {@link PeriodDiffers}. */
function isPeriodDiffers(value) {
if (typeof value !== 'object' || value === null || Array.isArray(value))
return false;
const d = value;
return ((d.against === 'asked' || d.against === 'person') &&
isRange(d.asked) &&
isRanges(d.read) &&
d.read.length > 0 &&
(d.source === 'declared' ||
d.source === 'sent' ||
d.source === 'shifted' ||
d.source === 'asked') &&
(d.stepMs === undefined || (Number.isInteger(d.stepMs) && d.stepMs > 0)) &&
isRanges(d.missing) &&
isRanges(d.extra) &&
d.missing.length + d.extra.length > 0);
}
exports.isPeriodDiffers = isPeriodDiffers;
/** The checkpoint door's test for a row's `shifted`. */
function isShifted(value) {
return (typeof value === 'object' &&
value !== null &&
Number.isInteger(value.byMs));
}
exports.isShifted = isShifted;
//# sourceMappingURL=checkRecord.js.map