# Import Wordcell and Jungle captures

Sponge has importers for Wordcell capture bundles and Jungle Reading notes only. There is no importer for Pocket,
Instapaper, Readwise or browser bookmarks; add those links with `sponge add URL` or `sponge saves add URL`.

Import saved captures, notes, and reading state through a source manifest.
The import leaves the source files unchanged.

Import is additive. It never changes Jungle files, reading authority, published
selection or source bundles. Explicit export is private by default; import does
not publish anything. Use an owner-prepared source manifest beside the capture
directories:

```json
{
  "schemaVersion": 1,
  "namespace": "jungle",
  "entries": [
    {
      "id": "existing-external-id",
      "bundle": "captures/example",
      "privacy": "private",
      "notes": [{"id": "existing-note-id", "body": "My reading note"}],
      "readingState": {"read": true}
    }
  ]
}
```

`bundle` must be a confined relative directory path with no symbolic-link
traversal. `privacy` is required for every entry; use `published` only for an
already published source association. IDs and note IDs must remain stable.
The source manifest is bounded to 1 MiB/1,000 entries; each entry admits up to
100 notes of 64 KiB each. Do not include passwords, cookies or runtime paths.

```sh
sponge import /absolute/source/manifest.json --dry-run --limit 20 --json
sponge import /absolute/source/manifest.json --apply --limit 20 --json
sponge import /absolute/source/manifest.json --apply --offset 20 --limit 20 --json
```

The default is a dry run. Follow `nextOffset` for bounded batches. Each entry
reports ready/imported/duplicate/unsupported/failed independently. A custody or
owner conflict stops the batch and returns the blocked entry as `nextOffset`.
After interrupted worker recovery, rerun the same batch; atomic import receipts
prevent duplicate revisions and notes. An explicitly cancelled job stays
cancelled; it is not automatically revived by import.

Released Wordcell web or PDF verifiers validate each bundle before copying.
Import computes current content digests and records them separately from the
historical verification result. Missing legacy document/asset digests remain
`historicalIntegrity: unavailable`, and the readable item remains partial.
Mismatched hashes and unsupported/blocked captures are rejected individually.

Source privacy and external IDs remain in revision metadata. Imported notes and
reading state are committed atomically with the revision and import receipt.
Existing user reading choices win; conflicting text for an existing imported
note ID fails that entry without changing the previous revision. No source
reading projection or consumer switch is part of this command.

## Jungle Reading notes

```sh
sponge import jungle /absolute/jungle --dry-run --json
sponge import jungle /absolute/jungle --apply --json
```

This reads Jungle in place and never writes to it. Each `kb/notes/reading/<slug>.md`
note names its capture in `relations.synthesizes` (or `evidenced-by` for the
`note` form) as `articles/<slug>/<slug>`; the bundle is that directory under `kb/`.
Per note, in one batch of up to 1,000:

- The capture is imported through the verified bundle path above (namespace
  `jungle`, external id = the capture link, privacy always `private`), so the
  item and revision do not depend on the note's publication state.
- The whole note becomes an `editorial` derivation (gist, every idea and quote,
  source, form, frontmatter, raw Markdown and its sha256), accepted through the
  logged `review.note` path. Digest, social, note and conversation forms are all
  kept; only a digest note is parsed strictly.
- A digest note is also converted to Digest v2: the Gist's first sentence is the
  lead and the rest its context; each idea's bold claim and detail are kept as
  written. Each quote is kept only when it occurs in the revision's Markdown as
  written (typography and whitespace folded; a match that only works across
  Markdown markup does not count) and has at least four words, with its
  attribution. As in the pipeline, quotes over the six-quote cap or the quoted
  word budget (at most 150 words and 10% of the source, with a 25-word floor)
  are dropped whole, longest first and then later in the note first, never the
  last one (`budget`); a single quote still over budget fails the note. Each
  kept quote goes to the idea sharing the most content words
  with its claim and detail (ties to the earliest idea), at most two per idea;
  an extra goes to the next best idea with room, else is dropped. Dropped quotes
  are reported per note (`quotes.dropped`, by position and reason). A note that
  then passes every v2 rule is stored as a Digest v2, accepted per field, linked
  to the editorial object by its input hash. Anything else is reported
  `not-convertible` with its reasons (for example `ideas:quote-count` when no
  quote survives, or `gist.lead:too-long`); quote text is never altered and owner
  text is never truncated or rewritten.
- `reading.status` sets visibility through the logged setter. Jungle's
  `kb/notes/reading/AGENTS.md` treats `published` as explicit public approval and
  keeps drafts out of the site, and its generated site lists exactly the published
  notes, so `published` sets `public` and `draft` restores `private`. Any other
  status leaves the default policy (private for imports). Capability URLs, local
  files and account captures are never made public (`guarded`).
- A local capture (a scanned PDF or file with no web URI of its own) takes its web
  address from the note: an http(s) `reading.source_url` becomes the item's primary
  shareable URI (origin `metadata`, derived like a captured locator, so fragments,
  click trackers and credential-like fields are dropped) and the file's
  `urn:sha256:` stays as an alias. The item is then classed as an import rather than
  a local file, so `published` makes it public. A capability `source_url` is never
  recorded (`uri: capability`) and the item stays guarded. The note's `title`
  replaces the capture's title (often a file name or OCR text) through the logged
  title field, unless the title was changed in Sponge since (`kept-user-edit`). A
  web capture keeps its captured title (`title: web-capture`); `sponge title set`
  overrides either.
  When several notes share one capture, the first by file name decides both
  (`other-note`). A later capture revision keeps the recorded URI as primary; its
  capture title is replaced again on the next import. Re-importing a library
  imported before this rule applies it once.

Re-importing an unchanged note changes nothing. An edited note is a new editorial
derivation (and a new v2 digest when it converts); earlier ones stay. A library
imported before Digest v2 gains a v2 digest for each convertible note on the next
import, beside its unchanged editorial object; an imported v1 digest stays, and the
new v2 digest becomes current unless the user reviewed the v1 differently. User edits
win: a visibility set in Sponge after the importer's own write (tracked by local
receipts) is kept, an editorial object the user rejected or cleared stops later
notes from being auto-accepted, and a digest the user made or reviewed differently
stops new imported digests. Deleted items are skipped.

The dry run reports per-note actions and counts (new items, editorial, digest,
a histogram of the reasons notes do not convert, quotes assigned and dropped by
reason, `uri` and `title` actions, visibility, skipped with reasons; report schema 2) without writing. It does not run the Wordcell
verifier, so it needs no capture runtime: it predicts against the bundle's own
Markdown, localized from `capture.json`'s asset list; `--apply` verifies every
bundle before anything is stored.

The Jungle checkout was read only during implementation. Corpus counts and
source associations are private operational evidence, not committed fixtures;
the tests use synthetic notes and captures. No consumer switch is part of this
command.
