Skip to content

Fix: CSL call-number not mapped to Zotero callNumber field #25

Description

@caedmon5

call-number falls through to extra instead of populating Zotero's callNumber

Summary

The pipeline does not map the standard CSL-JSON field call-number to Zotero's
callNumber field. There is no map_call_number in csl_field_mappers.py and no
catch-through in csl_to_zotero, so call-number (or any synonym like callNumber)
in the CSL-JSON input ends up in Zotero's free-text extra blob — not in the proper
callNumber field. As a result, every book, bookSection, etc. ingested to date
has gone in with a blank callNumber.

This blocks the physical-book cataloguing workflow (sticker / shelf-card generation
keyed off callNumber) and silently degrades records that do arrive with a call
number supplied.

Environment

  • Pipeline: v2
  • File: v2/csl_mapper.py, v2/csl_field_mappers.py
  • Affected item types: every type that lists callNumber in zotero_allowed_fields.py
    (book, bookSection, manuscript, map, thesis, audioRecording, videoRecording, etc.
    — basically everything physical)

Evidence

  • grep -n 'call' v2/csl_mapper.py v2/csl_field_mappers.py returns no map_call_number
    function and no copy-through.
  • csl_to_zotero (csl_mapper.py:146) only invokes the explicit map_* helpers; nothing
    copies arbitrary recognised CSL keys into zotero_item.
  • map_extra_fields (csl_field_mappers.py:373) treats unknown CSL keys as text and
    appends them to zotero_item['extra']call-number is not in the standard_keys
    set, so it lands there.
  • clean_unexpected_fields (csl_mapper.py:92) cannot recover this either, because it
    only acts on keys already present in zotero_item, and call-number was never added
    to that dict in the first place.

Expected vs Actual

  • Expected: CSL-JSON input containing "call-number": "PR1868.A1 1985" results
    in a Zotero item with callNumber: "PR1868.A1 1985", and that value flows through
    to the Obsidian note's callnumber YAML field (already wired in
    obsidian_writer.py:113).
  • Actual: The value is appended to extra as text (call-number: PR1868.A1 1985).
    Zotero's callNumber field stays empty. The Obsidian YAML callnumber field stays
    empty. Anything keyed off callNumber (sorting, filtering, label printing) is broken.

Likely Root Causes

  1. Missing mapper: No map_call_number helper exists.
  2. No catch-through for recognised-but-unmapped CSL fields: csl_to_zotero requires
    an explicit map_* for every field that should land in Zotero.
  3. Skill-side gap: The bibnow SKILL.md does not currently instruct Claude to
    include call-number for physical items, so even once the mapper is fixed, entries
    will continue to arrive without the field unless the skill is updated in parallel.

Proposed Fixes

  1. Add map_call_number to csl_field_mappers.py:

    • Read call-number from the CSL item.
    • Write to zotero_item['callNumber'] if the item type allows it
      (callNumber in ZOTERO_ALLOWED_FIELDS[item_type]); otherwise no-op (or fall
      through to extra with an explicit log line).
    • Accept both call-number (CSL standard, kebab) and callNumber (Zotero camelCase)
      as input keys, since LLM-generated CSL sometimes drifts.
  2. Wire it into csl_to_zotero alongside the existing map_isbn, map_doi, etc.

  3. Add call-number to standard_keys in map_extra_fields so it isn't
    double-handled (mapped to the proper field and re-appended to extra).

  4. Update bibnow/SKILL.md (separate change in the skill repo) to instruct Claude
    to look up the LC class number (catalog.loc.gov, MARC 050) by ISBN for any
    physical-item CSL entry and emit it as call-number.

  5. Test cases:

    • Book with call-number set → Zotero callNumber populated, extra does not
      contain call-number: line.
    • Book with callNumber (camelCase) input → same result.
    • Item type that doesn't allow callNumber (e.g. webpage) → field gracefully
      skipped or routed to extra, no crash.
    • Backfill: a one-off script to walk existing Zotero items and migrate any
      call-number: line out of extra into the proper callNumber field.

Out of Scope (separate issues)

  • The skill-side change (instructing Claude to generate call numbers).
  • Backfill of existing records that went in with blank callNumber.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions