provenance-grade
SkillAI & modelsAttach an advisory knowledge grade, verified / asserted / assumed, to fields a CALL-E phone agent extracts, using heuristic signals in the transcript turns the API returns.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the provenance-grade skill
What this skill tells your AI
The instructions your AI receives, as published by calle-ai/awesome-phone-call-agents in skills/provenance-grade/SKILL.md and read by ahel’s review.
Every phone agent extracts what was said and throws away how the speaker knew it.
Speech carries the speaker's epistemic state; text hides it. Structured extraction
throws that signal away. This skill puts it back: it reads
recipients[i].attempts[j].transcript_turns from a completed CALL-E call and attaches
a knowledge grade to every extracted field, with the exact transcript span that
supports it.
This skill never places calls: it lints a task before a host dispatches it and grades a transcript after a call has completed. It contains no dialing code, no phone numbers, and no network calls of any kind.
Two calls can both answer "Tuesday." In one, the person said "hold on, let me
check", went quiet for eleven seconds, and came back with "Tuesday — we're holding
eleven units at the Bhiwandi warehouse." In the other, they said "should be
Tuesday" half a second after the question. Same extracted value. Completely different
knowledge. The first is verified; the second is assumed — and if your workflow
auto-commits on the second one, the extraction was correct and the answer was still
wrong.
Why this is not a duplicate of anything in this repo
CALL-E already returns completion_confidence — but by its own documentation that is
confidence that the task reached a clear end state, explicitly not confidence in
the quality of the business answer. That is a documented, self-acknowledged gap, and
this skill fills exactly it.
Against the other skills here: call-summarizer summarises after the fact.
voice-preflight checks audio before the call. linecanary monitors line health.
Nothing grades the epistemic basis of a spoken claim.
The grades
| Grade | Meaning |
|---|---|
verified | The heuristic detects a lookup pause or explicit check language, plus an unrequested corroborating specific, plus read-back compliance where requested. This label is not proof that a lookup occurred or that the answer is true. |
asserted | Answered directly and cleanly, but nothing established where the value came from. |
assumed | Hedged, deferred, misaligned with the question, or a bare round number. The call did not establish this value. |
unstated | The field never appeared in the transcript (voicemail, IVR, unanswered question). Never a grade — there is nothing to grade. |
Consumer rule: grades are advisory inputs to the host's own validation, not
independent authorization to act. Keep a human in the loop for asserted, and
never auto-act on assumed or unstated. The high-stakes prohibitions in
references/safety.md apply to every grade, including verified.
Fail-closed: the default is assumed. asserted must be earned, verified must
be earned twice. Absence of signal never upgrades a field. This is the same
discipline this repo rewards everywhere else, applied one level up.
Two halves
Pre-call — scripts/lint-task.ts. Amends the caller's task text and
result_schema so the signals are actually elicitable: forces a read-back of
critical values, rewrites "do you know?" into "can you check?", asks for one
corroborating specific, and gives the agent permission to wait while the person
looks something up. You improve the signal you will later measure.
import { lintTask } from './scripts/lint-task.ts';
const { task, result_schema, amendments } = lintTask({
task: 'Call the supplier. Do you know the unit price and delivery date?',
result_schema: mySchema,
critical_fields: ['unit_price', 'delivery_day'],
});
// -> task now asks "can you check", requests a read-back, asks for one specific
Post-call — scripts/grade.ts. Reads the transcript turns and emits a grade plus
the supporting span per field.
import { gradeCall } from './scripts/grade.ts';
const provenance = gradeCall({
callId: call.id,
recipientId: recipient.id, // an ORGANISATION id, never a person
turns: attempt.transcript_turns, // { speaker, text, offset_seconds }
fields: [
{
field: 'delivery_day',
value: extracted.delivery_day, // from CALL-E's structured output
expects: 'weekday', // duration | weekday | date | price | count | text
questionTurn: 2, // where the bot asked
answerTurns: [4], // where the person answered ([] if never answered)
readbackRequested: true,
critical: true,
},
],
});
// provenance.fields[0] ->
// { field: 'delivery_day', value: 'Tuesday', grade: 'verified',
// signals: ['A:gap=12.6s', 'B:let me check', "C:stock_count 'eleven units'", "C:place 'Bhiwandi'"],
// span: 'It ships Tuesday. We are holding eleven units at the Bhiwandi warehouse for you.',
// turnOffset: 26, unstable: false, gapSeconds: 12.6 }
Output contract (scripts/types.ts):
type Grade = 'verified' | 'asserted' | 'assumed' | 'unstated';
interface FieldProvenance {
field: string; // "eta_days"
value: unknown; // 5
grade: Grade;
signals: string[]; // ["B:let me check", "C:place 'Bhiwandi'"]
span: string; // exact transcript text supporting the value
turnOffset: number | null;
unstable: boolean; // value was stated then revised (signal I)
gapSeconds: number | null;
}
interface CallProvenance {
callId: string;
recipientId: string; // organisation-level, never a person
fields: FieldProvenance[];
weakestGrade: Grade; // the call is only as good as its worst critical field
gradedAt: string;
}
How the grade is computed
Nine signals, described with examples in references/signals.md: retrieval gap (A), explicit check language (B), corroborating specific (C), hedging lexicon (D), deferral (E), read-back compliance (F), round-number shape (G), answer alignment (H), self-correction (I). They feed a fixed rule table:
if field not present in transcript -> 'unstated' (never a grade)
if E or H -> 'assumed'
if D present -> 'assumed'
if (B or A) and C and (F if requested) -> 'verified' (never when unstable)
if answered directly, no D/E/H -> 'asserted' (unless G with no C)
otherwise -> 'assumed'
Deterministic vs model-assisted — honestly. Signals A, B, D, E, F, G and I are
regex and arithmetic. Signals C and H need semantics; in this build they run on a
deterministic entity heuristic, and each exposes a provider interface
(CorroborationProvider, AlignmentProvider) where a model can be plugged in — a
model is constrained to returning spans, never a grade. The grade itself is always
computed by the rule table above, never by a model's opinion. That is what makes it
testable: 125 unit tests and a confusion matrix over 24 labelled fixtures, zero calls,
zero network.
truth \ pred verified asserted assumed unstated
verified 4 0 0 0
asserted 0 6 0 0
assumed 0 1 9 0
unstated 0 0 0 4 -> 23/24 (95.8%)
The one miss is deliberate and shipped as a fixture: a code-switched answer whose
hedge is the Hindi "shayad", which the English lexicon cannot see
(assets/fixtures/23-codeswitch-hindi-hedge-missed.json). A named limitation beats a
silent one. Reproduce with npm run eval; run tests with npm test.
Ethics boundary
This skill grades behaviour, not people.
- No emotion detection, no stress or deception scoring, no voice biometrics. (Also impossible here by construction: CALL-E exposes no audio to grade — only transcript text and integer offsets.)
- Hosts should associate grades with an organisation, not an individual. This pure grader does not enforce identifier contents or anonymize input: values, transcript spans and caller-supplied IDs may identify people. The caller must minimize inputs and redact display/export copies before sharing them.
- A low grade means the call did not establish this, never this person lied.
- Transcript spans are retained only as the minimum quote supporting one field.
Full statement: references/ethics.md.
Limitations
Stated, not hidden — the full list with reasoning is
references/limitations.md. Headlines: offset_seconds
marks turn start at integer resolution, so the latency signal is weak and weighted
accordingly; ASR errors corrupt hedge detection; the lexicon is English-centric and
swappable (scripts/lexicon/en.json), with the Hindi file (scripts/lexicon/hi.json) shipped unvalidated;
directness is cultural — a terse answer is not necessarily a guess; and the grades
are not yet validated against outcomes.
Roadmap (stated, not built)
The grade is a prediction. The validation is: record the grade, wait for the promised
event, record whether it held, and measure grade-vs-outcome accuracy per
organisation. Over enough calls this produces a calibration curve and a per-supplier
reliability score — a supplier whose asserted Tuesdays actually arrive on Tuesday
earns trust; one whose verified claims fail flags a broken lookup process. None of
that is built and none of it is tested. It is named here because naming the
unvalidated claim is what separates a measure from a demo.
CALL-E tells you the call finished. This tells you whether to believe it.
Signals
- GitHub stars
- 104
- Forks
- 527
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
provenance-grade- Source
- github.com/calle-ai/awesome-phone-call-agents