You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rewritten 2026-08-19 in a backlog staleness pass. The problem is real and confirmed in current code, but it is more severe than "an enhancement" and the implementation instructions pointed at a package that no longer exists plus a policy that has since been repudiated. The extractor itself is now CannObserv/cannobserv#354; this issue keeps the Watcher half. Original body preserved at the bottom.
The problem (confirmed)
src/core/registry.py maps five essences; dispatch is total and everything else falls back to HTML:
application/json is not listed, so JSON runs through HtmlExtractor.
Severity correction: not benign
The original said "HTML fallback is non-crashing today, so this is an enhancement, not a regression." Non-crashing, yes. Harmless, no — verified against installed co-core 0.10.0:
raw=b'{"items": [{"title": "Rule 5", "status": "active"}]}'HtmlExtractor().extract(raw, {"algorithm": "jsonpath", "selector": "$.items[*].title"})
# -> one chunk, text = the entire document. Selector ignored.
Identical to passing config=None. Archiver's schema enforces {json: jsonpath} as a content-kind family, so a JSON InfoSource legitimately carries a jsonpath spec — which then reaches an extractor that cannot honour it.
Result: a silent false-positive generator. An item whose spec says "watch $.items[*].title" fingerprints the whole document instead, so any unrelated field — a timestamp, a counter — reads as a change to the watched content. Chunks are non-empty, so #258's empty-extraction guard never trips and health stays OK. It fails quietly while appearing to work.
Whether this is live today depends on whether any watched item currently carries a jsonpath spec — worth checking before deciding priority, since it determines if this is latent or actively producing noise.
Corrections to the original instructions
The Ask said:
src/core/extractors/ — new JsonExtractor (mirror to /home/exedev/archiver/src/core/extractors/ per the mirrored-content-acquisition policy)
Both halves are wrong now:
There is no src/core/extractors/ in Watcher. Extraction moved to co-core (co_core.pure.extract.{html,pdf,csv_excel}) — confirmed: the package has no json module. Writing the extractor is a cannobserv change, filed as CannObserv/cannobserv#354.
Watcher's half (this issue) — blocked on cannobserv#354
Small once upstream lands:
Import JsonExtractor and map application/json → it in _DEFAULT_EXTRACTOR_MAP (src/core/registry.py).
application/*+json (vendor JSON) essence handling — this is genuinely Watcher's, since essence resolution lives in src/core/media_type.py. Decide whether application/ld+json, application/vnd.api+json etc. normalize to application/json for dispatch, or need explicit entries. A structured-suffix rule (+json ⇒ json family) is the obvious approach and mirrors the existing .xlsx/.pdf extension-tiebreaker pattern.
Routing tests: application/json → JsonExtractor; a +json vendor type → JsonExtractor; unmapped essence still → HtmlExtractor.
#168 (slice 2) made content_media_type drive extractor dispatch via ServiceRegistry.get_extractor (src/core/registry.py). Dispatch is total: any essence not explicitly mapped falls back to the HTML extractor.
application/json has no dedicated extractor, so JSON targets are currently run through HtmlExtractor. Archiver's declared content-kind family already includes json (jsonpath → json), but Watcher has no JSON extractor to match.
Ask
Add a JSON extractor (e.g. canonicalize/pretty-print + structural chunking, or JSONPath-aware extraction aligned with Archiver's jsonpath algorithm) and register it:
src/core/extractors/ — new JsonExtractor (mirror to /home/exedev/archiver/src/core/extractors/ per the mirrored-content-acquisition policy).
The problem (confirmed)
src/core/registry.pymaps five essences; dispatch is total and everything else falls back to HTML:application/jsonis not listed, so JSON runs throughHtmlExtractor.Severity correction: not benign
The original said "HTML fallback is non-crashing today, so this is an enhancement, not a regression." Non-crashing, yes. Harmless, no — verified against installed co-core 0.10.0:
Identical to passing
config=None. Archiver's schema enforces{json: jsonpath}as a content-kind family, so a JSON InfoSource legitimately carries ajsonpathspec — which then reaches an extractor that cannot honour it.Result: a silent false-positive generator. An item whose spec says "watch
$.items[*].title" fingerprints the whole document instead, so any unrelated field — a timestamp, a counter — reads as a change to the watched content. Chunks are non-empty, so #258's empty-extraction guard never trips and health stays OK. It fails quietly while appearing to work.Whether this is live today depends on whether any watched item currently carries a
jsonpathspec — worth checking before deciding priority, since it determines if this is latent or actively producing noise.Corrections to the original instructions
The Ask said:
Both halves are wrong now:
src/core/extractors/in Watcher. Extraction moved to co-core (co_core.pure.extract.{html,pdf,csv_excel}) — confirmed: the package has nojsonmodule. Writing the extractor is acannobservchange, filed as CannObserv/cannobserv#354.src/mirrors to Archiver (Refactor brittle Watcher↔Archiver "mirror discipline" for shared content-acquisition code #159, Phase 1c: adopt co-core v0.5.0 content-acquisition; delete the src/core mirror #236) — don't reintroduce a sync obligation." Following that instruction would resurrect a discipline that was deliberately removed. Do not.Watcher's half (this issue) — blocked on cannobserv#354
Small once upstream lands:
JsonExtractorand mapapplication/json→ it in_DEFAULT_EXTRACTOR_MAP(src/core/registry.py).application/*+json(vendor JSON) essence handling — this is genuinely Watcher's, since essence resolution lives insrc/core/media_type.py. Decide whetherapplication/ld+json,application/vnd.api+jsonetc. normalize toapplication/jsonfor dispatch, or need explicit entries. A structured-suffix rule (+json⇒ json family) is the obvious approach and mirrors the existing.xlsx/.pdfextension-tiebreaker pattern.application/json→JsonExtractor; a+jsonvendor type →JsonExtractor; unmapped essence still →HtmlExtractor.content_media_typeauto-detection (Auto-detect content type from URI response; evaluate MIME vs enum #168) resolves JSON correctly, or whether the tiebreaker needs a.jsonextension entry alongside.pdf/.xlsx.Acceptance
application/jsondispatches to a real JSON extractor, not HTMLapplication/*+jsonhandled deliberately (mapped or explicitly out of scope)jsonpathspec actually narrows the fingerprint to the selected nodesRelated
Original body (2026-06-23)
Background
#168 (slice 2) made
content_media_typedrive extractor dispatch viaServiceRegistry.get_extractor(src/core/registry.py). Dispatch is total: any essence not explicitly mapped falls back to the HTML extractor.application/jsonhas no dedicated extractor, so JSON targets are currently run throughHtmlExtractor. Archiver's declared content-kind family already includesjson(jsonpath → json), but Watcher has no JSON extractor to match.Ask
Add a JSON extractor (e.g. canonicalize/pretty-print + structural chunking, or JSONPath-aware extraction aligned with Archiver's
jsonpathalgorithm) and register it:src/core/extractors/— newJsonExtractor(mirror to/home/exedev/archiver/src/core/extractors/per the mirrored-content-acquisition policy).src/core/registry.py— mapapplication/json(and likelyapplication/*+json) →JsonExtractor.application/json→ JsonExtractor) + extraction behavior.Notes
application/*+json(vendor JSON) essence handling at the same time.