Changelogs don't know your repo
“The legacy endpoint stops accepting requests on 31 March 2027” is true and useless until someone greps for it. Grep doesn't know which of 40 matches matter.
Changelogs are prose. Your repo is code. Nothing connects the sentence to the line. Sunset matches a curated claim's surface tokens against your files and tells you which call sites break, and how many days you have left — then fails the policy when the answer is “not enough”.
The sample repo has four files and five call sites: three matching surfaces, one stale pinned
API version, and one unrelated service that should not be flagged. Everything below is
computed by the same POST /api/scan you can call.
scanning…
Each claim carries the verbatim sentence it was cut from. On load, Sunset re-checks that the quoted span literally appears in the stored vendor excerpt — the anchored chip is that check, not a promise. No quoted span, no claim. These ten are curated examples written for this demo, not a live vendor feed.
Why anchoring matters: a deprecation tool without a source quote is just a vibes-based linter. Every finding below can be traced back to a sentence someone can read.
“The legacy endpoint stops accepting requests on 31 March 2027” is true and useless until someone greps for it. Grep doesn't know which of 40 matches matter.
A call site is an identifier, a method chain, a pinned version string. Those are matchable without an LLM, reproducibly, in CI, on every pull request.
“Fail if any match has fewer than 30 days left” turns a blog post into a merge gate. The number is yours; the enforcement is the product.
No model call, no embeddings, no “AI-powered” asterisk. The same input produces the same findings, which is what a merge gate needs.
Read each file, strip line and block comments (keeping string state), and keep the remaining code text with line numbers.
For each claim, require all of its surface tokens to appear with word-ish boundaries. Tokens are identifiers, method chains, paths, and pinned version strings.
Days left = effective date − today, in whole UTC days. Severity comes from the claim, not from the match.
Every match with days left below the policy threshold becomes a policy violation and flips the verdict to FAIL.
# the ledger, with anchor status GET /api/claims # four files + default policy GET /api/sample-repo # scan a snippet or a whole repo POST /api/scan { "code": "stripe.charges.create({...})", "file": "payments.ts", "policyDays": 30 }
// response shape { "matches": [{ "file": "payments.ts", "line": 9, "claimId": "stripe-charges", "vendor": "Stripe", "daysLeft": 147, "severity": "critical", "quote": "The legacy charges.create…", "migration": "Replace with paymentIntents…" }], "counts": { "matches": 4, "policyViolations": 2 }, "policyViolations": [ ... ], "reportMarkdown": "# Sunset scan report…" }
A tool that hides its edges is a demo. These are the edges.
A wrapper like charge() that calls charges.create elsewhere hides the usage. Dynamic construction, aliases, and generated clients defeat token matching. Findings are candidates, not proof.
Quotes, dates, and URLs drift. The ten claims here are curated examples with plausible prose, not a live feed. Re-anchor claims against the vendor page before trusting a date.
The scan is advisory. Making it a merge gate — scheduled re-scans, per-repo policy files, ownership routing, audit history — is the part that costs money. This page deliberately stops at the report.