From 0fe07d12f8c164c106541ae7d0781d1051d2e57a Mon Sep 17 00:00:00 2001 From: Sulthan Zaki Date: Tue, 11 Aug 2026 09:28:26 +0700 Subject: [PATCH] Record the lightnovelworld series-identity decision (#77) A Series identity is discovered from the Site's own links, never derived from an address. ADR-0008 carries the decision and the evidence behind it: 3 of 41 sampled novels serve chapters under a slug that differs from their series slug, one novel serves chapters under two, and neither slug is computable from the other. CONTEXT.md gains Chapter Slug as a term, defines it as plural by nature and never an identity, and pins Series identity to the canonical slug the Site publishes. Latest Chapter is restated as the highest-numbered chapter rather than the newest-dated one, settling #79. The research note is corrected in place where later probes refuted it: the ul.clstyle container it named is the hidden empty "Latest Reading" template rather than the chapter list, and its caveat about the comment region understated the risk, since that region is writable by any visitor and the scan takes an unbounded maximum into a shared row. Docs only; no code. Implementation is specified in #80. --- CONTEXT.md | 26 +- ...ries-identity-is-discovered-not-derived.md | 102 ++++ .../lightnovelworld-chapter-vs-series-slug.md | 572 ++++++++++++++++++ 3 files changed, 692 insertions(+), 8 deletions(-) create mode 100644 docs/adr/0008-series-identity-is-discovered-not-derived.md create mode 100644 docs/research/lightnovelworld-chapter-vs-series-slug.md diff --git a/CONTEXT.md b/CONTEXT.md index 399fcfb..2501a21 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -7,17 +7,24 @@ so progress survives across sites and devices. ## Language **Series**: -One ongoing work — a manga or a novel — as published by a Site. Identified by its -stable slug on that Site, never by its title. A Series exists once and is shared by -every Reader who bookmarks it; it owns the facts that are true regardless of who is -reading — title, cover, Latest Chapter. A Reader cannot change them; they describe the -Series, not anyone's relationship to it. +One ongoing work — a manga or a novel — as published by a Site. Identified by the canonical +slug the Site itself publishes for it, never by its title and never by a Chapter Slug. A +Series exists once and is shared by every Reader who bookmarks it; it owns the facts that +are true regardless of who is reading — title, cover, Latest Chapter. A Reader cannot +change them; they describe the Series, not anyone's relationship to it. _Avoid_: manga, title, book, comic **Site**: One third-party source a Series is published on. A Series on two Sites is two Series. _Avoid_: source, host, provider, domain +**Chapter Slug**: +A slug a Site builds its chapter addresses from. Not an identity: one Series may have +several, any of them may differ from the slug that identifies the Series, and none is +computable from another. Only the Site's own links say which ones a Series uses, so a +Chapter Slug is always discovered, never derived. +_Avoid_: series slug, url slug, permalink, chapter path + **Cover**: The image that stands for a Series wherever it is listed. A fact about the Series like its title — one Cover per Series, shared by every Reader, never per-Reader. Defined by @@ -49,9 +56,12 @@ is real activity, so only Progress reorders the list. _Avoid_: position, bookmark (the noun is taken), last read **Latest Chapter**: -The newest chapter a Site has published for a Series, discovered without the reader -present. Distinct from Progress in every way that matters: it is a fact about the Site, -not about the reader, and it must never reorder the list. +The highest-numbered chapter a Site has published for a Series, discovered without the +reader present. The number is what ranks it, never a date and never the Site's own +"newest chapter" banner — where a Site disagrees with itself, its list of chapters is +the record and its summary of that list is not. Distinct from Progress in every way +that matters: it is a fact about the Site, not about the reader, and it must never +reorder the list. _Avoid_: newest, current chapter, update **Poll**: diff --git a/docs/adr/0008-series-identity-is-discovered-not-derived.md b/docs/adr/0008-series-identity-is-discovered-not-derived.md new file mode 100644 index 0000000..00246a0 --- /dev/null +++ b/docs/adr/0008-series-identity-is-discovered-not-derived.md @@ -0,0 +1,102 @@ +# ADR-0008: A Series identity is discovered from the Site's links, never derived from an address + +Date: 2026-08-11 +Status: accepted + +## Decision + +On lightnovelworld, a Series is identified by the slug in its `/novel//` +address, and the userscript obtains that address by reading the chapter page's +`a[aria-label="All Chapter"]` anchor. It no longer constructs the address by +string manipulation of the chapter path. When neither that anchor nor the +microdata breadcrumb is present, the page resolves to `type: "other"` and no +Bookmark is offered. + +A Chapter Slug — the slug a chapter address is built from — is not an identity +and is not stored. The backend finds chapters by matching +`lightnovelworld\.net/[a-z0-9-]+-chapter-([0-9.]+)/` against the series page +body **truncated at the first `wpd-threads`**. + +## Why + +Measured on lightnovelworld between 2026-08-10 and 2026-08-11. + +The chapter slug and the series slug are two independent facts. In a 41-novel +sample, 3 diverged (~7%). `/my-longevity-simulation-chapter-1/` returns 200 +while `/novel/my-longevity-simulation/` returns 404 with no redirect, and that +novel's real address is `/novel/immortality-simulator/`. Divergence runs in both +directions: `the-sword-illuminates-the-great-wilderness` is served by chapter +slug `radiant-blade-of-the-wilderness`. Neither slug is computable from the +other, and the Site publishes no alternative-names field, so the mapping exists +only in the chapter page's own markup. + +Storing the Chapter Slug beside the identity does not work, because a Series may +have more than one. `/novel/all-jobs-and-classes-i-just-wanted-one-skill-not-them-all/` +serves chapters 1–99 under `…-one-skill-not` and 100–423 under +`…-one-skill-not-them-all`. Both resolve, and both chapter pages point back at +the same Series. + +Deriving the identity from the chapter path also made one Series produce two +rows: bookmarking from the series page yielded `lightnovelworld:immortality-simulator`, +and from a chapter page `lightnovelworld:my-longevity-simulation`. + +The pointer is reliable. Across 8 chapter pages — chapter 1, chapter 1200, the +latest chapter, both slugs of the split novel, two divergent novels, and a novel +with a number in its title — the `All Chapter` anchor and breadcrumb position 2 +were both present and agreed every time, including on the old-slug pages. Three +narrower selectors were rejected on evidence: `a[href*="/novel/"]` matches the +header nav index first; matching the text "All Chapter" false-matches the novel +titled "…Not Them All Chapter 200"; and the JSON-LD breadcrumb's position 2 is +the chapter, not the Series. + +The scan is truncated because a series page server-renders a wpdiscuz comment +thread below the chapter list, and comment bodies are HTML that can carry an +anchor. Verified on `/novel/the-sword-illuminates-the-great-wilderness/`: +comment `#wpd-comm-358_0` rendered in the initial HTML, corroborated by that +page's comment RSS feed. The scanner takes the maximum chapter number with no +upper bound, and a Series row is shared by every Reader (ADR-0003), so one +comment containing a link to a high-numbered chapter would pin that Series' +Latest Chapter for everyone. `wpd-threads` occurs exactly once per page and +follows every chapter anchor on all 4 series pages measured. `wpdcom` and +`wpdiscuz` are unusable: they occur 111 to 143 times per page, including in +`` before the chapter list. + +## Considered options + +**Correct `series_url` only, leaving the Chapter Slug as the identity.** This +repairs the Poll with no migration, because the Poll reads the stored address +rather than the identity. Rejected: it keeps an identity that the Site does not +guarantee to be stable, and leaves the duplicate-row hazard in place. + +**Scope the match to the chapter-list container.** Rejected on measurement. The +prior research named `ul.clstyle`; on all 4 pages sampled that is the hidden, +empty "Latest Reading" template, and the real list is a classless `