agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
114 lines (113 loc) • 6.44 kB
TypeScript
/**
* time/check — the result checks (time design § 9.2, § 9.4, § 9.6, step T8).
*
* Pattern: one pure check per judged call, over what the record already holds
* — the call's `call-window` row (what the call asked, and the
* person's window beside it), its `call` row (the clock at
* dispatch), the periods its result declared, and the tool's facts —
* and one pure read over the turn's wall-clock sources. None reads
* words; none reads a period's offset as a clock.
* Role: L0 leaf (the time layer). Imports `instant.ts`, `range.ts`,
* `convert.ts` and the row types of `rows.ts`. Asked by the results
* layer (`results/subflow.ts` · `checkPeriods`), which files the
* answer on the call's `period` row (`coverage/period.ts` ·
* `PeriodRow`), and by the limits block (`coverage/period.ts` ·
* `periodCheckLine`, `clockLines`).
* Emits: N/A.
*
* ## Differs from asked — ONE check for narrower, wider and shifted (§ 9.2)
*
* The range the call asked for is compared with the range it READ, and two
* lists come back: `missing` (asked but not read — a narrower read, or the
* front of a shifted one) and `extra` (read but not asked — a wider read, or
* the tail of a shifted one). What was read, first found wins:
*
* | Source | The read |
* |--------|----------|
* | `declared` | the result's own `period.queried` — inclusive at both ends, read back as `[from, to + step)`, `step` the tool's `granularity` (else 1 ms; § 3.3). Several declared: each is a read |
* | `sent` | a widened fill the tool does not trim — the `call-window` row's `sent` |
* | `shifted` | a look-back that ran as sent after the clock moved past the tool's step — the asked range moved by the `call` row's `drift.byMs` |
* | `asked` | nothing says otherwise — the read IS the asked range (a fill, a binding, a redrawn look-back, a widened fill the tool trims) |
*
* Against what: the call's `asked` range — EXCEPT for a window the model
* chose (`how: 'model-chosen'`), which is compared with the person's window
* (§ 9.3: claims about the person's window are then "not sure"). A call with
* neither — refused, not filled, unread — is not compared.
*
* Two differences are not differences: a piece no longer than one
* millisecond (a closed end read as open — `[now − L, now]` against
* `[now − L, now)`), and § 7.4's first row — a call that SENT a look-back
* (`drift.ts` · `sentLookback`), with no drift recorded (the dispatch was
* within the step), whose declared read has the asked length and moved LATER
* by no more than the tool's step (one minute when none): the tool evaluated
* the look-back on its own clock, a little after `now`, and that read IS the
* asked range. Nothing else moves: a read moved EARLIER, a read of a window
* sent as bounds, or a look-back with a recorded drift is compared as read —
* its `missing` stands, and the answer is "not sure" (§ 9.2).
*
* ## Retention (§ 9.4)
*
* `beyondRetention` — the call was refused before dispatch because the window
* was wholly older than the source keeps, or its read lies wholly before
* `now − retention`. `partlyBeyondRetention` — the read starts before that
* edge and ends after it (the result's `held` decides `partly-held`).
*
* ## Clocks (§ 9.6)
*
* A dataset whose declared time axis names a `zone` holds wall times in that
* zone — a wall-clock source. {@link clocksDiffer} names the sources and zones
* when two sources of one answer declare different zones: a label only, every
* comparison is done on instants. A period's offset (`Z`, `-07:00`) is a
* spelling of an instant, never a clock, and is never read here.
*
* @example
* ```ts
* periodTimeCheck({
* window: { how: 'filled', asked: { from: '2026-10-02T15:40:00Z', to: '2026-10-09T15:40:00Z' } },
* declared: [{ from: '2026-10-07T15:40:00Z', to: '2026-10-09T15:39:59.999Z' }], // clamped to 2 days
* now: '2026-10-09T15:40:00Z',
* })?.differs?.missing; // [{ from: '2026-10-02T15:40:00Z', to: '2026-10-07T15:40:00Z' }]
* ```
*/
import { type InstantText } from './instant.js';
import { type InclusiveSpan, type TimeRange } from './range.js';
import { type PeriodFacts, type PeriodForm } from './convert.js';
import type { CallDrift, CallWindowRow } from './rows.js';
export { clocksDiffer, distinctSources, isPeriodDiffers, isShifted, type ClocksDiffer, type PeriodDiffers, type ReadSource, type SourceClock, } from './checkRecord.js';
import type { PeriodDiffers } from './checkRecord.js';
/** The time layer's result checks on one call — each key present only when it holds. */
export interface PeriodTimeCheck {
readonly differs?: PeriodDiffers;
/** `period-shifted` (§ 7.4): the look-back ran as sent, `byMs` after the turn's `now`. */
readonly shifted?: {
readonly byMs: number;
};
/** `period-beyond-retention` (§ 9.4). */
readonly beyondRetention?: true;
/** `partly-beyond-retention` (§ 9.4). */
readonly partlyBeyondRetention?: true;
}
/** What one call's check reads — every field off a row or a declaration. */
export interface PeriodCheckInput {
/** The call's `call-window` row — absent when the tool declares no forms. */
readonly window?: Pick<CallWindowRow, 'how' | 'form' | 'asked' | 'person' | 'sent' | 'trimmedByTool' | 'partlyBeyondRetention' | 'refused'>;
/** The call's `call` row's `drift`. */
readonly drift?: CallDrift;
/** The `queried` span of every period the call's result declared (inclusive ends). */
readonly declared?: readonly InclusiveSpan[];
readonly facts?: PeriodFacts;
/** The tool's period forms — with the window's `form`, whether the call sent a look-back (§ 7.4). */
readonly forms?: readonly PeriodForm[];
/** The turn's clock `now`. */
readonly now: InstantText;
}
/** The parts of `asked` not read, and the parts read not asked. */
export declare function rangeDifference(asked: TimeRange, read: readonly TimeRange[]): {
readonly missing: TimeRange[];
readonly extra: TimeRange[];
} | undefined;
/**
* The time layer's result checks on one call (§ 9.2, § 7.4, § 9.4) —
* `undefined` when none holds, so an unremarkable call's row gains nothing.
*/
export declare function periodTimeCheck(input: PeriodCheckInput): PeriodTimeCheck | undefined;