This post was written as part of the **Kiroween 2026 hackathon* (Resurrection category).*
The dead tech I wanted to resurrect: the hyperlink
Open any Hacker News front page from 2012 and start clicking. A lot of it is gone. I wanted a number instead of a feeling, so I built Necrolink: it measures link rot on a cohort of links and brings the dead ones back from the Wayback Machine.
The current result, generated from committed data (never hand-typed):
Of 299 links on Hacker News front-page stories from 2012, 55.2% are dead or moved today; Necrolink resurrects 84.2% of those from the Wayback Machine. Control: of 300 links from 2025, 10% are dead.
- Repo (MIT,
.kiro/committed): https://github.com/luongs3/necrolink - Live graveyard: https://luongs3.github.io/necrolink/web/
- Offline judge demo:
make judge-demo(under 1 second, no keys)
Why a control cohort
A claim like "55% of old links are dead" is useless if your checker calls everything dead. So the spec requires a second cohort: 300 links from 2025 HN. If the tool says those are dead too, the tool is broken. They come out 10% dead and 76.7% alive, so the checker isn't just a pessimism machine.
How Kiro shaped the build
Spec first, not vibes. I wrote .kiro/specs/necrolink/requirements.md in EARS form ("WHEN the user runs scan THEN the system SHALL…"), then design.md and tasks.md, and had Kiro CLI implement every task from the spec. The difference from vibe coding showed up the first time a real scan went wrong. I didn't patch the code by hand. I amended the spec and let Kiro re-implement against it:
- Wayback 429s. archive.org's availability API rate-limits under concurrency. The spec now says lookups run serially with a 1.2s gap, exponential backoff that honors
Retry-After, and a separateERRORstate that is distinct fromUNRECOVERABLE. "We couldn't ask" is not the same as "no snapshot exists". - Bot walls are not death. 401/403/405/429/436 now map to a
BLOCKEDclass that is reported in its own column and kept out of the dead%. Without this rule the 2012 number would be inflated by Cloudflare.
Both fixes live in the requirements doc, so the reason for each behavior is written down next to it.
Steering. product.md holds the one rule that matters: every headline number must be falsifiable and must have a control. tech.md keeps it zero-dependency (Node built-ins only). That stopped Kiro from pulling in an HTTP library or a test framework.
Hooks. test-on-save runs the suite whenever lib/ changes. claim-sync regenerates the README headline whenever data/ changes, so the number in the README can't drift away from the data.
Property-based tests. The URL normalizer gets randomized never-throw and idempotence tests. They caught a float-formatting bug (39.7 + 15.7 = 55.400000000000006) that would have ended up in the headline.
82 tests, all passing.
What I'd tell someone starting with Kiro
The spec is not paperwork. It's where bug fixes go. When reality disagrees with your code, change the requirement first, and the fix plus the reason for it stay in one place.
