fact-checking

star 2

Verify factual claims in a document against available evidence, producing an annotated report

PaulKinlan By PaulKinlan schedule Updated 2/10/2026

name: fact-checking description: Verify factual claims in a document against available evidence, producing an annotated report

Fact-Checking

When to Use

Use this skill when you need to verify the accuracy of claims and assertions in a document before it is published, shared, or used as a basis for decisions. This applies to research reports, design documents, status updates, incident summaries, analysis findings, or any deliverable that contains factual statements. The goal is to systematically identify every checkable claim, verify it against available evidence, and produce a clear report of what is confirmed, what is unsupported, and what is contradicted.

Claim Verdicts

Verdict Meaning Action
Confirmed Claim is supported by at least one reliable source No change needed
Unconfirmed No evidence found to support or refute the claim Flag for author -- add source or soften language
Contradicted Available evidence directly contradicts the claim Must be corrected before publication
Modified Claim is partially correct but needs qualification Suggest revised wording
Opinion Statement is subjective -- not a factual claim Mark as opinion if presented as fact
Projection Forward-looking statement that cannot be verified now Mark as projection if presented as fact

Output: Fact-Check Report

# Fact-Check Report

**Document:** [document name and path]
**Checked by:** [agent name]
**Date:** [YYYY-MM-DD]

## Summary Statistics
| Metric | Count |
|--------|-------|
| Total claims extracted | N |
| Verifiable claims | N |
| Confirmed | N |
| Modified | N |
| Unconfirmed | N |
| Contradicted | N |
| Opinions/Projections (not checked) | N |

**Overall Reliability:** [High / Medium / Low / Unreliable]

## Claim Register

| # | Claim | Location | Type | Verdict | Evidence | Notes |
|---|-------|----------|------|---------|----------|-------|
| 1 | [quoted claim] | Line N | Verifiable | Confirmed | [source] | |
| 2 | [quoted claim] | Line N | Verifiable | Contradicted | [source shows X] | Must correct |
| 3 | [quoted claim] | Line N | Opinion | N/A | | Presented as fact |

## Detailed Findings

### Claim 1: [quoted claim]
- **Location:** Line N, Section "[heading]"
- **Type:** Verifiable
- **Verdict:** Confirmed
- **Evidence:** [what was found and where]
- **Action:** None

### Claim 2: [quoted claim]
- **Location:** Line N, Section "[heading]"
- **Type:** Verifiable
- **Verdict:** Contradicted
- **Evidence:** [what was found and where, showing contradiction]
- **Action:** Correct to "[suggested replacement]"

## Annotated Document
[Full document with inline annotations marking each claim's verdict]

Procedure

1. Read the Document

DOC_FILE="${1:-/home/shared/document.md}"

if [ ! -f "$DOC_FILE" ]; then
  echo "ERROR: Document not found at $DOC_FILE"
  exit 1
fi

# Read the full document
cat "$DOC_FILE"

# Get basic stats
echo ""
echo "=== Document Stats ==="
wc -l "$DOC_FILE" | awk '{print "Lines:", $1}'
wc -w "$DOC_FILE" | awk '{print "Words:", $1}'

# Number the lines for reference
nl -ba "$DOC_FILE" > /tmp/doc-numbered.txt
echo "Numbered version saved to /tmp/doc-numbered.txt"

2. Extract All Factual Claims

Go through the document line by line and extract every statement that makes a factual assertion:

CLAIMS_FILE="/tmp/claims-register.jsonl"
> "$CLAIMS_FILE"

# Helper: add a claim to the register
add_claim() {
  local num="$1"
  local line="$2"
  local claim="$3"
  local type="$4"  # verifiable, opinion, projection

  jq -n \
    --arg num "$num" \
    --arg line "$line" \
    --arg claim "$claim" \
    --arg type "$type" \
    --arg verdict "pending" \
    --arg evidence "" \
    --arg notes "" \
    '{num: $num, line: $line, claim: $claim, type: $type, verdict: $verdict, evidence: $evidence, notes: $notes}' \
    >> "$CLAIMS_FILE"
}

# Example: extract claims (in practice, read through the document and call add_claim for each)
# add_claim "1" "12" "The system processes 10,000 requests per second" "verifiable"
# add_claim "2" "15" "This approach is better than the alternative" "opinion"
# add_claim "3" "23" "Usage will double by Q3" "projection"

echo "Claims extracted to: $CLAIMS_FILE"
jq -s 'length' "$CLAIMS_FILE"
echo "total claims"

Identify claims by looking for:

  • Numbers, statistics, measurements ("10,000 requests", "99.9% uptime", "3x faster")
  • Causal statements ("X causes Y", "X leads to Y", "because of X")
  • Comparisons ("faster than", "more reliable than", "unlike X")
  • Existence claims ("there are N", "X contains Y", "X supports Z")
  • Temporal claims ("since 2023", "before the migration", "always")
  • Attribution ("according to", "X recommends", "the standard requires")

3. Classify Each Claim

# Read claims and classify
jq -c '.' "$CLAIMS_FILE" | while read claim_json; do
  CLAIM_TEXT=$(echo "$claim_json" | jq -r '.claim')
  CLAIM_LOWER=$(echo "$CLAIM_TEXT" | tr '[:upper:]' '[:lower:]')

  # Classify type
  TYPE="verifiable"  # default

  # Opinion indicators
  if echo "$CLAIM_LOWER" | grep -qE '\b(better|worse|best|worst|should|ideal|prefer|recommend|believe|think|feel|obvious|clearly)\b'; then
    TYPE="opinion"
  fi

  # Projection indicators
  if echo "$CLAIM_LOWER" | grep -qE '\b(will|would|expect|predict|forecast|project|estimate.*future|by (q[1-4]|20[2-9][0-9]|next))\b'; then
    TYPE="projection"
  fi

  echo "Claim: $CLAIM_TEXT"
  echo "  Type: $TYPE"
done

4. Verify Each Verifiable Claim

For each verifiable claim, search available sources:

verify_claim() {
  local claim_num="$1"
  local claim_text="$2"

  echo "=== Verifying claim $claim_num: $claim_text ==="

  # Extract key terms for searching
  KEY_TERMS=$(echo "$claim_text" | tr '[:upper:]' '[:lower:]' \
    | grep -oE '[a-z]{4,}' \
    | grep -vE '^(that|this|with|from|have|been|were|will|would|does|than|more|less|also|each|into|only)$' \
    | sort -u | head -5 | tr '\n' '|' | sed 's/|$//')

  echo "  Search terms: $KEY_TERMS"

  # Search shared files for evidence
  echo "  --- Shared files ---"
  rg -l "$KEY_TERMS" /home/shared/ 2>/dev/null | head -10

  # Search with context to see what the sources actually say
  rg -C2 "$KEY_TERMS" /home/shared/ 2>/dev/null | head -30

  # Check data files for numerical claims
  if echo "$claim_text" | grep -qE '[0-9]'; then
    echo "  --- Numerical check ---"
    NUMBERS=$(echo "$claim_text" | grep -oE '[0-9][0-9,.]*')
    for num in $NUMBERS; do
      echo "  Looking for number: $num"
      rg "$num" /home/shared/ 2>/dev/null | head -5
    done
  fi

  # Check task board for process/status claims
  echo "  --- Task board ---"
  bash /home/shared/scripts/task.sh list 2>/dev/null | jq -r '.[].subject' \
    | grep -iE "$KEY_TERMS" 2>/dev/null | head -5

  # Check artifacts for referenced outputs
  echo "  --- Artifacts ---"
  bash /home/shared/scripts/artifact.sh list 2>/dev/null | jq -r '.[].name' \
    | grep -iE "$KEY_TERMS" 2>/dev/null | head -5
}

# Run verification for each verifiable claim
jq -c 'select(.type == "verifiable")' "$CLAIMS_FILE" | while read claim_json; do
  NUM=$(echo "$claim_json" | jq -r '.num')
  TEXT=$(echo "$claim_json" | jq -r '.claim')
  verify_claim "$NUM" "$TEXT"
  echo ""
done

5. Record Verdicts

Update the claims register with findings:

# Update a claim's verdict and evidence
update_verdict() {
  local claim_num="$1"
  local verdict="$2"     # confirmed, unconfirmed, contradicted, modified
  local evidence="$3"
  local notes="$4"

  # Read existing claims, update the matching one, write back
  jq -c "if .num == \"$claim_num\" then .verdict = \"$verdict\" | .evidence = \"$evidence\" | .notes = \"$notes\" else . end" \
    "$CLAIMS_FILE" > /tmp/claims-updated.jsonl
  mv /tmp/claims-updated.jsonl "$CLAIMS_FILE"

  echo "Claim $claim_num: $verdict"
}

# Examples:
# update_verdict "1" "confirmed" "Found in /home/shared/metrics.json line 42" ""
# update_verdict "2" "contradicted" "/home/shared/report.md says 5,000 not 10,000" "Must correct"
# update_verdict "3" "unconfirmed" "No supporting evidence found in available files" "Author should cite source"
# update_verdict "4" "modified" "Partially correct but missing qualifier" "Should say 'up to 10,000' not '10,000'"

6. Compute Summary Statistics

echo "=== Fact-Check Summary ==="

TOTAL=$(jq -s 'length' "$CLAIMS_FILE")
VERIFIABLE=$(jq -s '[.[] | select(.type == "verifiable")] | length' "$CLAIMS_FILE")
CONFIRMED=$(jq -s '[.[] | select(.verdict == "confirmed")] | length' "$CLAIMS_FILE")
MODIFIED=$(jq -s '[.[] | select(.verdict == "modified")] | length' "$CLAIMS_FILE")
UNCONFIRMED=$(jq -s '[.[] | select(.verdict == "unconfirmed")] | length' "$CLAIMS_FILE")
CONTRADICTED=$(jq -s '[.[] | select(.verdict == "contradicted")] | length' "$CLAIMS_FILE")
NOT_CHECKED=$(jq -s '[.[] | select(.type != "verifiable")] | length' "$CLAIMS_FILE")

echo "Total claims:       $TOTAL"
echo "Verifiable:         $VERIFIABLE"
echo "  Confirmed:        $CONFIRMED"
echo "  Modified:         $MODIFIED"
echo "  Unconfirmed:      $UNCONFIRMED"
echo "  Contradicted:     $CONTRADICTED"
echo "Not checked:        $NOT_CHECKED (opinions/projections)"

# Determine overall reliability
if [ "$VERIFIABLE" -eq 0 ]; then
  RELIABILITY="N/A -- no verifiable claims"
elif [ "$CONTRADICTED" -gt 0 ]; then
  RELIABILITY="Unreliable -- $CONTRADICTED claim(s) contradicted by evidence"
elif [ "$UNCONFIRMED" -gt $(( VERIFIABLE / 2 )) ]; then
  RELIABILITY="Low -- majority of claims lack supporting evidence"
elif [ "$UNCONFIRMED" -gt 0 ] || [ "$MODIFIED" -gt 0 ]; then
  RELIABILITY="Medium -- some claims need sources or correction"
else
  RELIABILITY="High -- all verifiable claims confirmed"
fi

echo "Overall reliability: $RELIABILITY"

7. Produce the Annotated Document

Create a version of the original document with inline annotations:

ANNOTATED_FILE="/tmp/doc-annotated.md"
cp "$DOC_FILE" "$ANNOTATED_FILE"

# For each claim with a non-confirmed verdict, insert an annotation
jq -c 'select(.verdict != "confirmed" and .verdict != "pending" and .type == "verifiable")' "$CLAIMS_FILE" \
  | sort -t'"' -k4 -rn \
  | while read claim_json; do
    LINE=$(echo "$claim_json" | jq -r '.line')
    VERDICT=$(echo "$claim_json" | jq -r '.verdict')
    NOTES=$(echo "$claim_json" | jq -r '.notes')
    CLAIM=$(echo "$claim_json" | jq -r '.claim' | head -c 80)

    # Create annotation marker
    case "$VERDICT" in
      contradicted) MARKER="[CONTRADICTED: $NOTES]" ;;
      unconfirmed)  MARKER="[UNCONFIRMED: no supporting evidence found]" ;;
      modified)     MARKER="[NEEDS CORRECTION: $NOTES]" ;;
    esac

    # Insert annotation after the relevant line
    sed -i "${LINE}s/$/ <!-- FACT-CHECK: ${VERDICT^^} -->/" "$ANNOTATED_FILE" 2>/dev/null

    echo "Annotated line $LINE: $VERDICT"
done

echo "Annotated document: $ANNOTATED_FILE"

8. Write the Full Report

REPORT_FILE="/home/shared/fact-check-$(date +%Y%m%d)-$(basename "$DOC_FILE" .md).md"
TIMESTAMP=$(date +%Y-%m-%d)
AGENT_NAME=$(whoami)

cat > "$REPORT_FILE" <<EOF
# Fact-Check Report

**Document:** $(basename "$DOC_FILE") ($DOC_FILE)
**Checked by:** ${AGENT_NAME}
**Date:** ${TIMESTAMP}

## Summary Statistics
| Metric | Count |
|--------|-------|
| Total claims extracted | ${TOTAL} |
| Verifiable claims | ${VERIFIABLE} |
| Confirmed | ${CONFIRMED} |
| Modified | ${MODIFIED} |
| Unconfirmed | ${UNCONFIRMED} |
| Contradicted | ${CONTRADICTED} |
| Opinions/Projections (not checked) | ${NOT_CHECKED} |

**Overall Reliability:** ${RELIABILITY}

## Claim Register

| # | Claim | Line | Type | Verdict | Evidence | Notes |
|---|-------|------|------|---------|----------|-------|
EOF

# Append each claim as a table row
jq -r '[.num, .claim[0:60], .line, .type, .verdict, .evidence[0:40], .notes[0:30]] | join(" | ")' \
  "$CLAIMS_FILE" | while read row; do
  echo "| $row |" >> "$REPORT_FILE"
done

cat >> "$REPORT_FILE" <<'EOF'

## Detailed Findings

EOF

# Append detailed findings for non-confirmed claims
jq -c 'select(.verdict != "confirmed" and .type == "verifiable")' "$CLAIMS_FILE" | while read claim_json; do
  NUM=$(echo "$claim_json" | jq -r '.num')
  CLAIM=$(echo "$claim_json" | jq -r '.claim')
  LINE=$(echo "$claim_json" | jq -r '.line')
  VERDICT=$(echo "$claim_json" | jq -r '.verdict')
  EVIDENCE=$(echo "$claim_json" | jq -r '.evidence')
  NOTES=$(echo "$claim_json" | jq -r '.notes')

  cat >> "$REPORT_FILE" <<EOF
### Claim ${NUM}: "${CLAIM}"
- **Location:** Line ${LINE}
- **Verdict:** ${VERDICT^}
- **Evidence:** ${EVIDENCE}
- **Action:** ${NOTES:-Review and update}

EOF
done

# Append the annotated document
cat >> "$REPORT_FILE" <<EOF
## Annotated Document

Below is the original document with fact-check annotations inline.
Claims marked with \`<!-- FACT-CHECK: VERDICT -->\` require attention.

\`\`\`
$(cat "$ANNOTATED_FILE")
\`\`\`
EOF

echo "Report written to: $REPORT_FILE"

9. Register and Notify

# Register the report as an artifact
bash /home/shared/scripts/artifact.sh register \
  --name "fact-check-$(basename "$DOC_FILE" .md)" \
  --type "fact-check" \
  --path "$REPORT_FILE" \
  --description "Fact-check report for $(basename "$DOC_FILE"): $CONFIRMED confirmed, $CONTRADICTED contradicted, $UNCONFIRMED unconfirmed"

# Notify the document author or requester
TASK_ID="${2:-}"
if [ -n "$TASK_ID" ]; then
  OWNER=$(bash /home/shared/scripts/task.sh get "$TASK_ID" 2>/dev/null | jq -r '.owner // "manager"')
  bash /home/shared/scripts/send-mail.sh "$OWNER" <<EOF
Fact-check complete for: $(basename "$DOC_FILE")

Results: $CONFIRMED confirmed, $MODIFIED need correction, $UNCONFIRMED lack evidence, $CONTRADICTED contradicted
Overall reliability: $RELIABILITY

Full report: $REPORT_FILE

$(if [ "$CONTRADICTED" -gt 0 ]; then
  echo "ACTION REQUIRED: $CONTRADICTED claim(s) are contradicted by available evidence and must be corrected."
fi)
$(if [ "$UNCONFIRMED" -gt 0 ]; then
  echo "RECOMMENDATION: $UNCONFIRMED claim(s) have no supporting evidence. Consider adding sources or softening language."
fi)
EOF
fi

echo "Notifications sent."

10. Update Task

if [ -n "$TASK_ID" ]; then
  bash /home/shared/scripts/task.sh update "$TASK_ID" \
    --status completed \
    --result "Fact-check complete: $TOTAL claims, $CONFIRMED confirmed, $CONTRADICTED contradicted. Report: $REPORT_FILE"
fi

Quality Checklist

  • Every factual statement in the document was extracted (not just obvious numbers)
  • Each claim is classified as verifiable, opinion, or projection
  • Opinions and projections presented as facts are flagged
  • Every verifiable claim was checked against available sources
  • Evidence is cited with specific file paths, line numbers, or data points
  • Verdicts use the standard vocabulary: confirmed, unconfirmed, contradicted, modified
  • Contradicted claims include the correct information from the source
  • Summary statistics are accurate and match the detailed register
  • Overall reliability rating is justified by the numbers
  • Annotated document clearly marks claims that need attention
  • The report is actionable: the author knows exactly what to fix
  • Report is registered as an artifact and requester is notified
Install via CLI
npx skills add https://github.com/PaulKinlan/docker-agent-test --skill fact-checking
Repository Details
star Stars 2
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator