chore: drop asuracomic.net from the userscript, CORS allowlist and docs (#97)

Closes #96.

## What

Removes every reference that still invites a Reader onto `asuracomic.net`.
The domain's deep links 301 to the `asurascans.com` **root**, discarding the
path (re-checked 2026-07-25), so a page on it never yields a series document
client-side and a stored address on it never yields a series page server-side.
#95 already pinned each Site to one hostname, so the backend rejects such an
address cleanly; this is the cleanup around that.

| File | Change |
|---|---|
| `userscript/manga-bookmark.user.js` | drops the `@match`, narrows the asura adapter to `/(^\|\.)asurascans\.com$/` |
| `userscript/test/logic.test.js` | new test pinning the narrowed host match |
| `.env.example`, `docker-compose.yml` | origin dropped from the `ALLOWED_ORIGINS` default |
| `DEPLOY.md` | same, and the sample list gains the two novel origins it was missing |
| `backend/api_test.go` | CORS fixtures and round-trip seed move to `asurascans.com` |
| `README.md`, `AGENTS.md` | notes say the host is dropped, not "stays matched" |

## Behaviour

- A Reader landing on `asuracomic.net` gets no userscript UI. Previously the
  script loaded and could do nothing useful — the redirect had already
  discarded the path.
- A request whose `Origin` is `https://asuracomic.net` is no longer reflected
  by a deployment using the shipped defaults.
- No backend logic changed: the CORS rule, the address gate and the poller are
  untouched. `AllowedOrigins` is data, not code.

## Security invariant preserved

CORS still reflects `Origin` only when it appears in `ALLOWED_ORIGINS`, with
`GET,PUT,DELETE,OPTIONS` and a `204` preflight — `TestCORSPreflight` and
`TestCORSDisallowedOrigin` still pin both halves, now against a live origin.
This change only removes a value from the allowlist, which is a narrowing.

## Verification

- `go test ./...` — full backend suite green (real Postgres per package).
- `node --test test/*.test.js` — 66/66 green, up one from the new match test.

## Deploy note (does not happen on merge)

The live allowlist comes from the VPS `.env`, not from these defaults, so the
origin must be dropped there in the same deploy. The one-off row repair for any
stored `asuracomic.net` address is in #96.

Reviewed-on: #97
Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com>
Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
This commit was merged in pull request #97.
This commit is contained in:
2026-08-12 05:53:17 +07:00
committed by sulthan
parent 21615be2bd
commit c62c3bb07b
13 changed files with 1351 additions and 1992 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ OWNER_DISCORD_ID=changeme-your-discord-user-id
# Comma-separated origins allowed to call the API (CORS). Both Asura domains # Comma-separated origins allowed to call the API (CORS). Both Asura domains
# plus Demonic, Comix, Kagane, and the two novel sites. Add/remove as the # plus Demonic, Comix, Kagane, and the two novel sites. Add/remove as the
# sites' hostnames change. # sites' hostnames change.
ALLOWED_ORIGINS=https://asuracomic.net,https://asurascans.com,https://demonicscans.org,https://comix.to,https://kagane.to,https://novelfull.com,https://lightnovelworld.net ALLOWED_ORIGINS=https://asurascans.com,https://demonicscans.org,https://comix.to,https://kagane.to,https://novelfull.com,https://lightnovelworld.net
# Password for the bundled Postgres container, and therefore half of the # Password for the bundled Postgres container, and therefore half of the
# DATABASE_URL compose builds for the backend. Generate one: # DATABASE_URL compose builds for the backend. Generate one:
+1 -1
View File
@@ -6,7 +6,7 @@ Guidance for OpenCode (and Claude Code) working in this repo.
Read-progress tracker for two libraries — manga and novels — behind one self-hosted Go backend. Two separate Violentmonkey userscripts inject on-page UI (floating button + slide-in panel) and sync progress, so bookmarks unify across sites and devices: Read-progress tracker for two libraries — manga and novels — behind one self-hosted Go backend. Two separate Violentmonkey userscripts inject on-page UI (floating button + slide-in panel) and sync progress, so bookmarks unify across sites and devices:
- `manga-bookmark.user.js` — **asurascans.com** (current domain; asuracomic.net 301s here), **demonicscans.org**, **comix.to**, **kagane.to**. - `manga-bookmark.user.js` — **asurascans.com** (asuracomic.net is dropped: its deep links 301 to the asurascans.com root, discarding the path), **demonicscans.org**, **comix.to**, **kagane.to**.
- `novel-bookmark.user.js` — **novelfull.com**, **lightnovelworld.net**. - `novel-bookmark.user.js` — **novelfull.com**, **lightnovelworld.net**.
One backend, one `bookmarks` table: a `kind` column (`manga`|`novel`) splits the libraries and the web UI switches between them. Rows are keyed `<site>:<series_id>`. One backend, one `bookmarks` table: a `kind` column (`manga`|`novel`) splits the libraries and the web UI switches between them. Rows are keyed `<site>:<series_id>`.
+1 -1
View File
@@ -43,7 +43,7 @@ TOKEN_KEY=<paste output of: openssl rand -hex 32>
OWNER_DISCORD_ID=<discord user id> OWNER_DISCORD_ID=<discord user id>
# CORS allowlist — leave as-is unless a site changes hostname. # CORS allowlist — leave as-is unless a site changes hostname.
ALLOWED_ORIGINS=https://asuracomic.net,https://asurascans.com,https://demonicscans.org,https://comix.to,https://kagane.to ALLOWED_ORIGINS=https://asurascans.com,https://demonicscans.org,https://comix.to,https://kagane.to,https://novelfull.com,https://lightnovelworld.net
# Required — password for the bundled Postgres container. Compose builds the # Required — password for the bundled Postgres container. Compose builds the
# backend's DATABASE_URL out of it and has no fallback for either. # backend's DATABASE_URL out of it and has no fallback for either.
+7 -6
View File
@@ -1,6 +1,6 @@
# Manga Bookmark # Manga Bookmark
Track manga read-progress on **asurascans.com** (a.k.a. asuracomic.net), Track manga read-progress on **asurascans.com**,
**demonicscans.org**, **comix.to**, and **kagane.to** from a phone (Bromite / **demonicscans.org**, **comix.to**, and **kagane.to** from a phone (Bromite /
mobile Chromium), synced to a self-hosted Go backend so bookmarks unify across mobile Chromium), synced to a self-hosted Go backend so bookmarks unify across
all four sites and all devices. all four sites and all devices.
@@ -274,11 +274,12 @@ the backend acquires, stores and serves every Cover from its own origin
| **Kagane** (`kagane.to`) | `/series/<uuid>` | `/series/<uuid>/reader/<bookUuid>` | `<uuid>` | | **Kagane** (`kagane.to`) | `/series/<uuid>` | `/series/<uuid>/reader/<bookUuid>` | `<uuid>` |
Notes: Notes:
- **`asuracomic.net` deep links are dead (re-checked 2026-07-25).** They 301 to - **`asuracomic.net` is no longer matched (deep links dead, re-checked
the `asurascans.com` **root**, discarding the path, at the edge — before the 2026-07-25).** They 301 to the `asurascans.com` **root**, discarding the path,
userscript gets a document — so nothing client-side can rescue them. Reach at the edge — before the userscript gets a document — so nothing client-side
series through `asurascans.com`. The host stays matched in case the redirect can rescue them. The backend rejects stored addresses on that host too, since
starts preserving paths again. the poller pins each Site to one hostname. Reach series through
`asurascans.com`.
- Asura `og:title` carries a `Chapter N - Read Online \| Asura Scans` suffix that - Asura `og:title` carries a `Chapter N - Read Online \| Asura Scans` suffix that
the adapter strips; Demonic chapter `og:title` is `<Title> Chapter N`. the adapter strips; Demonic chapter `og:title` is `<Title> Chapter N`.
- Demonic's `<slug>` is identical on `/manga/…` and the canonical `/title/…` - Demonic's `<slug>` is identical on `/manga/…` and the canonical `/title/…`
+6 -6
View File
@@ -33,7 +33,7 @@ const testCoverBaseURL = "https://bookmarks.test"
func testConfig() Config { func testConfig() Config {
return Config{ return Config{
TokenKey: testTokenKey, TokenKey: testTokenKey,
AllowedOrigins: []string{"https://asuracomic.net", "https://demonicscans.org"}, AllowedOrigins: []string{"https://asurascans.com", "https://demonicscans.org"},
Port: "8080", Port: "8080",
} }
} }
@@ -169,7 +169,7 @@ func TestAuthAccepted(t *testing.T) {
func TestCORSPreflight(t *testing.T) { func TestCORSPreflight(t *testing.T) {
srv := newTestServer(t) srv := newTestServer(t)
req := httptest.NewRequest(http.MethodOptions, "/bookmarks/asura:foo-1", nil) req := httptest.NewRequest(http.MethodOptions, "/bookmarks/asura:foo-1", nil)
req.Header.Set("Origin", "https://asuracomic.net") req.Header.Set("Origin", "https://asurascans.com")
req.Header.Set("Access-Control-Request-Method", "PUT") req.Header.Set("Access-Control-Request-Method", "PUT")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
srv.ServeHTTP(rr, req) srv.ServeHTTP(rr, req)
@@ -177,7 +177,7 @@ func TestCORSPreflight(t *testing.T) {
if rr.Code != http.StatusNoContent { if rr.Code != http.StatusNoContent {
t.Fatalf("preflight status = %d, want 204", rr.Code) t.Fatalf("preflight status = %d, want 204", rr.Code)
} }
if got := rr.Header().Get("Access-Control-Allow-Origin"); got != "https://asuracomic.net" { if got := rr.Header().Get("Access-Control-Allow-Origin"); got != "https://asurascans.com" {
t.Fatalf("Allow-Origin = %q, want reflected origin", got) t.Fatalf("Allow-Origin = %q, want reflected origin", got)
} }
if got := rr.Header().Get("Access-Control-Allow-Methods"); got == "" { if got := rr.Header().Get("Access-Control-Allow-Methods"); got == "" {
@@ -204,11 +204,11 @@ func TestBookmarkRoundTrip(t *testing.T) {
key := "asura:solo-leveling-123" key := "asura:solo-leveling-123"
in := store.Bookmark{ in := store.Bookmark{
Title: "Solo Leveling", Title: "Solo Leveling",
SeriesURL: "https://asuracomic.net/series/solo-leveling-123", SeriesURL: "https://asurascans.com/series/solo-leveling-123",
Cover: "https://asuracomic.net/cover.jpg", Cover: "https://asurascans.com/cover.jpg",
LastChapter: "Chapter 10", LastChapter: "Chapter 10",
LastChapterNum: 10, LastChapterNum: 10,
LastChapterURL: "https://asuracomic.net/series/solo-leveling-123/chapter/10", LastChapterURL: "https://asurascans.com/series/solo-leveling-123/chapter/10",
} }
body, _ := json.Marshal(in) body, _ := json.Marshal(in)
+1 -1
View File
@@ -25,7 +25,7 @@ services:
# Owner's Discord user ID — required. Seeds the owner Reader (the # Owner's Discord user ID — required. Seeds the owner Reader (the
# administrator); every other Reader registers on their first login. # administrator); every other Reader registers on their first login.
OWNER_DISCORD_ID: ${OWNER_DISCORD_ID:?set OWNER_DISCORD_ID in .env} OWNER_DISCORD_ID: ${OWNER_DISCORD_ID:?set OWNER_DISCORD_ID in .env}
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-https://asuracomic.net,https://asurascans.com,https://demonicscans.org,https://comix.to,https://kagane.to,https://novelfull.com,https://lightnovelworld.net} ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-https://asurascans.com,https://demonicscans.org,https://comix.to,https://kagane.to,https://novelfull.com,https://lightnovelworld.net}
# The bookmarks database. Host is the compose service name; the password # The bookmarks database. Host is the compose service name; the password
# comes from .env so it is never committed. # comes from .env so it is never committed.
DATABASE_URL: ${DATABASE_URL:-postgres://bookmarks:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@postgres:5432/bookmarks?sslmode=disable} DATABASE_URL: ${DATABASE_URL:-postgres://bookmarks:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@postgres:5432/bookmarks?sslmode=disable}
-29
View File
@@ -15,10 +15,8 @@
"13": "Web UI Handlers", "13": "Web UI Handlers",
"14": "Go Error Handling", "14": "Go Error Handling",
"15": "CDP Browser Client", "15": "CDP Browser Client",
"16": "Store",
"17": "Go Code Style Guide", "17": "Go Code Style Guide",
"18": "Agent Skills", "18": "Agent Skills",
"19": "Open",
"20": "I/O Performance Patterns", "20": "I/O Performance Patterns",
"21": "CPU Optimization", "21": "CPU Optimization",
"22": "Caching Patterns", "22": "Caching Patterns",
@@ -29,7 +27,6 @@
"27": "Find Skills Guide", "27": "Find Skills Guide",
"28": "Allocation Patterns", "28": "Allocation Patterns",
"29": "Observability & Alerting", "29": "Observability & Alerting",
"30": "PG Test Infrastructure",
"31": "Memory Layout", "31": "Memory Layout",
"32": "Repo Hard Constraints", "32": "Repo Hard Constraints",
"33": "Go Testing Guide", "33": "Go Testing Guide",
@@ -40,8 +37,6 @@
"38": "novel-logic.test.js", "38": "novel-logic.test.js",
"39": "UI Critique 2026-07-26A", "39": "UI Critique 2026-07-26A",
"40": "UI Critique 2026-07-26B", "40": "UI Critique 2026-07-26B",
"41": "DB Cutover Runbook",
"42": "Memory Layout Patterns",
"43": "Issue Tracker & Triage", "43": "Issue Tracker & Triage",
"44": "Ticket Workflow", "44": "Ticket Workflow",
"45": "Go Perf Alert Rules", "45": "Go Perf Alert Rules",
@@ -124,7 +119,6 @@
"122": "Identity comes from Discord OAuth; we store no passwords and send no email", "122": "Identity comes from Discord OAuth; we store no passwords and send no email",
"123": "The wire format stays flat and deliberately does not mirror the schema", "123": "The wire format stays flat and deliberately does not mirror the schema",
"124": "ADR-0005: On-demand browser sidecar", "124": "ADR-0005: On-demand browser sidecar",
"125": "Duration",
"126": "Bookmark Manager", "126": "Bookmark Manager",
"127": "triage-labels.md", "127": "triage-labels.md",
"128": "Cross-Ticket Contract", "128": "Cross-Ticket Contract",
@@ -152,14 +146,6 @@
"150": "Reviewer Subagent (opencode)", "150": "Reviewer Subagent (opencode)",
"151": "Finding Severity Rubric", "151": "Finding Severity Rubric",
"152": "Spec Compliance Review", "152": "Spec Compliance Review",
"153": "CDP Browser Unit (chrome/)",
"154": "Cinder Design System",
"155": "Cloudflare Challenge Behaviour",
"156": "Ember Means New Chapter Only",
"157": "Gitea + tea CLI",
"158": "Graphify Knowledge Graph",
"159": "novel-bookmark.user.js",
"160": "Security Invariants",
"161": "Why Use samber/oops", "161": "Why Use samber/oops",
"162": "singleflight Cache Stampede Prevention", "162": "singleflight Cache Stampede Prevention",
"163": "Struct Field Alignment", "163": "Struct Field Alignment",
@@ -184,15 +170,6 @@
"194": "SQLite-to-Postgres Cutover Runbook", "194": "SQLite-to-Postgres Cutover Runbook",
"195": "Import SQL Generation Rules", "195": "Import SQL Generation Rules",
"196": "Throwaway Import Generator", "196": "Throwaway Import Generator",
"197": "ADR-0002 (Discord OAuth Sign-In)",
"198": "ADR-0006 (Browser Out of API Stack)",
"199": "ADR-0007 (Absolute Cover URLs)",
"200": "bookmark-api Service",
"201": "Deployment Runbook",
"202": "Issue #24 (Derived Credentials)",
"203": "Tailscale ACL Tagging",
"204": "TOKEN_KEY Credential Derivation",
"205": "Traefik Reverse Proxy",
"206": "Real scaling limit is the poller outbound fetch budget", "206": "Real scaling limit is the poller outbound fetch budget",
"207": "PostgreSQL (jackc/pgx/v5)", "207": "PostgreSQL (jackc/pgx/v5)",
"208": "SQLite (modernc.org/sqlite)", "208": "SQLite (modernc.org/sqlite)",
@@ -249,12 +226,6 @@
"259": "Dark-First Design Constraint", "259": "Dark-First Design Constraint",
"260": "Discord Guild Membership", "260": "Discord Guild Membership",
"261": "Reader Isolation Invariant", "261": "Reader Isolation Invariant",
"262": "Site Adapters",
"263": "Go Backend (backend/)",
"264": "localStorage Cache and Retry Queue",
"265": "Latest Chapter Poller",
"266": "Postgres Store (postgres:17-alpine)",
"267": "README Config and Endpoint Reference",
"268": "Browser Unit Redeploy", "268": "Browser Unit Redeploy",
"269": "pg_dump Hot Backup", "269": "pg_dump Hot Backup",
"270": "Redeploy Runbook", "270": "Redeploy Runbook",
+29 -76
View File
@@ -1,16 +1,16 @@
# Graph Report - mangaBookmark (2026-08-12) # Graph Report - mangaBookmark (2026-08-12)
## Corpus Check ## Corpus Check
- 110 files · ~267,753 words - 110 files · ~267,791 words
- Verdict: corpus is large enough that graph structure adds value. - Verdict: corpus is large enough that graph structure adds value.
## Summary ## Summary
- 1651 nodes · 3253 edges · 262 communities (67 shown, 195 thin omitted) - 1621 nodes · 3242 edges · 233 communities (63 shown, 170 thin omitted)
- Extraction: 90% EXTRACTED · 10% INFERRED · 0% AMBIGUOUS · INFERRED: 314 edges (avg confidence: 0.77) - Extraction: 90% EXTRACTED · 10% INFERRED · 0% AMBIGUOUS · INFERRED: 312 edges (avg confidence: 0.77)
- Token cost: 0 input · 0 output - Token cost: 0 input · 0 output
## Graph Freshness ## Graph Freshness
- Built from commit: `700de202` - Built from commit: `8ae98816`
- Run `git rev-parse HEAD` and compare to check if the graph is stale. - Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost). - Run `graphify update .` after code changes (no API cost).
@@ -31,10 +31,8 @@
- [[_COMMUNITY_Web UI Handlers|Web UI Handlers]] - [[_COMMUNITY_Web UI Handlers|Web UI Handlers]]
- [[_COMMUNITY_Go Error Handling|Go Error Handling]] - [[_COMMUNITY_Go Error Handling|Go Error Handling]]
- [[_COMMUNITY_CDP Browser Client|CDP Browser Client]] - [[_COMMUNITY_CDP Browser Client|CDP Browser Client]]
- [[_COMMUNITY_Store|Store]]
- [[_COMMUNITY_Go Code Style Guide|Go Code Style Guide]] - [[_COMMUNITY_Go Code Style Guide|Go Code Style Guide]]
- [[_COMMUNITY_Agent Skills|Agent Skills]] - [[_COMMUNITY_Agent Skills|Agent Skills]]
- [[_COMMUNITY_Open|Open]]
- [[_COMMUNITY_IO Performance Patterns|I/O Performance Patterns]] - [[_COMMUNITY_IO Performance Patterns|I/O Performance Patterns]]
- [[_COMMUNITY_CPU Optimization|CPU Optimization]] - [[_COMMUNITY_CPU Optimization|CPU Optimization]]
- [[_COMMUNITY_Caching Patterns|Caching Patterns]] - [[_COMMUNITY_Caching Patterns|Caching Patterns]]
@@ -45,7 +43,6 @@
- [[_COMMUNITY_Find Skills Guide|Find Skills Guide]] - [[_COMMUNITY_Find Skills Guide|Find Skills Guide]]
- [[_COMMUNITY_Allocation Patterns|Allocation Patterns]] - [[_COMMUNITY_Allocation Patterns|Allocation Patterns]]
- [[_COMMUNITY_Observability & Alerting|Observability & Alerting]] - [[_COMMUNITY_Observability & Alerting|Observability & Alerting]]
- [[_COMMUNITY_PG Test Infrastructure|PG Test Infrastructure]]
- [[_COMMUNITY_Memory Layout|Memory Layout]] - [[_COMMUNITY_Memory Layout|Memory Layout]]
- [[_COMMUNITY_Repo Hard Constraints|Repo Hard Constraints]] - [[_COMMUNITY_Repo Hard Constraints|Repo Hard Constraints]]
- [[_COMMUNITY_Go Testing Guide|Go Testing Guide]] - [[_COMMUNITY_Go Testing Guide|Go Testing Guide]]
@@ -56,8 +53,6 @@
- [[_COMMUNITY_novel-logic.test.js|novel-logic.test.js]] - [[_COMMUNITY_novel-logic.test.js|novel-logic.test.js]]
- [[_COMMUNITY_UI Critique 2026-07-26A|UI Critique 2026-07-26A]] - [[_COMMUNITY_UI Critique 2026-07-26A|UI Critique 2026-07-26A]]
- [[_COMMUNITY_UI Critique 2026-07-26B|UI Critique 2026-07-26B]] - [[_COMMUNITY_UI Critique 2026-07-26B|UI Critique 2026-07-26B]]
- [[_COMMUNITY_DB Cutover Runbook|DB Cutover Runbook]]
- [[_COMMUNITY_Memory Layout Patterns|Memory Layout Patterns]]
- [[_COMMUNITY_Issue Tracker & Triage|Issue Tracker & Triage]] - [[_COMMUNITY_Issue Tracker & Triage|Issue Tracker & Triage]]
- [[_COMMUNITY_Ticket Workflow|Ticket Workflow]] - [[_COMMUNITY_Ticket Workflow|Ticket Workflow]]
- [[_COMMUNITY_Go Perf Alert Rules|Go Perf Alert Rules]] - [[_COMMUNITY_Go Perf Alert Rules|Go Perf Alert Rules]]
@@ -140,7 +135,6 @@
- [[_COMMUNITY_Identity comes from Discord OAuth; we store no passwords and send no email|Identity comes from Discord OAuth; we store no passwords and send no email]] - [[_COMMUNITY_Identity comes from Discord OAuth; we store no passwords and send no email|Identity comes from Discord OAuth; we store no passwords and send no email]]
- [[_COMMUNITY_The wire format stays flat and deliberately does not mirror the schema|The wire format stays flat and deliberately does not mirror the schema]] - [[_COMMUNITY_The wire format stays flat and deliberately does not mirror the schema|The wire format stays flat and deliberately does not mirror the schema]]
- [[_COMMUNITY_ADR-0005 On-demand browser sidecar|ADR-0005: On-demand browser sidecar]] - [[_COMMUNITY_ADR-0005 On-demand browser sidecar|ADR-0005: On-demand browser sidecar]]
- [[_COMMUNITY_Duration|Duration]]
- [[_COMMUNITY_Bookmark Manager|Bookmark Manager]] - [[_COMMUNITY_Bookmark Manager|Bookmark Manager]]
- [[_COMMUNITY_triage-labels|triage-labels.md]] - [[_COMMUNITY_triage-labels|triage-labels.md]]
- [[_COMMUNITY_Cross-Ticket Contract|Cross-Ticket Contract]] - [[_COMMUNITY_Cross-Ticket Contract|Cross-Ticket Contract]]
@@ -168,14 +162,6 @@
- [[_COMMUNITY_Reviewer Subagent (opencode)|Reviewer Subagent (opencode)]] - [[_COMMUNITY_Reviewer Subagent (opencode)|Reviewer Subagent (opencode)]]
- [[_COMMUNITY_Finding Severity Rubric|Finding Severity Rubric]] - [[_COMMUNITY_Finding Severity Rubric|Finding Severity Rubric]]
- [[_COMMUNITY_Spec Compliance Review|Spec Compliance Review]] - [[_COMMUNITY_Spec Compliance Review|Spec Compliance Review]]
- [[_COMMUNITY_CDP Browser Unit (chrome)|CDP Browser Unit (chrome/)]]
- [[_COMMUNITY_Cinder Design System|Cinder Design System]]
- [[_COMMUNITY_Cloudflare Challenge Behaviour|Cloudflare Challenge Behaviour]]
- [[_COMMUNITY_Ember Means New Chapter Only|Ember Means New Chapter Only]]
- [[_COMMUNITY_Gitea + tea CLI|Gitea + tea CLI]]
- [[_COMMUNITY_Graphify Knowledge Graph|Graphify Knowledge Graph]]
- [[_COMMUNITY_novel-bookmark.user.js|novel-bookmark.user.js]]
- [[_COMMUNITY_Security Invariants|Security Invariants]]
- [[_COMMUNITY_Why Use samberoops|Why Use samber/oops]] - [[_COMMUNITY_Why Use samberoops|Why Use samber/oops]]
- [[_COMMUNITY_singleflight Cache Stampede Prevention|singleflight Cache Stampede Prevention]] - [[_COMMUNITY_singleflight Cache Stampede Prevention|singleflight Cache Stampede Prevention]]
- [[_COMMUNITY_Struct Field Alignment|Struct Field Alignment]] - [[_COMMUNITY_Struct Field Alignment|Struct Field Alignment]]
@@ -199,15 +185,6 @@
- [[_COMMUNITY_SQLite-to-Postgres Cutover Runbook|SQLite-to-Postgres Cutover Runbook]] - [[_COMMUNITY_SQLite-to-Postgres Cutover Runbook|SQLite-to-Postgres Cutover Runbook]]
- [[_COMMUNITY_Import SQL Generation Rules|Import SQL Generation Rules]] - [[_COMMUNITY_Import SQL Generation Rules|Import SQL Generation Rules]]
- [[_COMMUNITY_Throwaway Import Generator|Throwaway Import Generator]] - [[_COMMUNITY_Throwaway Import Generator|Throwaway Import Generator]]
- [[_COMMUNITY_ADR-0002 (Discord OAuth Sign-In)|ADR-0002 (Discord OAuth Sign-In)]]
- [[_COMMUNITY_ADR-0006 (Browser Out of API Stack)|ADR-0006 (Browser Out of API Stack)]]
- [[_COMMUNITY_ADR-0007 (Absolute Cover URLs)|ADR-0007 (Absolute Cover URLs)]]
- [[_COMMUNITY_bookmark-api Service|bookmark-api Service]]
- [[_COMMUNITY_Deployment Runbook|Deployment Runbook]]
- [[_COMMUNITY_Issue 24 (Derived Credentials)|Issue #24 (Derived Credentials)]]
- [[_COMMUNITY_Tailscale ACL Tagging|Tailscale ACL Tagging]]
- [[_COMMUNITY_TOKEN_KEY Credential Derivation|TOKEN_KEY Credential Derivation]]
- [[_COMMUNITY_Traefik Reverse Proxy|Traefik Reverse Proxy]]
- [[_COMMUNITY_Real scaling limit is the poller outbound fetch budget|Real scaling limit is the poller outbound fetch budget]] - [[_COMMUNITY_Real scaling limit is the poller outbound fetch budget|Real scaling limit is the poller outbound fetch budget]]
- [[_COMMUNITY_PostgreSQL (jackcpgxv5)|PostgreSQL (jackc/pgx/v5)]] - [[_COMMUNITY_PostgreSQL (jackcpgxv5)|PostgreSQL (jackc/pgx/v5)]]
- [[_COMMUNITY_SQLite (modernc.orgsqlite)|SQLite (modernc.org/sqlite)]] - [[_COMMUNITY_SQLite (modernc.orgsqlite)|SQLite (modernc.org/sqlite)]]
@@ -264,12 +241,6 @@
- [[_COMMUNITY_Dark-First Design Constraint|Dark-First Design Constraint]] - [[_COMMUNITY_Dark-First Design Constraint|Dark-First Design Constraint]]
- [[_COMMUNITY_Discord Guild Membership|Discord Guild Membership]] - [[_COMMUNITY_Discord Guild Membership|Discord Guild Membership]]
- [[_COMMUNITY_Reader Isolation Invariant|Reader Isolation Invariant]] - [[_COMMUNITY_Reader Isolation Invariant|Reader Isolation Invariant]]
- [[_COMMUNITY_Site Adapters|Site Adapters]]
- [[_COMMUNITY_Go Backend (backend)|Go Backend (backend/)]]
- [[_COMMUNITY_localStorage Cache and Retry Queue|localStorage Cache and Retry Queue]]
- [[_COMMUNITY_Latest Chapter Poller|Latest Chapter Poller]]
- [[_COMMUNITY_Postgres Store (postgres17-alpine)|Postgres Store (postgres:17-alpine)]]
- [[_COMMUNITY_README Config and Endpoint Reference|README Config and Endpoint Reference]]
- [[_COMMUNITY_Browser Unit Redeploy|Browser Unit Redeploy]] - [[_COMMUNITY_Browser Unit Redeploy|Browser Unit Redeploy]]
- [[_COMMUNITY_pg_dump Hot Backup|pg_dump Hot Backup]] - [[_COMMUNITY_pg_dump Hot Backup|pg_dump Hot Backup]]
- [[_COMMUNITY_Redeploy Runbook|Redeploy Runbook]] - [[_COMMUNITY_Redeploy Runbook|Redeploy Runbook]]
@@ -307,15 +278,13 @@
## Hyperedges (group relationships) ## Hyperedges (group relationships)
- **Batch Ticket Implementation Pipeline** — _claude_skills_implement_tickets_skill_implement_tickets, _omp_agents_ticket_implementer_ticket_implementer, _omp_agents_ticket_implementer_cr_spec, _omp_agents_ticket_implementer_cr_standards [INFERRED 0.85] - **Batch Ticket Implementation Pipeline** — _claude_skills_implement_tickets_skill_implement_tickets, _omp_agents_ticket_implementer_ticket_implementer, _omp_agents_ticket_implementer_cr_spec, _omp_agents_ticket_implementer_cr_standards [INFERRED 0.85]
- **Subagent-Driven Development Pipeline** — _opencode_agent_implementer_implementer, _opencode_agent_reviewer_reviewer, _opencode_agent_implementer_subagent_driven_development [INFERRED 0.85] - **Subagent-Driven Development Pipeline** — _opencode_agent_implementer_implementer, _opencode_agent_reviewer_reviewer, _opencode_agent_implementer_subagent_driven_development [INFERRED 0.85]
- **Deployment Runbook Family** — cutover_cutover, deploy_deploy, redeploy_redeploy [INFERRED 0.85]
- **Go HTML Template Family** — backend_internal_web_templates_app_doc, backend_internal_web_templates_card_doc, backend_internal_web_templates_list_doc, backend_internal_web_templates_chrome_doc, backend_internal_web_templates_login_doc, backend_internal_web_templates_setup_doc, backend_internal_web_templates_readers_doc, backend_internal_web_templates_icons_doc [INFERRED 0.95] - **Go HTML Template Family** — backend_internal_web_templates_app_doc, backend_internal_web_templates_card_doc, backend_internal_web_templates_list_doc, backend_internal_web_templates_chrome_doc, backend_internal_web_templates_login_doc, backend_internal_web_templates_setup_doc, backend_internal_web_templates_readers_doc, backend_internal_web_templates_icons_doc [INFERRED 0.95]
- **htmx Fragment Swap Flow** — backend_internal_web_templates_app_doc, backend_internal_web_templates_card_doc, backend_internal_web_templates_chrome_doc, backend_internal_web_templates_setup_doc, backend_internal_web_templates_readers_doc [INFERRED 0.95] - **htmx Fragment Swap Flow** — backend_internal_web_templates_app_doc, backend_internal_web_templates_card_doc, backend_internal_web_templates_chrome_doc, backend_internal_web_templates_setup_doc, backend_internal_web_templates_readers_doc [INFERRED 0.95]
- **Deployment Topology** — docker_compose_bookmark_api, docker_compose_postgres, docker_compose_prod_traefik, chrome_docker_compose_browser, docker_compose_db_network [INFERRED 0.95]
- **Backend owns the truth (single-writer ownership of shared facts)** — docs_adr_0003_series_shared_and_poll_owned_poll_owned_writes, docs_adr_0004_wire_format_does_not_mirror_the_schema_flat_wire_contract, docs_adr_0007_backend_hosts_cover_bytes_server_side_covers [INFERRED 0.85] - **Backend owns the truth (single-writer ownership of shared facts)** — docs_adr_0003_series_shared_and_poll_owned_poll_owned_writes, docs_adr_0004_wire_format_does_not_mirror_the_schema_flat_wire_contract, docs_adr_0007_backend_hosts_cover_bytes_server_side_covers [INFERRED 0.85]
- **Headless browser infrastructure (sidecar, on-demand, home deployment)** — docs_adr_0005_on_demand_browser_headless_shell, docs_adr_0005_on_demand_browser_cdp, docs_adr_0005_on_demand_browser_on_demand_start, docs_adr_0006_browser_on_the_home_machine_home_machine_rationale [INFERRED 0.85] - **Headless browser infrastructure (sidecar, on-demand, home deployment)** — docs_adr_0005_on_demand_browser_headless_shell, docs_adr_0005_on_demand_browser_cdp, docs_adr_0005_on_demand_browser_on_demand_start, docs_adr_0006_browser_on_the_home_machine_home_machine_rationale [INFERRED 0.85]
- **lightnovelworld series-identity investigation and fix** — docs_research_lightnovelworld_chapter_vs_series_slug_issue_77, docs_research_lightnovelworld_chapter_vs_series_slug_unscoped_regex, docs_adr_0008_series_identity_is_discovered_not_derived_discovered_identity [INFERRED 0.85] - **lightnovelworld series-identity investigation and fix** — docs_research_lightnovelworld_chapter_vs_series_slug_issue_77, docs_research_lightnovelworld_chapter_vs_series_slug_unscoped_regex, docs_adr_0008_series_identity_is_discovered_not_derived_discovered_identity [INFERRED 0.85]
## Communities (262 total, 195 thin omitted) ## Communities (233 total, 170 thin omitted)
### Community 0 - "HTMX Library Internals" ### Community 0 - "HTMX Library Internals"
Cohesion: 0.08 Cohesion: 0.08
@@ -327,47 +296,47 @@ Nodes (84): floatPtr(), testConfig(), getCover(), Cookie, Handler, ResponseRecor
### Community 2 - "Manga Userscript Adapters" ### Community 2 - "Manga Userscript Adapters"
Cohesion: 0.06 Cohesion: 0.06
Nodes (77): adapterFor(), anchorsFromDocument(), anchorsFromHTML(), apiDelete(), apiGet(), apiPut(), applyFabPos(), applyLatestChapterIfChanged() (+69 more) Nodes (76): adapterFor(), anchorsFromDocument(), anchorsFromHTML(), apiDelete(), apiGet(), apiPut(), applyFabPos(), applyLatestChapterIfChanged() (+68 more)
### Community 3 - "Novel Userscript Adapters" ### Community 3 - "Novel Userscript Adapters"
Cohesion: 0.06 Cohesion: 0.06
Nodes (78): adapterFor(), anchorsFromDocument(), anchorsFromHTML(), apiDelete(), apiGet(), apiPut(), applyFabPos(), applyLatestChapterIfChanged() (+70 more) Nodes (79): adapterFor(), anchorsFromDocument(), anchorsFromHTML(), apiDelete(), apiGet(), apiPut(), applyFabPos(), applyLatestChapterIfChanged() (+71 more)
### Community 4 - "Series Acquisition Tests" ### Community 4 - "Series Acquisition Tests"
Cohesion: 0.09 Cohesion: 0.10
Nodes (68): bookmarkNewKaganeSeries(), bookmarkNewNovelfullSeries(), bookmarkNewSeries(), Context, Store, T, newAcquirer(), readBookmark() (+60 more) Nodes (67): bookmarkNewKaganeSeries(), bookmarkNewNovelfullSeries(), bookmarkNewSeries(), Context, Store, T, newAcquirer(), readBookmark() (+59 more)
### Community 5 - "Bookmarks API Tests" ### Community 5 - "Bookmarks API Tests"
Cohesion: 0.08 Cohesion: 0.08
Nodes (67): auth(), getBookmarks(), Handler, Request, Store, T, newTestServer(), newTestStore() (+59 more) Nodes (67): auth(), getBookmarks(), Handler, Request, Store, T, newTestServer(), newTestStore() (+59 more)
### Community 7 - "Cover & Acquire Internals" ### Community 7 - "Cover & Acquire Internals"
Cohesion: 0.10 Cohesion: 0.09
Nodes (30): Addr, Context, Store, defaultCoverResolver(), fetchCoverBytes(), Client, Context, NewCoverFetcher() (+22 more) Nodes (31): Addr, Context, Store, defaultCoverResolver(), fetchCoverBytes(), Client, Context, NewCoverFetcher() (+23 more)
### Community 8 - "System Architecture Concepts" ### Community 8 - "System Architecture Concepts"
Cohesion: 0.10 Cohesion: 0.10
Nodes (26): Confirm-Gated Destructive Actions, Discord OAuth & Guild-Membership Gate, Lifecycle Buckets (reading/archived/finished), Reader-Owned Store, HMAC-Derived Reader Credentials, Web Package (Browser UI + Templates), app.html — App Shell Template, Manga/Novel Library Switch (+18 more) Nodes (26): Confirm-Gated Destructive Actions, Discord OAuth & Guild-Membership Gate, Lifecycle Buckets (reading/archived/finished), Reader-Owned Store, HMAC-Derived Reader Credentials, Web Package (Browser UI + Templates), app.html — App Shell Template, Manga/Novel Library Switch (+18 more)
### Community 9 - "Session Middleware" ### Community 9 - "Session Middleware"
Cohesion: 0.16 Cohesion: 0.07
Nodes (20): ClearCookie(), ClientIP(), Duration, Mutex, Request, ResponseWriter, Time, isHTTPS() (+12 more) Nodes (35): ClearCookie(), ClientIP(), Duration, Mutex, Request, ResponseWriter, Time, isHTTPS() (+27 more)
### Community 10 - "Go Test Helpers" ### Community 10 - "Go Test Helpers"
Cohesion: 0.05 Cohesion: 0.05
Nodes (39): Test Helpers, Test Timeout, Basic Handler Test, HTTP Handler Testing, Query Parameters and Headers, Docker Compose Fixture, Integration Testing, SQL Schema Fixture (+31 more) Nodes (39): Test Helpers, Test Timeout, Basic Handler Test, HTTP Handler Testing, Query Parameters and Headers, Docker Compose Fixture, Integration Testing, SQL Schema Fixture (+31 more)
### Community 11 - "Store Tests" ### Community 11 - "Store Tests"
Cohesion: 0.15 Cohesion: 0.07
Nodes (43): Store, T, newTestStore(), readLatestCheckedAt(), readSeries(), secondReader(), seedForCheck(), seedSecondReader() (+35 more) Nodes (79): M, TestMain(), M, TestMain(), M, Main(), start(), URL() (+71 more)
### Community 12 - "Bookmarks API Handler" ### Community 12 - "Bookmarks API Handler"
Cohesion: 0.08 Cohesion: 0.08
Nodes (32): Handler, Request, ResponseWriter, Store, Healthz(), writeJSON(), Auth(), compressible() (+24 more) Nodes (33): Handler, Request, ResponseWriter, Store, Healthz(), writeJSON(), Auth(), compressible() (+25 more)
### Community 13 - "Web UI Handlers" ### Community 13 - "Web UI Handlers"
Cohesion: 0.07 Cohesion: 0.06
Nodes (33): Context, Mutex, Request, ResponseWriter, Time, Handler, newOAuthStates(), T (+25 more) Nodes (24): coverRelativePath(), coverSourceAddress(), displayChapter(), Store, currentLib(), currentTab(), filterBookmarks(), Client (+16 more)
### Community 14 - "Go Error Handling" ### Community 14 - "Go Error Handling"
Cohesion: 0.06 Cohesion: 0.06
@@ -377,18 +346,10 @@ Nodes (33): Creating Errors, Custom Error Types, Custom types that wrap other er
Cohesion: 0.08 Cohesion: 0.08
Nodes (31): awaitPromise(), browserConnectionLost(), classifyBrowserError(), Action, Context, Mutex, jsString(), kaganeAPIURL() (+23 more) Nodes (31): awaitPromise(), browserConnectionLost(), classifyBrowserError(), Action, Context, Mutex, jsString(), kaganeAPIURL() (+23 more)
### Community 16 - "Store"
Cohesion: 0.12
Nodes (4): CoverContentType(), coverRelativePath(), coverSourceAddress(), Store
### Community 17 - "Go Code Style Guide" ### Community 17 - "Go Code Style Guide"
Cohesion: 0.08 Cohesion: 0.08
Nodes (23): Code Style Details, Extract Complex Conditions, Value vs Pointer Arguments, Code Organization Within Files, Complex Conditions & Init Scope, Composite Literals, Control Flow, Cross-References (+15 more) Nodes (23): Code Style Details, Extract Complex Conditions, Value vs Pointer Arguments, Code Organization Within Files, Complex Conditions & Init Scope, Composite Literals, Control Flow, Cross-References (+15 more)
### Community 19 - "Open"
Cohesion: 0.17
Nodes (19): applyMigration(), migrate(), Open(), refreshOwnerToken(), scanSeries(), seedOwner(), TestCoverIsContentAddressedOnFilesystem(), TestCoverPersistsAcrossReopen() (+11 more)
### Community 20 - "I/O Performance Patterns" ### Community 20 - "I/O Performance Patterns"
Cohesion: 0.11 Cohesion: 0.11
Nodes (18): Avoid io.ReadAll for large payloads, Batch Operations, Buffered I/O, Cgo Overhead, Channel: batch processing from a stream, Concurrent Multi-Stage Pipelines, Connection pooling, Database: batch inserts over row-by-row (+10 more) Nodes (18): Avoid io.ReadAll for large payloads, Batch Operations, Buffered I/O, Cgo Overhead, Channel: batch processing from a stream, Concurrent Multi-Stage Pipelines, Connection pooling, Database: batch inserts over row-by-row (+10 more)
@@ -429,10 +390,6 @@ Nodes (14): Allocation Patterns, Backing Array Leaks, Direct indexing vs append,
Cohesion: 0.22 Cohesion: 0.22
Nodes (9): Alerting rules (examples), CPU saturation, GC pressure, Goroutine leaks, Grafana Dashboards, Memory leaks, Prometheus Metrics for Go, PromQL Queries for Performance Diagnosis (+1 more) Nodes (9): Alerting rules (examples), CPU saturation, GC pressure, Goroutine leaks, Grafana Dashboards, Memory leaks, Prometheus Metrics for Go, PromQL Queries for Performance Diagnosis (+1 more)
### Community 30 - "PG Test Infrastructure"
Cohesion: 0.18
Nodes (12): M, TestMain(), M, TestMain(), M, Main(), start(), URL() (+4 more)
### Community 31 - "Memory Layout" ### Community 31 - "Memory Layout"
Cohesion: 0.40 Cohesion: 0.40
Nodes (5): Map of pointers for large, frequently updated structs, Memory Layout, Pointer receivers for large structs, Struct field alignment, Zero-size field at end of struct Nodes (5): Map of pointers for large, frequently updated structs, Memory Layout, Pointer receivers for large structs, Struct field alignment, Zero-size field at end of struct
@@ -465,10 +422,6 @@ Nodes (6): Design Health Score, Design Specificity Verdict, Minor Observations,
Cohesion: 0.29 Cohesion: 0.29
Nodes (6): Design Health Score, Design Specificity Verdict, Minor Observations, Persona Red Flags, Priority Issues, Questions to Consider Nodes (6): Design Health Score, Design Specificity Verdict, Minor Observations, Persona Red Flags, Priority Issues, Questions to Consider
### Community 42 - "Memory Layout Patterns"
Cohesion: 0.48
Nodes (6): T, TestCreateAndGetSession(), TestDeleteSessionIsPerReader(), TestDeleteSessionRevokes(), TestExpiredSessionIsGone(), TestGetSessionUnknownID()
### Community 45 - "Go Perf Alert Rules" ### Community 45 - "Go Perf Alert Rules"
Cohesion: 0.50 Cohesion: 0.50
Nodes (4): Prometheus Alerting Rules (Go Performance), GoroutineLeak Alert, HighGCPauseTime Alert, MemoryNearLimit Alert Nodes (4): Prometheus Alerting Rules (Go Performance), GoroutineLeak Alert, HighGCPauseTime Alert, MemoryNearLimit Alert
@@ -490,12 +443,12 @@ Cohesion: 1.00
Nodes (3): Mirrored Double Bookmark Mark, Ember Flame Accent, BookmarkManager Logo Nodes (3): Mirrored Double Bookmark Mark, Ember Flame Accent, BookmarkManager Logo
### Community 103 - "bookmark-api Service" ### Community 103 - "bookmark-api Service"
Cohesion: 0.18 Cohesion: 0.24
Nodes (16): Backend Go Service (stdlib net/http), Browser Sidecar (BROWSER_WS_URL), Store Package (Postgres Persistence), Browser Sidecar Service, CDP Endpoint (Tailnet-Bound :9222), Persistent Chrome Profile Volume, chrome/docker-compose.yml — Browser Deployable Unit, bookmark-api Service (+8 more) Nodes (10): Backend Go Service (stdlib net/http), Browser Sidecar (BROWSER_WS_URL), Browser Sidecar Service, CDP Endpoint (Tailnet-Bound :9222), Persistent Chrome Profile Volume, chrome/docker-compose.yml — Browser Deployable Unit, bookmark-api Prod Override, CDP Never on Shared Proxy Network (+2 more)
### Community 104 - "AGENTS.md" ### Community 104 - "AGENTS.md"
Cohesion: 0.12 Cohesion: 0.12
Nodes (15): Agent skills, AGENTS.md, Architecture, Commands, Comments, Design system, Domain docs, Forge: Gitea, not GitHub (+7 more) Nodes (14): Agent skills, Architecture, Commands, Comments, Design system, Domain docs, Forge: Gitea, not GitHub, graphify (+6 more)
### Community 105 - "reviewer.md" ### Community 105 - "reviewer.md"
Cohesion: 0.12 Cohesion: 0.12
@@ -582,19 +535,19 @@ Cohesion: 0.50
Nodes (3): Live URL shapes (verified 2026-07-26, may drift — re-check against live pages before trust), Second script: `novel-bookmark.user.js`, Userscript structure (single IIFE, `manga-bookmark.user.js`) Nodes (3): Live URL shapes (verified 2026-07-26, may drift — re-check against live pages before trust), Second script: `novel-bookmark.user.js`, Userscript structure (single IIFE, `manga-bookmark.user.js`)
## Knowledge Gaps ## Knowledge Gaps
- **530 isolated node(s):** `bookmarkmanager/backend`, `ctxKey`, `loginView`, `ctxKey`, `test` (+525 more) - **508 isolated node(s):** `bookmarkmanager/backend`, `ctxKey`, `loginView`, `ctxKey`, `test` (+503 more)
These have ≤1 connection - possible missing edges or undocumented components. These have ≤1 connection - possible missing edges or undocumented components.
- **195 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes. - **170 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions ## Suggested Questions
_Questions this graph is uniquely positioned to answer:_ _Questions this graph is uniquely positioned to answer:_
- **Why does `New()` connect `Series Acquisition Tests` to `Bookmarks API Tests`, `Cover & Acquire Internals`, `Session Middleware`, `Web UI Handlers`, `Open`?** - **Why does `New()` connect `Series Acquisition Tests` to `Bookmarks API Tests`, `Cover & Acquire Internals`, `Session Middleware`, `Store Tests`, `Web UI Handlers`?**
_High betweenness centrality (0.047) - this node is a cross-community bridge._ _High betweenness centrality (0.052) - this node is a cross-community bridge._
- **Why does `Open()` connect `Open` to `Cover Fetch Test Helpers`, `Series Acquisition Tests`, `Bookmarks API Tests`, `Store Tests`, `Store`, `PG Test Infrastructure`?** - **Why does `Open()` connect `Store Tests` to `Cover Fetch Test Helpers`, `Web UI Handlers`, `Series Acquisition Tests`, `Bookmarks API Tests`?**
_High betweenness centrality (0.032) - this node is a cross-community bridge._ _High betweenness centrality (0.034) - this node is a cross-community bridge._
- **Why does `newRouter()` connect `Bookmarks API Tests` to `Cover Fetch Test Helpers`, `Bookmarks API Handler`, `Series Acquisition Tests`?** - **Why does `newRouter()` connect `Bookmarks API Tests` to `Cover Fetch Test Helpers`, `Bookmarks API Handler`, `Series Acquisition Tests`?**
_High betweenness centrality (0.025) - this node is a cross-community bridge._ _High betweenness centrality (0.027) - this node is a cross-community bridge._
- **Are the 47 inferred relationships involving `testConfig()` (e.g. with `TestListRendersAcquiredCover()` and `TestPublicCoverNeverEchoesNonImage()`) actually correct?** - **Are the 47 inferred relationships involving `testConfig()` (e.g. with `TestListRendersAcquiredCover()` and `TestPublicCoverNeverEchoesNonImage()`) actually correct?**
_`testConfig()` has 47 INFERRED edges - model-reasoned connections that need verification._ _`testConfig()` has 47 INFERRED edges - model-reasoned connections that need verification._
- **Are the 8 inferred relationships involving `newWebTestServer()` (e.g. with `TestListRendersAcquiredCover()` and `TestPublicCoverRejectsUnknownAddress()`) actually correct?** - **Are the 8 inferred relationships involving `newWebTestServer()` (e.g. with `TestListRendersAcquiredCover()` and `TestPublicCoverRejectsUnknownAddress()`) actually correct?**
@@ -602,4 +555,4 @@ _Questions this graph is uniquely positioned to answer:_
- **Are the 6 inferred relationships involving `newTestStore()` (e.g. with `TestCreateAndGetSession()` and `TestDeleteSessionIsPerReader()`) actually correct?** - **Are the 6 inferred relationships involving `newTestStore()` (e.g. with `TestCreateAndGetSession()` and `TestDeleteSessionIsPerReader()`) actually correct?**
_`newTestStore()` has 6 INFERRED edges - model-reasoned connections that need verification._ _`newTestStore()` has 6 INFERRED edges - model-reasoned connections that need verification._
- **What connects `bookmarkmanager/backend`, `ctxKey`, `loginView` to the rest of the system?** - **What connects `bookmarkmanager/backend`, `ctxKey`, `loginView` to the rest of the system?**
_572 weakly-connected nodes found - possible documentation gaps or missing edges._ _548 weakly-connected nodes found - possible documentation gaps or missing edges._
File diff suppressed because one or more lines are too long
+1266 -1837
View File
File diff suppressed because it is too large Load Diff
+22 -22
View File
@@ -35,9 +35,9 @@
"semantic_hash": "4a94ac85bad6bce330d085bcc0ae3ffd" "semantic_hash": "4a94ac85bad6bce330d085bcc0ae3ffd"
}, },
"userscript/manga-bookmark.user.js": { "userscript/manga-bookmark.user.js": {
"mtime": 1786363889.5678573, "mtime": 1786488438.9080842,
"ast_hash": "de4abcf9e0f099fb82b630d443db739f", "ast_hash": "1f8bcddd3632d709f058a8401af8f127",
"semantic_hash": "de4abcf9e0f099fb82b630d443db739f" "semantic_hash": ""
}, },
".agents/skills/find-skills/SKILL.md": { ".agents/skills/find-skills/SKILL.md": {
"mtime": 1784884338.760326, "mtime": 1784884338.760326,
@@ -140,19 +140,19 @@
"semantic_hash": "3a08979e4603aae5c32a58d5b6c39765" "semantic_hash": "3a08979e4603aae5c32a58d5b6c39765"
}, },
"CLAUDE.md": { "CLAUDE.md": {
"mtime": 1786446007.0443642, "mtime": 1786488493.5922732,
"ast_hash": "8f79ab45cb9c6466da7c4b65f0bceadf", "ast_hash": "7362a25f37333a2a6a55a5aeccf9b0cb",
"semantic_hash": "" "semantic_hash": ""
}, },
"DEPLOY.md": { "DEPLOY.md": {
"mtime": 1786363889.552731, "mtime": 1786488464.5532806,
"ast_hash": "7fa7bb75239f8ad3798e44c12dce374c", "ast_hash": "2b7b537aa1c0954400c19acc4b634029",
"semantic_hash": "7fa7bb75239f8ad3798e44c12dce374c" "semantic_hash": ""
}, },
"README.md": { "README.md": {
"mtime": 1786363889.552731, "mtime": 1786488487.8305292,
"ast_hash": "bb33ecb93e23c8f039049aec75217263", "ast_hash": "9d6be8aa8a2946c23ad48d8f2864b5ca",
"semantic_hash": "bb33ecb93e23c8f039049aec75217263" "semantic_hash": ""
}, },
"docker-compose.prod.yml": { "docker-compose.prod.yml": {
"mtime": 1786292465.8305523, "mtime": 1786292465.8305523,
@@ -160,9 +160,9 @@
"semantic_hash": "0751998a532297b8ac507a01ec48dc31" "semantic_hash": "0751998a532297b8ac507a01ec48dc31"
}, },
"docker-compose.yml": { "docker-compose.yml": {
"mtime": 1786363889.5678573, "mtime": 1786488450.3508112,
"ast_hash": "d9a58147a573517afa4603b41cf55ca8", "ast_hash": "124fd581bf0a662ff15012abfdb40a92",
"semantic_hash": "d9a58147a573517afa4603b41cf55ca8" "semantic_hash": ""
}, },
".claude/settings.json": { ".claude/settings.json": {
"mtime": 1784951973.1869545, "mtime": 1784951973.1869545,
@@ -200,14 +200,14 @@
"semantic_hash": "e69a8340a371579ca3ea689660f7d7bd" "semantic_hash": "e69a8340a371579ca3ea689660f7d7bd"
}, },
"AGENTS.md": { "AGENTS.md": {
"mtime": 1786446007.0443642, "mtime": 1786488493.5922732,
"ast_hash": "8f79ab45cb9c6466da7c4b65f0bceadf", "ast_hash": "7362a25f37333a2a6a55a5aeccf9b0cb",
"semantic_hash": "" "semantic_hash": ""
}, },
"userscript/test/logic.test.js": { "userscript/test/logic.test.js": {
"mtime": 1786363889.5716388, "mtime": 1786488563.581765,
"ast_hash": "03b5c6c93786c55ac771fd79fd4a75bb", "ast_hash": "80d512bfe6fa8b847f3ba6169c321a74",
"semantic_hash": "03b5c6c93786c55ac771fd79fd4a75bb" "semantic_hash": ""
}, },
".claude/skills/testing-the-userscript/SKILL.md": { ".claude/skills/testing-the-userscript/SKILL.md": {
"mtime": 1786363889.5489495, "mtime": 1786363889.5489495,
@@ -225,9 +225,9 @@
"semantic_hash": "421cd7e57f02d4b467f120ca6ddd7b6a" "semantic_hash": "421cd7e57f02d4b467f120ca6ddd7b6a"
}, },
"backend/api_test.go": { "backend/api_test.go": {
"mtime": 1786363889.552731, "mtime": 1786488480.637822,
"ast_hash": "3fbe8423060efbc34517901dce44e9b5", "ast_hash": "8e4b9293bc2e45ee3f42027315594fd5",
"semantic_hash": "3fbe8423060efbc34517901dce44e9b5" "semantic_hash": ""
}, },
"backend/cover_test.go": { "backend/cover_test.go": {
"mtime": 1786363889.552731, "mtime": 1786363889.552731,
+4 -7
View File
@@ -6,7 +6,6 @@
// @author you // @author you
// @downloadURL https://bookmark-api.violetcrown.my.id/u/__API_TOKEN__/manga-bookmark.user.js // @downloadURL https://bookmark-api.violetcrown.my.id/u/__API_TOKEN__/manga-bookmark.user.js
// @updateURL https://bookmark-api.violetcrown.my.id/u/__API_TOKEN__/manga-bookmark.user.js // @updateURL https://bookmark-api.violetcrown.my.id/u/__API_TOKEN__/manga-bookmark.user.js
// @match https://asuracomic.net/*
// @match https://asurascans.com/* // @match https://asurascans.com/*
// @match https://demonicscans.org/* // @match https://demonicscans.org/*
// @match https://comix.to/* // @match https://comix.to/*
@@ -112,12 +111,10 @@
const asura = { const asura = {
site: "asura", site: "asura",
// asuracomic.net deep links 301 to the asurascans.com *root*, dropping the // asuracomic.net is not matched: its deep links 301 to the asurascans.com
// path, and that happens at the edge before this script gets a document — // *root* at the edge, discarding the path, so this script never sees a
// so those URLs cannot be handled here at all (checked 2026-07-25). It stays // series document there (re-checked 2026-07-25).
// matched in case the redirect starts preserving paths again; until then, matches: (loc) => /(^|\.)asurascans\.com$/.test(loc.hostname),
// reach series through asurascans.com.
matches: (loc) => /(^|\.)asurascans\.com$|(^|\.)asuracomic\.net$/.test(loc.hostname),
detect(loc) { detect(loc) {
const path = loc.pathname; const path = loc.pathname;
// /comics/<slug-hash>/chapter/<n> // /comics/<slug-hash>/chapter/<n>
+8
View File
@@ -120,6 +120,14 @@ test("asura.detect returns other for non-series paths", () => {
assert.equal(asura.detect(loc("https://asurascans.com/bookmarks")).type, "other"); assert.equal(asura.detect(loc("https://asurascans.com/bookmarks")).type, "other");
}); });
test("asura.matches accepts only asurascans.com", () => {
assert.equal(asura.matches({ hostname: "asurascans.com" }), true);
assert.equal(asura.matches({ hostname: "www.asurascans.com" }), true);
// Dead domain: deep links 301 to the asurascans.com root, discarding the path.
assert.equal(asura.matches({ hostname: "asuracomic.net" }), false);
assert.equal(asura.matches({ hostname: "asurascans.com.evil.example" }), false);
});
test("asura.latestChapterFromAnchors takes the highest and skips the First Chapter shortcut", () => { test("asura.latestChapterFromAnchors takes the highest and skips the First Chapter shortcut", () => {
const best = asura.latestChapterFromAnchors([ const best = asura.latestChapterFromAnchors([
{ href: "/comics/solo-leveling-059befe1/chapter/1", text: "Chapter 1" }, { href: "/comics/solo-leveling-059befe1/chapter/1", text: "Chapter 1" },