Files
jakevin 0a85e73aa5 feat(convention): listing↔detail id pairing rule + CI gate (#1297)
* feat(convention): listing↔detail id pairing rule + CI gate

Adds a hard convention: when a site exposes both a listing-class command
(search / hot / top / recent / ...) and a detail-class command (read /
paper / article / view / ...), every listing row MUST surface an id-shaped
column whose value round-trips into the detail command. Without that, an
agent has no way to follow up on a listing row except re-searching by
title or scraping URLs out of band — both of which break the agent-native
contract.

What's in this PR

- docs/conventions/listing-detail-id-pairing.md — full rule, examples
  table, why-it-matters, what counts as id-shaped, exemption taxonomy,
  how to add an id column to a listing.
- scripts/check-listing-id-pairing.mjs — validator that reads
  cli-manifest.json, classifies each entry as listing / detail / other,
  and fails when a listing on a site that also has a read-detail command
  is missing an id-shaped column. Exemption allowlist records WHY each
  pair is exempt so future maintainers know what to verify.
- npm run check:listing-id-pairing — strict-mode wrapper.
- CI: new step in build job runs the validator after the manifest
  freshness check on Linux.
- docs/developer/ts-adapter.md — cross-link from the adapter authoring
  guide.
- docs/.vitepress/config.mts — sidebar entries for the new conventions
  section.

Fixes brought to zero violations

- 1688/search: add offer_id (already extracted, just surfaced)
- bluesky/user: add uri (AT URI round-trips into bluesky/thread)
- tieba/search: add id + url (thread_id already extracted)
- tieba/hot: add url (rows are topics, not threads — url is the
  best-effort round-trip handle, doc'd as such)

Exemptions (intentional, doc'd in EXEMPT map with rationale)

- nowcoder/hot, bluesky/trending, twitter/trending — listing rows are
  topic strings, not posts.
- lesswrong/user, reddit/user — rows are profile-attribute key/value
  pairs, addressed by the username arg.
- discord-app/search — desktop UI session, message ids not extractable.
- notion/search — Strategy.UI Quick Find, page ids not exposed in DOM.

Validator output after this PR: 32 sites scanned, 75 listings checked,
7 exempted, 0 violations.

* fix(convention): tighten listing id gate

* fix(convention): close url-derived id loophole
2026-05-04 20:54:14 +08:00
..