🟡 CI for facts
You get: a build that fails when your landing page lies. Pairs with: a weekly GitHub Action and marketing’s forgiveness.
tabstack generate json https://yoursite.com
--instructions "List every quantitative claim on this page — numbers, counts, latencies — with its exact text."
--schema '{"type":"object","properties":{"claims":{"type":"array","items":{
"type":"object","properties":{"text":{"type":"string"},"value":{"type":"number"}}}}}}'
| jq -e '.claims | length > 0' The idea
“Trusted by 500+ teams.” “Sub-100ms p99.” “99.9% uptime.” Marketing pages are full of numbers that were true when someone wrote them and drift quietly after.
This recipe extracts every quantitative claim from your landing page as structured data. Add it to CI and you can assert those claims against real data, fail the build when they’re wrong, and catch content rot the same way you’d catch a broken test.
GitHub Action
name: Fact-check landing page
on:
schedule:
- cron: '0 9 * * 1' # Monday mornings
workflow_dispatch:
jobs:
fact-check:
runs-on: ubuntu-latest
steps:
- name: Extract claims
env:
TABSTACK_API_KEY: ${{ secrets.TABSTACK_API_KEY }}
run: |
tabstack generate json https://yoursite.com --instructions "List every quantitative claim with its exact text and numeric value." --schema '{"type":"object","properties":{"claims":{"type":"array","items":{
"type":"object","properties":{"text":{"type":"string"},"value":{"type":"number"}}}}}}' > claims.json
echo "Claims found:"
cat claims.json | jq -r '.claims[] | " • (.text)"'
- name: Verify customer count
run: |
# Example: assert the "500+ teams" claim against your real DB count
CLAIMED=$(cat claims.json | jq '.claims[] | select(.text | test("teams")) | .value')
ACTUAL=$(curl -s https://api.yoursite.com/stats | jq .customer_count)
if [ "$ACTUAL" -lt "$CLAIMED" ]; then
echo "❌ Landing page claims $CLAIMED teams but DB shows $ACTUAL"
exit 1
fi Simpler version — just detect claim drift
If you don’t have an API to compare against, at least detect when the page changes:
# Save a snapshot
tabstack generate json https://yoursite.com
--instructions "List all quantitative claims."
--schema '{"type":"object","properties":{"claims":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"}}}}}}'
| jq -r '.claims[].text' | sort > claims-snapshot.txt
# In CI: compare against the snapshot
tabstack generate json https://yoursite.com
--instructions "List all quantitative claims."
--schema '{"type":"object","properties":{"claims":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"}}}}}}'
| jq -r '.claims[].text' | sort > claims-current.txt
diff claims-snapshot.txt claims-current.txt || echo "Claims changed — review manually" Why generate not extract
extract json structures what’s on the page literally. generate json reasons about it with your instructions — in this case, identifying which text elements are making quantitative claims, even when they’re phrased as “more than 500” or “over 10,000”. The instruction “list every quantitative claim” applies semantic judgment that pure extraction can’t.