fix: an asleep browser Lane is not a stalled one on the admin page
laneRow.Stalled was Due > 0 && Checked == 0, which is exactly the shape of the on-demand wake gate declining to start Chrome (ADR-0005): under five due Series and none overdue by 15m, a browser Lane reads nothing on purpose. kagane and novelfull each have one bookmarked Series, so that is their state for most of every cycle and the page marked both "not checking" — spending the one mark that means "go look" on the commonest healthy state. LaneState.Asleep is set at the wake gate, excluded from Stalled, kept out of Attention, and rendered as "browser asleep". Verified in prod: those Lanes wake on the age rule 15m past due (kagane and novelfull both polled at 14:55Z for a 14:40Z due moment, novelfull picking up Chapter 4458).
This commit is contained in:
@@ -266,3 +266,9 @@ Guidance for OpenCode (and Claude Code) working under `backend/`. See root `AGEN
|
||||
than recording zeroes; a Lane that has never reached a pace renders no gap at
|
||||
all. `Checked` next to `Due` is what separates a stopped Lane from a quiet
|
||||
one, so neither figure may be dropped from the row.
|
||||
Due-without-Checked is *not* by itself a stall: a browser Lane under both
|
||||
wake thresholds sets `LaneState.Asleep` at the on-demand gate and renders
|
||||
"browser asleep" instead of "not checking", and never counts toward
|
||||
`Attention`. That is the commonest healthy state for kagane, comix and
|
||||
novelfull — one due Series, nothing checked — so spending the stall mark on
|
||||
it would train the owner to ignore the mark that matters.
|
||||
|
||||
@@ -263,7 +263,7 @@ func (p *Poller) runLanePass(ctx context.Context, name string, paced bool) time.
|
||||
// browser): waking it for a single Poll would cost a challenge solve
|
||||
// per request. The Lane still paces at the default gap, which is what
|
||||
// the owner's page must show rather than a zero.
|
||||
st.Gap = defaultGap
|
||||
st.Gap, st.Asleep = defaultGap, true
|
||||
return defaultGap
|
||||
}
|
||||
if s.Browser != nil {
|
||||
|
||||
@@ -1388,6 +1388,11 @@ func TestBrowserLaneWakeThresholds(t *testing.T) {
|
||||
if got := browser.callCount(); got != 0 {
|
||||
t.Fatalf("browser fetches with 3 freshly-due series = %d, want 0 (Chrome stays asleep)", got)
|
||||
}
|
||||
// The owner's page reads this state off the snapshot, and Due-without-
|
||||
// Checked has to be distinguishable there from a Lane that has stopped.
|
||||
if lane := laneByName(t, p, "kagane"); !lane.Asleep || lane.Due != 3 || lane.Checked != 0 {
|
||||
t.Fatalf("asleep kagane lane = %+v, want Asleep with 3 due and 0 checked", lane)
|
||||
}
|
||||
// 5 due crosses the count threshold.
|
||||
for i := 3; i < 5; i++ {
|
||||
seed(i)
|
||||
@@ -1396,6 +1401,9 @@ func TestBrowserLaneWakeThresholds(t *testing.T) {
|
||||
if got := browser.callCount(); got != 5 {
|
||||
t.Fatalf("browser fetches with 5 due series = %d, want 5", got)
|
||||
}
|
||||
if lane := laneByName(t, p, "kagane"); lane.Asleep {
|
||||
t.Fatalf("woken kagane lane still reports Asleep: %+v", lane)
|
||||
}
|
||||
// A single long-neglected series wakes the browser by age alone.
|
||||
seedForCheck(t, s, "kagane:ancient", "https://kagane.to/series/ancient", 0)
|
||||
p.runOnce(context.Background())
|
||||
@@ -1404,6 +1412,19 @@ func TestBrowserLaneWakeThresholds(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// laneByName pulls one Lane out of the poller's snapshot, failing rather than
|
||||
// returning a zero LaneState a caller would assert against by accident.
|
||||
func laneByName(t *testing.T, p *Poller, site string) LaneState {
|
||||
t.Helper()
|
||||
for _, lane := range p.LaneStatus().Lanes {
|
||||
if lane.Site == site {
|
||||
return lane
|
||||
}
|
||||
}
|
||||
t.Fatalf("no %q lane in the snapshot", site)
|
||||
return LaneState{}
|
||||
}
|
||||
|
||||
// When one browser Lane loses the sidecar, the round's remaining browser
|
||||
// Lanes are skipped: every fetch would fail anyway, and their Series must not
|
||||
// burn their stamps on a dead Chrome (issue #100).
|
||||
|
||||
@@ -19,6 +19,11 @@ type LaneState struct {
|
||||
Clamped bool
|
||||
Refusing bool
|
||||
Browser bool
|
||||
// Asleep marks a browser Lane whose last pass declined to wake Chrome
|
||||
// because it was under both wake thresholds (ADR-0005). Due without
|
||||
// Checked then means "waiting for the group to gather", not "stopped", and
|
||||
// the page must not draw it as a stall.
|
||||
Asleep bool
|
||||
}
|
||||
|
||||
// Status is the owner's page snapshot of the whole poller (issue #102).
|
||||
|
||||
@@ -61,8 +61,11 @@ type laneRow struct {
|
||||
// is configured, which stops those Series just as completely.
|
||||
BrowserLost bool
|
||||
// Stalled marks a Lane with Series waiting that its last pass did not read
|
||||
// — the difference between a stopped Lane and a quiet one (story 13).
|
||||
// — the difference between a stopped Lane and a quiet one (story 13). A
|
||||
// browser Lane holding Chrome asleep under the wake thresholds is neither,
|
||||
// so it carries Asleep instead and never Stalled.
|
||||
Stalled bool
|
||||
Asleep bool
|
||||
// Attention is the one flag the template colours on, so an unhealthy Lane
|
||||
// is found at a glance rather than read for.
|
||||
Attention bool
|
||||
@@ -152,8 +155,9 @@ func (h *Handler) lanesView() lanesView {
|
||||
for _, l := range snap.Lanes {
|
||||
lost := l.Browser && !snap.BrowserReachable
|
||||
// Series waiting and none read is the shape of a Lane that has stopped
|
||||
// working, as distinct from one that is quiet for want of work.
|
||||
stalled := l.Due > 0 && l.Checked == 0
|
||||
// working, as distinct from one that is quiet for want of work — or one
|
||||
// deliberately leaving Chrome asleep until its group gathers.
|
||||
stalled := l.Due > 0 && l.Checked == 0 && !l.Asleep
|
||||
gap := ""
|
||||
if l.Gap > 0 {
|
||||
gap = l.Gap.Truncate(time.Second).String()
|
||||
@@ -168,6 +172,7 @@ func (h *Handler) lanesView() lanesView {
|
||||
Refusing: l.Refusing,
|
||||
BrowserLost: lost,
|
||||
Stalled: stalled,
|
||||
Asleep: l.Asleep,
|
||||
Attention: l.Clamped || l.Refusing || lost || stalled,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
{{if .Refusing}}<span class="lane-mark">refusing</span>{{end}}
|
||||
{{if .BrowserLost}}<span class="lane-mark">no browser</span>{{end}}
|
||||
{{if .Stalled}}<span class="lane-mark">not checking</span>{{end}}
|
||||
{{if .Asleep}}<span class="lane-mark">browser asleep</span>{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
@@ -776,6 +776,37 @@ func TestAdminPageShowsLaneStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A browser Lane under both wake thresholds holds Chrome asleep (ADR-0005), so
|
||||
// Series due with none checked is the design working, not a stopped Lane. The
|
||||
// two must not render the same mark: "not checking" is the owner's cue to go
|
||||
// looking, and spending it on the commonest healthy browser-Lane state trains
|
||||
// them to ignore it.
|
||||
func TestAsleepBrowserLaneIsNotMarkedStalled(t *testing.T) {
|
||||
lanes := fakeLanes{latest.Status{
|
||||
Lanes: []latest.LaneState{
|
||||
{Site: "kagane", Due: 1, LastRun: time.Now(), Gap: 10 * time.Second, Browser: true, Asleep: true},
|
||||
},
|
||||
BrowserConfigured: true,
|
||||
BrowserReachable: true,
|
||||
}}
|
||||
router, st, _ := oauthWebTestServer(t, lanes)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/ui/admin/lanes", nil)
|
||||
req.AddCookie(sessionCookie(t, st))
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
body := rr.Body.String()
|
||||
if strings.Contains(body, "not checking") {
|
||||
t.Errorf("an asleep browser Lane is marked as stalled:\n%s", body)
|
||||
}
|
||||
if !strings.Contains(body, "browser asleep") {
|
||||
t.Errorf("an asleep browser Lane says nothing about why it read nothing:\n%s", body)
|
||||
}
|
||||
if strings.Contains(body, `class="attention"`) {
|
||||
t.Errorf("an asleep browser Lane is coloured as unhealthy:\n%s", body)
|
||||
}
|
||||
}
|
||||
|
||||
// A Lane whose pass never reached a figure must not have that figure drawn as
|
||||
// a zero: a refusing Lane still reports the due count and gap its last real
|
||||
// pass saw, and a Lane that has never reached one omits it entirely.
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
"10": "Go Test Helpers",
|
||||
"11": "Store Tests",
|
||||
"12": "Bookmarks API Handler",
|
||||
"13": "Web UI Handlers",
|
||||
"13": "displayChapter",
|
||||
"14": "Go Error Handling",
|
||||
"15": "CDP Browser Client",
|
||||
"16": "Cloudflare bot scoring and poll cadence — what is actually documented",
|
||||
"17": "Go Code Style Guide",
|
||||
"18": "Agent Skills",
|
||||
@@ -30,7 +29,6 @@
|
||||
"28": "Allocation Patterns",
|
||||
"29": "Observability & Alerting",
|
||||
"30": "AGENTS.md",
|
||||
"31": "Store",
|
||||
"32": "Open",
|
||||
"33": "Go Testing Guide",
|
||||
"34": "Session Store",
|
||||
@@ -41,7 +39,6 @@
|
||||
"39": "UI Critique 2026-07-26A",
|
||||
"40": "UI Critique 2026-07-26B",
|
||||
"41": "Handler",
|
||||
"42": "pgtest.go",
|
||||
"43": "Issue Tracker & Triage",
|
||||
"44": "Ticket Workflow",
|
||||
"45": "Go Perf Alert Rules",
|
||||
@@ -124,7 +121,6 @@
|
||||
"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",
|
||||
"124": "ADR-0005: On-demand browser sidecar",
|
||||
"125": "sessions_test.go",
|
||||
"126": "Bookmark Manager",
|
||||
"127": "triage-labels.md",
|
||||
"128": "Cross-Ticket Contract",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Graph Report - mangaBookmark (2026-08-16)
|
||||
|
||||
## Corpus Check
|
||||
- 119 files · ~290,953 words
|
||||
- 119 files · ~291,777 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 1736 nodes · 3597 edges · 221 communities (70 shown, 151 thin omitted)
|
||||
- Extraction: 90% EXTRACTED · 10% INFERRED · 0% AMBIGUOUS · INFERRED: 361 edges (avg confidence: 0.78)
|
||||
- 1739 nodes · 3610 edges · 217 communities (67 shown, 150 thin omitted)
|
||||
- Extraction: 90% EXTRACTED · 10% INFERRED · 0% AMBIGUOUS · INFERRED: 363 edges (avg confidence: 0.78)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `56afb9f2`
|
||||
- Built from commit: `20fff588`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
@@ -28,9 +28,8 @@
|
||||
- [[_COMMUNITY_Go Test Helpers|Go Test Helpers]]
|
||||
- [[_COMMUNITY_Store Tests|Store Tests]]
|
||||
- [[_COMMUNITY_Bookmarks API Handler|Bookmarks API Handler]]
|
||||
- [[_COMMUNITY_Web UI Handlers|Web UI Handlers]]
|
||||
- [[_COMMUNITY_displayChapter|displayChapter]]
|
||||
- [[_COMMUNITY_Go Error Handling|Go Error Handling]]
|
||||
- [[_COMMUNITY_CDP Browser Client|CDP Browser Client]]
|
||||
- [[_COMMUNITY_Cloudflare bot scoring and poll cadence — what is actually documented|Cloudflare bot scoring and poll cadence — what is actually documented]]
|
||||
- [[_COMMUNITY_Go Code Style Guide|Go Code Style Guide]]
|
||||
- [[_COMMUNITY_Agent Skills|Agent Skills]]
|
||||
@@ -46,7 +45,6 @@
|
||||
- [[_COMMUNITY_Allocation Patterns|Allocation Patterns]]
|
||||
- [[_COMMUNITY_Observability & Alerting|Observability & Alerting]]
|
||||
- [[_COMMUNITY_AGENTS|AGENTS.md]]
|
||||
- [[_COMMUNITY_Store|Store]]
|
||||
- [[_COMMUNITY_Go Testing Guide|Go Testing Guide]]
|
||||
- [[_COMMUNITY_Session Store|Session Store]]
|
||||
- [[_COMMUNITY_Web UI Filter Logic|Web UI Filter Logic]]
|
||||
@@ -56,7 +54,6 @@
|
||||
- [[_COMMUNITY_UI Critique 2026-07-26A|UI Critique 2026-07-26A]]
|
||||
- [[_COMMUNITY_UI Critique 2026-07-26B|UI Critique 2026-07-26B]]
|
||||
- [[_COMMUNITY_Handler|Handler]]
|
||||
- [[_COMMUNITY_pgtest.go|pgtest.go]]
|
||||
- [[_COMMUNITY_Issue Tracker & Triage|Issue Tracker & Triage]]
|
||||
- [[_COMMUNITY_Ticket Workflow|Ticket Workflow]]
|
||||
- [[_COMMUNITY_Go Perf Alert Rules|Go Perf Alert Rules]]
|
||||
@@ -139,7 +136,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_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_sessions_test.go|sessions_test.go]]
|
||||
- [[_COMMUNITY_Bookmark Manager|Bookmark Manager]]
|
||||
- [[_COMMUNITY_triage-labels|triage-labels.md]]
|
||||
- [[_COMMUNITY_Cross-Ticket Contract|Cross-Ticket Contract]]
|
||||
@@ -270,7 +266,7 @@
|
||||
- **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]
|
||||
|
||||
## Communities (221 total, 151 thin omitted)
|
||||
## Communities (217 total, 150 thin omitted)
|
||||
|
||||
### Community 0 - "HTMX Library Internals"
|
||||
Cohesion: 0.08
|
||||
@@ -278,7 +274,7 @@ Nodes (101): A(), ae(), an(), at(), B(), be(), bn(), bt() (+93 more)
|
||||
|
||||
### Community 1 - "Cover Fetch Test Helpers"
|
||||
Cohesion: 0.09
|
||||
Nodes (89): floatPtr(), testConfig(), getCover(), Cookie, Handler, ResponseRecorder, T, TestListRendersAcquiredCover() (+81 more)
|
||||
Nodes (88): floatPtr(), testConfig(), getCover(), Cookie, Handler, ResponseRecorder, T, TestListRendersAcquiredCover() (+80 more)
|
||||
|
||||
### Community 2 - "Manga Userscript Adapters"
|
||||
Cohesion: 0.06
|
||||
@@ -293,12 +289,12 @@ Cohesion: 0.07
|
||||
Nodes (103): bookmarkNewKaganeSeries(), bookmarkNewNovelfullSeries(), bookmarkNewSeries(), Context, Store, T, newAcquirer(), readBookmark() (+95 more)
|
||||
|
||||
### Community 5 - "Bookmarks API Tests"
|
||||
Cohesion: 0.07
|
||||
Nodes (71): auth(), getBookmarks(), Handler, Request, Store, T, newTestServer(), newTestStore() (+63 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (54): auth(), getBookmarks(), Handler, Request, Store, T, newTestServer(), newTestStore() (+46 more)
|
||||
|
||||
### Community 7 - "Cover & Acquire Internals"
|
||||
Cohesion: 0.06
|
||||
Nodes (43): Addr, fakeLanes, Context, Store, WaitGroup, isInterstitial(), defaultCoverResolver(), fetchCoverBytes() (+35 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (31): fakeLanes, Context, Store, WaitGroup, isInterstitial(), fetchCoverBytes(), browserWakeDue(), fetcherFor() (+23 more)
|
||||
|
||||
### Community 8 - "System Architecture Concepts"
|
||||
Cohesion: 0.15
|
||||
@@ -313,21 +309,21 @@ 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)
|
||||
|
||||
### Community 11 - "Store Tests"
|
||||
Cohesion: 0.14
|
||||
Nodes (45): Store, T, newTestStore(), readLatestCheckedAt(), readSeries(), secondReader(), seedForCheck(), seedSecondReader() (+37 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (81): M, TestMain(), M, TestMain(), M, Main(), start(), URL() (+73 more)
|
||||
|
||||
### Community 12 - "Bookmarks API Handler"
|
||||
Cohesion: 0.08
|
||||
Nodes (32): Handler, Request, ResponseWriter, Store, Healthz(), writeJSON(), Auth(), compressible() (+24 more)
|
||||
|
||||
### Community 13 - "displayChapter"
|
||||
Cohesion: 0.09
|
||||
Nodes (36): Addr, Config, NewBrowserFetcher(), defaultCoverResolver(), Client, Context, NewCoverFetcher(), NewCoverFetcherWithResolver() (+28 more)
|
||||
|
||||
### Community 14 - "Go Error Handling"
|
||||
Cohesion: 0.06
|
||||
Nodes (33): Creating Errors, Custom Error Types, Custom types that wrap other errors, Decision table: which error strategy to use, Error Creation, Error String Conventions, Errors as Values, `errors.New` — static error messages (+25 more)
|
||||
|
||||
### Community 15 - "CDP Browser Client"
|
||||
Cohesion: 0.20
|
||||
Nodes (17): applyMigration(), migrate(), Open(), refreshOwnerToken(), seedOwner(), TestCoverIsContentAddressedOnFilesystem(), TestCoverPersistsAcrossReopen(), TestMigration0002BackfillsExistingBookmarks() (+9 more)
|
||||
|
||||
### Community 16 - "Cloudflare bot scoring and poll cadence — what is actually documented"
|
||||
Cohesion: 0.06
|
||||
Nodes (33): 1.1 The score itself, 1.2 The detection engines (Enterprise Bot Management), 1.3 Rate limiting is a separate product, 1. What a bot score is and what feeds it, 2.1 What each plan gets, 2.2 Bot Fight Mode specifics (the Free-plan product), 2.3 Does the free tier "score" continuously?, 2. The free-plan reality (+25 more)
|
||||
@@ -365,8 +361,8 @@ Cohesion: 0.33
|
||||
Nodes (12): coverResponse(), Request, T, TestCoverFetcherCanonicalisesJpgAlias(), TestCoverFetcherFetchesPublicHTTPSImage(), TestCoverFetcherRefusesUnsafeDestinationsBeforeRequest(), TestCoverFetcherRejectsNonImage(), TestCoverFetcherRejectsOversizedBody() (+4 more)
|
||||
|
||||
### Community 26 - "Open"
|
||||
Cohesion: 0.05
|
||||
Nodes (61): awaitPromise(), browserConnectionLost(), classifyBrowserError(), comixRead(), comixSeriesPageURL(), Action, Context, Mutex (+53 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (57): awaitPromise(), browserConnectionLost(), classifyBrowserError(), comixRead(), comixSeriesPageURL(), Action, Context, Mutex (+49 more)
|
||||
|
||||
### Community 27 - "Find Skills Guide"
|
||||
Cohesion: 0.14
|
||||
@@ -384,10 +380,6 @@ Nodes (9): Alerting rules (examples), CPU saturation, GC pressure, Goroutine lea
|
||||
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
|
||||
|
||||
### Community 31 - "Store"
|
||||
Cohesion: 0.09
|
||||
Nodes (5): coverRelativePath(), coverSourceAddress(), Store, scanSeries(), ReaderSummary
|
||||
|
||||
### Community 33 - "Go Testing Guide"
|
||||
Cohesion: 0.20
|
||||
Nodes (10): CI Regression Detection, Common Mistakes, Core Philosophy, Cross-References, Decision Tree: Where Is Time Spent?, Deep Dives, Go Performance Optimization, Iterative Optimization Methodology (+2 more)
|
||||
@@ -417,12 +409,8 @@ Cohesion: 0.29
|
||||
Nodes (6): Design Health Score, Design Specificity Verdict, Minor Observations, Persona Red Flags, Priority Issues, Questions to Consider
|
||||
|
||||
### Community 41 - "Handler"
|
||||
Cohesion: 0.14
|
||||
Nodes (18): currentLib(), currentTab(), filterBookmarks(), Client, HandlerFunc, Request, ResponseWriter, Store (+10 more)
|
||||
|
||||
### Community 42 - "pgtest.go"
|
||||
Cohesion: 0.18
|
||||
Nodes (12): M, TestMain(), M, TestMain(), M, Main(), start(), URL() (+4 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (25): coverRelativePath(), coverSourceAddress(), displayChapter(), Store, scanSeries(), currentLib(), currentTab(), filterBookmarks() (+17 more)
|
||||
|
||||
### Community 45 - "Go Perf Alert Rules"
|
||||
Cohesion: 0.50
|
||||
@@ -532,10 +520,6 @@ Nodes (3): Consequence, The wire format stays flat and deliberately does not mir
|
||||
Cohesion: 0.50
|
||||
Nodes (3): ADR-0005: On-demand browser sidecar, Constraints, Decision
|
||||
|
||||
### Community 125 - "sessions_test.go"
|
||||
Cohesion: 0.48
|
||||
Nodes (6): T, TestCreateAndGetSession(), TestDeleteSessionIsPerReader(), TestDeleteSessionRevokes(), TestExpiredSessionIsGone(), TestGetSessionUnknownID()
|
||||
|
||||
### Community 154 - "ADR-0011: Sightings — a Reader report defers a Poll where being wrong hurts only them"
|
||||
Cohesion: 0.33
|
||||
Nodes (5): ADR-0011: Sightings — a Reader report defers a Poll where being wrong hurts only them, Constraints preserved, Decision, Tradeoffs and rejections, Why
|
||||
@@ -551,17 +535,17 @@ Nodes (3): Live URL shapes (verified 2026-07-26, may drift — re-check against
|
||||
## Knowledge Gaps
|
||||
- **524 isolated node(s):** `bookmarkmanager/backend`, `ctxKey`, `loginView`, `ctxKey`, `test` (+519 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **151 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
- **150 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `Open()` connect `CDP Browser Client` to `Cover Fetch Test Helpers`, `Series Acquisition Tests`, `Bookmarks API Tests`, `pgtest.go`, `Store Tests`, `Store`?**
|
||||
_High betweenness centrality (0.060) - this node is a cross-community bridge._
|
||||
- **Why does `New()` connect `Series Acquisition Tests` to `Bookmarks API Tests`, `Cover & Acquire Internals`, `Handler`, `Session Middleware`, `CDP Browser Client`, `ResponseWriter`?**
|
||||
_High betweenness centrality (0.050) - 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`, `ResponseWriter`?**
|
||||
_High betweenness centrality (0.030) - this node is a cross-community bridge._
|
||||
- **Why does `Open()` connect `Store Tests` to `Cover Fetch Test Helpers`, `Series Acquisition Tests`, `Bookmarks API Tests`, `Handler`, `displayChapter`?**
|
||||
_High betweenness centrality (0.057) - this node is a cross-community bridge._
|
||||
- **Why does `New()` connect `Series Acquisition Tests` to `Bookmarks API Tests`, `Cover & Acquire Internals`, `Handler`, `Session Middleware`, `Store Tests`, `displayChapter`, `ResponseWriter`?**
|
||||
_High betweenness centrality (0.051) - this node is a cross-community bridge._
|
||||
- **Why does `newRouter()` connect `Bookmarks API Tests` to `Cover Fetch Test Helpers`, `Series Acquisition Tests`, `Bookmarks API Handler`, `displayChapter`, `ResponseWriter`?**
|
||||
_High betweenness centrality (0.031) - this node is a cross-community bridge._
|
||||
- **Are the 25 inferred relationships involving `newTestStore()` (e.g. with `TestAcquireDoesNotBlockTheWrite()` and `TestAcquireFailureLeavesTheBookmarkIntact()`) actually correct?**
|
||||
_`newTestStore()` has 25 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 48 inferred relationships involving `testConfig()` (e.g. with `TestListRendersAcquiredCover()` and `TestPublicCoverNeverEchoesNonImage()`) actually correct?**
|
||||
|
||||
File diff suppressed because one or more lines are too long
+689
-519
File diff suppressed because it is too large
Load Diff
+39
-39
@@ -25,7 +25,7 @@
|
||||
"semantic_hash": "dac242903b0e98c3e4395159d609e08e"
|
||||
},
|
||||
"backend/main.go": {
|
||||
"mtime": 1786863963.3450089,
|
||||
"mtime": 1786886056.023822,
|
||||
"ast_hash": "d5a50b03438d7c120079d40cc578462b",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"semantic_hash": "4a94ac85bad6bce330d085bcc0ae3ffd"
|
||||
},
|
||||
"userscript/manga-bookmark.user.js": {
|
||||
"mtime": 1786872888.3482232,
|
||||
"mtime": 1786886056.0389624,
|
||||
"ast_hash": "4e79684942234b26f4a1fb03fccd6d35",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -170,8 +170,8 @@
|
||||
"semantic_hash": "e51077b6a7f1f67afc748f1a32a1557d"
|
||||
},
|
||||
"backend/web_test.go": {
|
||||
"mtime": 1786865248.7717106,
|
||||
"ast_hash": "7c298e39c63e4502cf6272d1e206e9ef",
|
||||
"mtime": 1786891437.925184,
|
||||
"ast_hash": "74aed7f5a5b196d2b860cd6a34266ec6",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/main_test.go": {
|
||||
@@ -220,22 +220,22 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/design-system.md": {
|
||||
"mtime": 1786865139.9022946,
|
||||
"mtime": 1786886056.0313923,
|
||||
"ast_hash": "3bd39932997c8e245508874194db4e49",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/api_test.go": {
|
||||
"mtime": 1786868080.2317674,
|
||||
"mtime": 1786886056.016252,
|
||||
"ast_hash": "b64372df5429fb14e48b37b3bab10402",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/cover_test.go": {
|
||||
"mtime": 1786863966.7084978,
|
||||
"mtime": 1786886056.016252,
|
||||
"ast_hash": "fd6b3f6ef46bd17b0241d104b6e5bdf6",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/api/handlers.go": {
|
||||
"mtime": 1786867745.788687,
|
||||
"mtime": 1786886056.016252,
|
||||
"ast_hash": "ae4f3b961450f06a06e0632eb273a5b2",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -245,13 +245,13 @@
|
||||
"semantic_hash": "385b36f58488b7e6d93eb6d6034e9ee3"
|
||||
},
|
||||
"backend/internal/latest/browser.go": {
|
||||
"mtime": 1786861012.026504,
|
||||
"ast_hash": "75f34a974568d122681939dd297944a2",
|
||||
"mtime": 1786889854.5578685,
|
||||
"ast_hash": "b65f3693db77c69ef0a6247fd75118bc",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/browser_test.go": {
|
||||
"mtime": 1786857336.6414917,
|
||||
"ast_hash": "800fa6aa471ada054a3c48943ad17ab7",
|
||||
"mtime": 1786889854.5578685,
|
||||
"ast_hash": "118aba3d794b9faf893c45e58298e11a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/fetch.go": {
|
||||
@@ -260,17 +260,17 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/poller.go": {
|
||||
"mtime": 1786872774.641225,
|
||||
"ast_hash": "5e55e2ac5cb7d347807754f92425d29b",
|
||||
"mtime": 1786891405.415882,
|
||||
"ast_hash": "54f6eec9d2090c7713f502c8553063a2",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/poller_test.go": {
|
||||
"mtime": 1786865291.3758123,
|
||||
"ast_hash": "1da669ac748a8d1a0289e557392fa3fd",
|
||||
"mtime": 1786891462.5665002,
|
||||
"ast_hash": "512806a9c6e15036a75f01bf5577c20b",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/sites.go": {
|
||||
"mtime": 1786868771.0663204,
|
||||
"mtime": 1786886056.0200372,
|
||||
"ast_hash": "5786000dd0dfd5b4b3bd2b87b1fc100b",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -340,12 +340,12 @@
|
||||
"semantic_hash": "0b6764a0ee20f5cb7748eecd31a1d220"
|
||||
},
|
||||
"backend/internal/store/store.go": {
|
||||
"mtime": 1786868763.646761,
|
||||
"mtime": 1786886056.0200372,
|
||||
"ast_hash": "34f55617b7409f03f02e925bec92fac0",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/store/store_test.go": {
|
||||
"mtime": 1786867781.301985,
|
||||
"mtime": 1786886056.0200372,
|
||||
"ast_hash": "16f69d97eb63bcec7409294466642411",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -380,12 +380,12 @@
|
||||
"semantic_hash": "19a573773be4ca22570ca2f8543120c5"
|
||||
},
|
||||
"backend/internal/web/web.go": {
|
||||
"mtime": 1786863744.1453943,
|
||||
"mtime": 1786886056.023822,
|
||||
"ast_hash": "b051f2de214c4b253ee09fbbdb8ebb60",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/reader_credential_test.go": {
|
||||
"mtime": 1786863966.7092986,
|
||||
"mtime": 1786886056.023822,
|
||||
"ast_hash": "0c93de387af595901c43b6bdb3bed8cc",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -395,7 +395,7 @@
|
||||
"semantic_hash": "8008a187690764436540fab47ba0cfcc"
|
||||
},
|
||||
"userscript/novel-bookmark.user.js": {
|
||||
"mtime": 1786872902.809149,
|
||||
"mtime": 1786886056.0389624,
|
||||
"ast_hash": "36c81462919719f0e053ca5d633a243a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -425,17 +425,17 @@
|
||||
"semantic_hash": "6c6f3e4c4c2f57867894280bce728c50"
|
||||
},
|
||||
"backend/AGENTS.md": {
|
||||
"mtime": 1786872951.3127444,
|
||||
"ast_hash": "68bc86af9593adad1e37fa05188fe63e",
|
||||
"mtime": 1786891513.6486945,
|
||||
"ast_hash": "b4233ebc5ff905b701ded9427277ad8c",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/CLAUDE.md": {
|
||||
"mtime": 1786872951.3127444,
|
||||
"ast_hash": "68bc86af9593adad1e37fa05188fe63e",
|
||||
"mtime": 1786891513.6486945,
|
||||
"ast_hash": "b4233ebc5ff905b701ded9427277ad8c",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/web/templates/app.html": {
|
||||
"mtime": 1786864445.1584811,
|
||||
"mtime": 1786886056.023822,
|
||||
"ast_hash": "99d3e1139042d0eb61627155dddb3843",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -465,7 +465,7 @@
|
||||
"semantic_hash": "bcc3101498a66cf8b79f9d97c6c9cd6b"
|
||||
},
|
||||
"backend/internal/web/templates/readers.html": {
|
||||
"mtime": 1786864296.770521,
|
||||
"mtime": 1786886056.023822,
|
||||
"ast_hash": "2b1cbb24d6185e48561966db1af04ba4",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -635,42 +635,42 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/status.go": {
|
||||
"mtime": 1786865033.5648637,
|
||||
"ast_hash": "8141514efa5a7a66e77b9a62d4982d52",
|
||||
"mtime": 1786891396.3216066,
|
||||
"ast_hash": "5893c77b117a5bcec3a4583aa51e7f42",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/store/migrations/0010_reader_sightings.sql": {
|
||||
"mtime": 1786863695.6957178,
|
||||
"mtime": 1786886056.0200372,
|
||||
"ast_hash": "a72c08c63fad530df3c793d16edfa385",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/web/admin.go": {
|
||||
"mtime": 1786865087.473256,
|
||||
"ast_hash": "b1c0f23a102a9bc7f84adf306d743f56",
|
||||
"mtime": 1786891411.0618205,
|
||||
"ast_hash": "e02249496b57096cf1929f9cd0e35c3e",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/web/templates/admin.html": {
|
||||
"mtime": 1786865108.4095602,
|
||||
"mtime": 1786886056.023822,
|
||||
"ast_hash": "f04ea1cb01369d53053eac489e796faa",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/web/templates/lanes.html": {
|
||||
"mtime": 1786865113.5362902,
|
||||
"ast_hash": "8b9f9c7a56e0ec3bf950aa70acd95e92",
|
||||
"mtime": 1786891416.6118941,
|
||||
"ast_hash": "0e96f66560bac29713a71ec31139d25e",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/latest/sighting_test.go": {
|
||||
"mtime": 1786868815.9760568,
|
||||
"mtime": 1786886056.0200372,
|
||||
"ast_hash": "3b1372bbeeb538b73f86f57da210b4cc",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"backend/internal/store/migrations/0011_series_sightings.sql": {
|
||||
"mtime": 1786867605.3728056,
|
||||
"mtime": 1786886056.0200372,
|
||||
"ast_hash": "d0ade928a6e951eb179e4d0255cca526",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/adr/0011-sighting-deferral-trust-model.md": {
|
||||
"mtime": 1786872970.473592,
|
||||
"mtime": 1786886056.0313923,
|
||||
"ast_hash": "0b6ca704f62185c9d629320f2d8475b5",
|
||||
"semantic_hash": ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user