With extraction disabled, an item that fails classification is never retried
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 53
- Forks
- 7
- PR merge metrics
- No merged PRs in 30d
Description
When extraction.enabled is false, an item that reaches the database but fails classification is never picked up again: neither startup loop can return it, and the feed update path skips it as already seen. Line references are against e220a28.
Mechanism
processExistingItems is the only catch-up path, and both of its loops exclude these rows:
pkg/scheduler/scheduler.go:335-341: the extraction backlog loop breaks immediately when extraction is disabled, because nothing marks such items extracted andGetItemsNeedingExtractionwould keep returning them.pkg/repository/item.go:171-179: the second loop usesGetUnclassifiedItems, whose SQL requiresextracted_content != ''.- With extraction disabled
extractContentreturns before touching the database, andProcessBatchpasses a nil extraction toUpdateItemProcessed, which takes the classification-only branch (pkg/repository/item.go:285-298). Soextracted_contentstays empty andextracted_atstays NULL for the life of the row. ItemExistsmakes later feed updates skip the GUID, so the live path never revisits it either.
Reproduction
- Run with
extraction.enabled: falseand a working LLM endpoint, and add a feed. - Break the LLM endpoint (stop it, or point
llm.endpointat a closed port) and let an update cycle run:[WARN] failed to classify batchappears and the new rows land withclassified_atNULL. - Restart newscope, with the endpoint working again.
Observed: SELECT count(*) FROM items WHERE classified_at IS NULL does not move, neither startup pass picks anything up, and those articles never appear in the UI, which lists classified rows only. Nothing queries them again for as long as the database lives.
Expected: they are classified once the endpoint recovers.
Impact
Articles are stored and then invisible for good after an ordinary transient outage plus a restart. Extraction is off by default when the section is absent from the config (pkg/config/config.go:81), so this is not an exotic setup. The gap became reachable only with #43, which stopped that mode from panicking on the first item; before that, extraction-disabled installs died immediately, so nothing regressed here.
Options
- A startup query for unclassified rows that does not require extracted content. Add
GetItemsNeedingClassification(ctx, limit)(classified_at IS NULL AND extraction_error = '') to the repository and to the scheduler'sItemManager, and use it instead of the two loops when extraction is disabled, feeding the existing processing worker. It drains as classification setsclassified_at, so the endless re-query that the current guard avoids cannot come back. Cost: one query and one interface method. - Store the feed content as the extracted content when extraction is off, which makes both existing queries work untouched. Cheaper, but it changes what
extracted_contentmeans, shows feed content in the UI as though it had been extracted, and leaves those rows permanently un-extractable if extraction is enabled later, sinceextracted_atwould be set. I would not do this without you asking for it. - Leave it and document the mode as fire-and-forget: with extraction disabled, an item missed at ingest is not retried. Cheapest, and defensible if that mode is secondary.
I would take option 1. Happy to send the PR with a regression test that fails classification, restarts the scheduler and asserts the item is picked up, once you say which shape you want.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with processExistingItems in pkg/scheduler/scheduler.go, then read the item queries and UpdateItemProcessed in pkg/repository/item.go, along with extractContent and ProcessBatch in pkg/scheduler/feed_processor.go. Add a regression test for extraction-disabled classification failure followed by scheduler restart; done means the previously unclassified item is picked up and classified after recovery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100