[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ACF53J5MJCDNAEMCBV0RPP":3,"skill-same:01M6ACF53J5MJCDNAEMCBV0RPP":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":31,"linkOnly":32,"markdownContent":33,"markdownBody":34,"frontMatter":35,"refName":36,"commitSha":37,"skillDirectory":38,"forksCount":39,"repositoryHtmlUrl":40,"repositoryHomepageUrl":41,"ownerHtmlUrl":42,"ownerBlogUrl":43,"ownerDescription":6,"updatedAt":44,"install":45,"resources":58},"01M6ACF53J5MJCDNAEMCBV0RPP","tika-eval-compare",null,"Compare extracts from two Tika builds over a corpus to detect regressions in content, encoding, exceptions, and embedded-document handling. Use for \"compare before\u002Fafter extracts\", \"eval this change against the corpus\".","apache\u002Ftika","apache","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F47359?v=4","01M6AC83E6K4FDKK7HZ8WNMQKE","The Apache Software Foundation","01M6ACEMPJD6J74AGZBFFEGEYV",4145,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fdevs\u002Ftika-eval-compare\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30],"tika","tika eval compare","eval","compare","change","content","encoding","use","embedded","document","detect","for","2026-11-10 10:12:30",false,"---\nname: tika-eval-compare\ndescription: >\n  Compare extracts from two Tika builds over a corpus to detect regressions\n  in content, encoding, exceptions, and embedded-document handling. Use for\n  \"compare before\u002Fafter extracts\", \"eval this change against the corpus\".\n---\n\n\u003C!--\nLicensed to the Apache Software Foundation (ASF) under one or more\ncontributor license agreements.  See the NOTICE file distributed with\nthis work for additional information regarding copyright ownership.\nThe ASF licenses this file to You under the Apache License, Version 2.0\n(the \"License\"); you may not use this file except in compliance with\nthe License.  You may obtain a copy of the License at\n\n    http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\nLocal override: `$TIKA_SKILLS_LOCAL\u002Ftika-eval-compare\u002FLOCAL.md` (default `~\u002F.tika-skills`),\nread after this file, wins on conflict.\n\n# tika-eval: Compare Before\u002FAfter Extracts\n\nCompare the output of two versions of Tika against a corpus of files\nto detect regressions in content extraction, encoding, exceptions, and\nembedded document handling.\n\n## Before You Start\n\nAsk the user for:\n\n1. **Working directory** — where to put builds, extracts, eval db, and\n   reports (`\u003Cworkdir>` below).  All artifacts go here.\n2. **Number of threads** (`-n`) — default is 2.  Use `-n 6` for faster\n   runs when parse time comparison is not needed.  When comparing parse\n   times between A and B, use the same `-n` for both.\n3. **Run reports?** — whether to auto-generate the HTML\u002FExcel reports\n   and `summary.md` at the end (the `-r` flag on tika-eval Compare).\n\n## Prerequisites\n\n- Two tika-app builds (a \"before\" and an \"after\"), each as an unzipped\n  zip archive containing `tika-app-*.jar`, `lib\u002F`, and `plugins\u002F`.\n- A corpus of input files (a directory tree).\n- tika-eval-app, built from `tika-eval\u002Ftika-eval-app` (use the zip).\n  The bare `target\u002Ftika-eval-app-*.jar` is thin (no bundled deps) and dies with\n  `NoClassDefFoundError: ...GzipCompressorOutputStream` (esp. with `-r`). Always\n  run the jar from the unzipped `target\u002F*.zip` dir, which carries its `lib\u002F`.\n- **Enable MD5 digesting** in both configs so tika-eval can match\n  embedded documents by content hash (not just index position).\n  Add to the config JSON:\n  ```json\n  \"parse-context\": {\n    \"commons-digester-factory\": {\n      \"digests\": [\n        { \"algorithm\": \"MD5\" }\n      ]\n    }\n  }\n  ```\n  Note: `parse-context` is a JSON **object**, not an array.\n\n## Step 1 — Generate Extracts\n\nRun each tika-app version against the same input corpus.  The batch\nmode is triggered automatically when the first positional argument is\na directory:\n\n```bash\njava -jar \u003Cbefore>\u002Ftika-app-*.jar \u003Cinput-dir> \u003Cextracts-a-dir>\njava -jar \u003Cafter>\u002Ftika-app-*.jar  \u003Cinput-dir> \u003Cextracts-b-dir>\n```\n\nTo use a custom config (e.g., SAX vs DOM parsers, deleted content,\nmacros), pass `--config=\u003Cfile.json>` **before** the input\u002Foutput dirs:\n\n```bash\njava -jar \u003Ctika-app>\u002Ftika-app-*.jar --config=dom-config.json \u003Cinput-dir> \u003Cextracts-a-dir>\njava -jar \u003Ctika-app>\u002Ftika-app-*.jar --config=sax-config.json \u003Cinput-dir> \u003Cextracts-b-dir>\n```\n\nEach run walks the input directory recursively and writes one\n`.json` file per input file (recursive metadata + XHTML content,\nequivalent to `tika-app -J`).  The directory structure mirrors\nthe input.\n\n### Provenance + crash ledger (preferred)\n\n`run-batch.sh` (next to this file) wraps the same invocation and records what\nran, so an extract set can be tied to a build after the fact and a crashed\nfile is distinguishable from one that parsed to nothing:\n\n```bash\n.skills\u002Fdevs\u002Ftika-eval-compare\u002Frun-batch.sh --app \u003Cbefore> --input \u003Cinput-dir> --extracts \u003Cextracts-a-dir> --note baseline\n.skills\u002Fdevs\u002Ftika-eval-compare\u002Frun-batch.sh --app \u003Cafter>  --input \u003Cinput-dir> --extracts \u003Cextracts-b-dir> --note candidate [--config cfg.json]\n```\n\nIt writes `run-info-\u003Crun.id>.json` and, when the app's file-system plugin\nships the jsonl reporter (TIKA-4846), `crashes-\u003Crun.id>.jsonl` into\n`\u003Cextracts>\u002F.run-info\u002F`; Compare picks them up from there by default\n(`-ra\u002F-rb`, `-pa\u002F-pb` override). A baseline without the reporter gets\nrun-info but no ledger. Needs python3.\n\nLaunching tika-app directly (a standing run script with its own config, as on a\ncorpus box) skips `run-batch.sh`, so add the reporter to that config once with the\nledger path read from the environment, and `export TIKA_EXTRACTS=\u003Cextracts-dir>`\nin the same shell as the run. An unset variable fails the load naming it and the\nJSON path, so a run cannot silently proceed without its ledger. The file name must\nkeep the `crashes-` prefix for Compare\u002FProfile to find it by default:\n\n```json\n\"pipes-reporters\": {\n  \"file-system-jsonl-reporter\": {\n    \"path\": \"${env:TIKA_EXTRACTS}\u002F.run-info\u002Fcrashes-batch.jsonl\",\n    \"includes\": [\"OOM\", \"TIMEOUT\", \"UNSPECIFIED_CRASH\", \"FAILED_TO_INITIALIZE\",\n                 \"FETCHER_INITIALIZATION_EXCEPTION\", \"EMITTER_INITIALIZATION_EXCEPTION\",\n                 \"CLIENT_UNAVAILABLE_WITHIN_MS\", \"FETCH_EXCEPTION\", \"EMIT_EXCEPTION\",\n                 \"FETCHER_NOT_FOUND\", \"EMITTER_NOT_FOUND\", \"PAYLOAD_LIMIT_EXCEEDED\"],\n    \"onExists\": \"EXCEPTION\",\n    \"maxMessageLength\": 4096\n  }\n}\n```\n\n### Notes\n\n- Do NOT pass `-n \u003CN>` as a trailing argument — it confuses the\n  async mode auto-detection.  If you need to control parallelism,\n  use `-i \u003Cinput-dir> -o \u003Coutput-dir> -n \u003CN>` with explicit flags.\n- Default parallelism is 2 forked JVM clients.  Use `-n 6` for faster\n  runs when parse time comparison is not needed.  When comparing parse\n  times, keep `-n` the same for both A and B.\n- Default timeout is 30 000 ms per file.\n\n## Step 2 — Run tika-eval Compare\n\nUnzip the tika-eval-app zip, then run:\n\n```bash\njava -jar \u003Ctika-eval>\u002Ftika-eval-app-*.jar Compare \\\n  -a \u003Cextracts-a-dir> \\\n  -b \u003Cextracts-b-dir> \\\n  -d \u003Cdb-path> \\\n  -r \\\n  -rd \u003Creports-dir>\n```\n\n| Flag | Description |\n|------|-------------|\n| `-a` | Directory of \"before\" extracts (required) |\n| `-b` | Directory of \"after\" extracts (required) |\n| `-d` | H2 database path (temp file if omitted) |\n| `-r` | Auto-run Report + tgz the reports dir (`\u003CreportsDir>.tgz`) after Compare |\n| `-rd` | Reports output directory (default: `reports`) |\n| `-z` | Gzip the H2 db (`\u003Cdb>.mv.db.gz`) after Compare for transfer; requires `-d` (no-op + warning for a temp db). Combine with `-r` to package both. |\n| `-n` | Number of worker threads |\n| `-pa`\u002F`-pb` | jsonl ledger for A\u002FB (default: `\u003Cextracts>\u002F.run-info\u002Fcrashes-*.jsonl`); fills `containers.pipes_status_a\u002Fb` |\n| `-ra`\u002F`-rb` | run-info json for A\u002FB (default: `\u003Cextracts>\u002F.run-info\u002Frun-info-*.json`); lands in `run_info_a\u002Fb`. Refused unless the matching ledger is named `crashes-\u003Crun.id>.jsonl`; discovery refuses a `.run-info` holding more than one run-info or ledger |\n\nWith `-pa`\u002F`-pb`, `summary.md` and `exceptions\u002Fextract_exceptions_by_pipes_status_*.xlsx`\nsplit `NO_EXTRACT_FILE` into `CRASH` (OOM\u002FTIMEOUT\u002FUNSPECIFIED_CRASH), any other\nrecorded status as-is, `NO_PIPES_RECORD` (ledger has no line: the batch recorded\nno failure), `BATCH_WITHOUT_LEDGER`, and `NO_PIPES_REPORT_SUPPLIED`. A crash\nstatus *with* an extract present is a success whose status was lost — listed\nseparately, not a failure. `run_info_a\u002Fb.pipes_report.joined` says how many\ncontainers matched a ledger row; zero with a non-empty ledger means the wrong\nledger, or a crawl without `-i` (which never sees files that crashed).\n\n## Step 3 — Review Results\n\nReports are written as Excel `.xlsx` files under the reports\ndirectory, plus a `summary.md` with key metrics:\n\n- **Content Quality (Dice Coefficient)** — similarity between A and B\n  per mime type.  Mean dice \u003C 0.95 warrants investigation.\n- **OOV \u002F Languageness Changes** — increased out-of-vocabulary rate\n  or decreased languageness z-score may indicate encoding regressions.\n- **Content Length Ratio Outliers** — files where B is >2× or \u003C0.5×\n  the length of A.\n- **Exception Changes** — new exceptions in B or fixed exceptions.\n- **Embedded Document Count Changes** — gained\u002Flost attachments.\n- **Content Regressions** — lowest-dice files with token-level diffs.\n- **Content Lost \u002F Gained** — files that went empty↔non-empty.\n\n### Interpreting Results\n\n| Metric | Good | Investigate |\n|--------|------|-------------|\n| Mean dice (same mime) | ≥ 0.95 | \u003C 0.90 |\n| New exceptions in B | 0 | > 0 — every one needs explanation |\n| Embedded doc count losses | 0 | > 0 — investigate by mime type |\n| OOV delta | \u003C 0.05 | > 0.10 |\n| Content length ratio | 0.5–2.0 | > 5× or \u003C 0.2× |\n| Exception count | ≤ A | > A |\n| Total files (B) vs (A) | equal or higher | lower — missing embedded docs |\n\n### Encoding-detection evals\n\nFor charset\u002Fencoding-detector changes, the summary reports don't cover it — query\nthe db directly (see the **tika-eval-h2-query** skill). The detected encoding is in\nthe `ENCODINGS_A`\u002F`ENCODINGS_B` tables (`DETECTED_ENCODING`, `ENCODING_DETECTOR`,\n`DECLARED_METADATA`), **not** `PROFILES`. Key signals: per-encoding counts (e.g. CJK\ntotal), A→B flips by direction, and OOV on the flipped files (a flip that *worsens*\nOOV is a regression; one that *improves* it is a fix). Pair on `ID`; map back to the\nsource file via `PROFILES_*.FILE_NAME` (the content hash).\n\n### CRITICAL: Review Checklist\n\nThe purpose of tika-eval is to find regressions BEFORE a release. After\nreading summary.md, report each of these to the user:\n\n1. **New exceptions**: Exact count. If > 0, investigate stack traces.\n   Every new exception is a bug.\n2. **Total files delta**: \"Total files (A)\" vs \"(B)\". If B \u003C A, embedded\n   documents are being lost — aggregate the losses by child mime type.\n3. **Embedded doc count changes**: Report losers from the summary table.\n4. **Dice scores**: Flag any mean \u003C 0.99 for OOXML or \u003C 0.95 for others.\n5. **Content length outliers**: Flag ratio > 3x or \u003C 0.3x.\n6. **Fixed exceptions**: Report the count.\n\n**Do not summarize results as \"looks good\" based on dice scores alone.**\nDice measures text similarity, not attachment completeness. Always check\nthe Total files delta.\n\n**After fixing regressions, re-run the full eval.** A fix for one format\nmay not cover another. Verify the numbers moved and no new issues appeared.\n\n## Building from Source\n\n```bash\n# tika-app\n.\u002Fmvnw clean install -pl tika-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\n\n# tika-eval-app\n.\u002Fmvnw clean install -pl tika-eval\u002Ftika-eval-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\n```\n\nThe zip artifacts are in `\u003Cmodule>\u002Ftarget\u002F\u003Cmodule>-*.zip`.\n\n## Example: Comparing MSG parsing changes\n\n```bash\n# Download the \"before\" snapshot\ncurl -o \u002Ftmp\u002Ftika-app-before.zip \u003Csnapshot-url>\nunzip -qo \u002Ftmp\u002Ftika-app-before.zip -d \u002Ftmp\u002Ftika-app-before\n\n# Build the \"after\" from local changes\n.\u002Fmvnw clean install -pl tika-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\nunzip -qo tika-app\u002Ftarget\u002Ftika-app-*.zip -d \u002Ftmp\u002Ftika-app-after\n\n# Generate extracts\njava -jar \u002Ftmp\u002Ftika-app-before\u002Ftika-app-*.jar \u003Ccorpus> \u002Ftmp\u002Fextracts-a\njava -jar \u002Ftmp\u002Ftika-app-after\u002Ftika-app-*.jar  \u003Ccorpus> \u002Ftmp\u002Fextracts-b\n\n# Build and run tika-eval\n.\u002Fmvnw clean install -pl tika-eval\u002Ftika-eval-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\nunzip -qo tika-eval\u002Ftika-eval-app\u002Ftarget\u002Ftika-eval-app-*.zip -d \u002Ftmp\u002Ftika-eval\n\njava -jar \u002Ftmp\u002Ftika-eval\u002Ftika-eval-app-*.jar Compare \\\n  -a \u002Ftmp\u002Fextracts-a -b \u002Ftmp\u002Fextracts-b \\\n  -d \u002Ftmp\u002Feval-db -r -rd \u002Ftmp\u002Feval-reports\n\n# Review\ncat \u002Ftmp\u002Feval-reports\u002Fsummary.md\n```\n","\u003C!--\nLicensed to the Apache Software Foundation (ASF) under one or more\ncontributor license agreements.  See the NOTICE file distributed with\nthis work for additional information regarding copyright ownership.\nThe ASF licenses this file to You under the Apache License, Version 2.0\n(the \"License\"); you may not use this file except in compliance with\nthe License.  You may obtain a copy of the License at\n\n    http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\nLocal override: `$TIKA_SKILLS_LOCAL\u002Ftika-eval-compare\u002FLOCAL.md` (default `~\u002F.tika-skills`),\nread after this file, wins on conflict.\n\n# tika-eval: Compare Before\u002FAfter Extracts\n\nCompare the output of two versions of Tika against a corpus of files\nto detect regressions in content extraction, encoding, exceptions, and\nembedded document handling.\n\n## Before You Start\n\nAsk the user for:\n\n1. **Working directory** — where to put builds, extracts, eval db, and\n   reports (`\u003Cworkdir>` below).  All artifacts go here.\n2. **Number of threads** (`-n`) — default is 2.  Use `-n 6` for faster\n   runs when parse time comparison is not needed.  When comparing parse\n   times between A and B, use the same `-n` for both.\n3. **Run reports?** — whether to auto-generate the HTML\u002FExcel reports\n   and `summary.md` at the end (the `-r` flag on tika-eval Compare).\n\n## Prerequisites\n\n- Two tika-app builds (a \"before\" and an \"after\"), each as an unzipped\n  zip archive containing `tika-app-*.jar`, `lib\u002F`, and `plugins\u002F`.\n- A corpus of input files (a directory tree).\n- tika-eval-app, built from `tika-eval\u002Ftika-eval-app` (use the zip).\n  The bare `target\u002Ftika-eval-app-*.jar` is thin (no bundled deps) and dies with\n  `NoClassDefFoundError: ...GzipCompressorOutputStream` (esp. with `-r`). Always\n  run the jar from the unzipped `target\u002F*.zip` dir, which carries its `lib\u002F`.\n- **Enable MD5 digesting** in both configs so tika-eval can match\n  embedded documents by content hash (not just index position).\n  Add to the config JSON:\n  ```json\n  \"parse-context\": {\n    \"commons-digester-factory\": {\n      \"digests\": [\n        { \"algorithm\": \"MD5\" }\n      ]\n    }\n  }\n  ```\n  Note: `parse-context` is a JSON **object**, not an array.\n\n## Step 1 — Generate Extracts\n\nRun each tika-app version against the same input corpus.  The batch\nmode is triggered automatically when the first positional argument is\na directory:\n\n```bash\njava -jar \u003Cbefore>\u002Ftika-app-*.jar \u003Cinput-dir> \u003Cextracts-a-dir>\njava -jar \u003Cafter>\u002Ftika-app-*.jar  \u003Cinput-dir> \u003Cextracts-b-dir>\n```\n\nTo use a custom config (e.g., SAX vs DOM parsers, deleted content,\nmacros), pass `--config=\u003Cfile.json>` **before** the input\u002Foutput dirs:\n\n```bash\njava -jar \u003Ctika-app>\u002Ftika-app-*.jar --config=dom-config.json \u003Cinput-dir> \u003Cextracts-a-dir>\njava -jar \u003Ctika-app>\u002Ftika-app-*.jar --config=sax-config.json \u003Cinput-dir> \u003Cextracts-b-dir>\n```\n\nEach run walks the input directory recursively and writes one\n`.json` file per input file (recursive metadata + XHTML content,\nequivalent to `tika-app -J`).  The directory structure mirrors\nthe input.\n\n### Provenance + crash ledger (preferred)\n\n`run-batch.sh` (next to this file) wraps the same invocation and records what\nran, so an extract set can be tied to a build after the fact and a crashed\nfile is distinguishable from one that parsed to nothing:\n\n```bash\n.skills\u002Fdevs\u002Ftika-eval-compare\u002Frun-batch.sh --app \u003Cbefore> --input \u003Cinput-dir> --extracts \u003Cextracts-a-dir> --note baseline\n.skills\u002Fdevs\u002Ftika-eval-compare\u002Frun-batch.sh --app \u003Cafter>  --input \u003Cinput-dir> --extracts \u003Cextracts-b-dir> --note candidate [--config cfg.json]\n```\n\nIt writes `run-info-\u003Crun.id>.json` and, when the app's file-system plugin\nships the jsonl reporter (TIKA-4846), `crashes-\u003Crun.id>.jsonl` into\n`\u003Cextracts>\u002F.run-info\u002F`; Compare picks them up from there by default\n(`-ra\u002F-rb`, `-pa\u002F-pb` override). A baseline without the reporter gets\nrun-info but no ledger. Needs python3.\n\nLaunching tika-app directly (a standing run script with its own config, as on a\ncorpus box) skips `run-batch.sh`, so add the reporter to that config once with the\nledger path read from the environment, and `export TIKA_EXTRACTS=\u003Cextracts-dir>`\nin the same shell as the run. An unset variable fails the load naming it and the\nJSON path, so a run cannot silently proceed without its ledger. The file name must\nkeep the `crashes-` prefix for Compare\u002FProfile to find it by default:\n\n```json\n\"pipes-reporters\": {\n  \"file-system-jsonl-reporter\": {\n    \"path\": \"${env:TIKA_EXTRACTS}\u002F.run-info\u002Fcrashes-batch.jsonl\",\n    \"includes\": [\"OOM\", \"TIMEOUT\", \"UNSPECIFIED_CRASH\", \"FAILED_TO_INITIALIZE\",\n                 \"FETCHER_INITIALIZATION_EXCEPTION\", \"EMITTER_INITIALIZATION_EXCEPTION\",\n                 \"CLIENT_UNAVAILABLE_WITHIN_MS\", \"FETCH_EXCEPTION\", \"EMIT_EXCEPTION\",\n                 \"FETCHER_NOT_FOUND\", \"EMITTER_NOT_FOUND\", \"PAYLOAD_LIMIT_EXCEEDED\"],\n    \"onExists\": \"EXCEPTION\",\n    \"maxMessageLength\": 4096\n  }\n}\n```\n\n### Notes\n\n- Do NOT pass `-n \u003CN>` as a trailing argument — it confuses the\n  async mode auto-detection.  If you need to control parallelism,\n  use `-i \u003Cinput-dir> -o \u003Coutput-dir> -n \u003CN>` with explicit flags.\n- Default parallelism is 2 forked JVM clients.  Use `-n 6` for faster\n  runs when parse time comparison is not needed.  When comparing parse\n  times, keep `-n` the same for both A and B.\n- Default timeout is 30 000 ms per file.\n\n## Step 2 — Run tika-eval Compare\n\nUnzip the tika-eval-app zip, then run:\n\n```bash\njava -jar \u003Ctika-eval>\u002Ftika-eval-app-*.jar Compare \\\n  -a \u003Cextracts-a-dir> \\\n  -b \u003Cextracts-b-dir> \\\n  -d \u003Cdb-path> \\\n  -r \\\n  -rd \u003Creports-dir>\n```\n\n| Flag | Description |\n|------|-------------|\n| `-a` | Directory of \"before\" extracts (required) |\n| `-b` | Directory of \"after\" extracts (required) |\n| `-d` | H2 database path (temp file if omitted) |\n| `-r` | Auto-run Report + tgz the reports dir (`\u003CreportsDir>.tgz`) after Compare |\n| `-rd` | Reports output directory (default: `reports`) |\n| `-z` | Gzip the H2 db (`\u003Cdb>.mv.db.gz`) after Compare for transfer; requires `-d` (no-op + warning for a temp db). Combine with `-r` to package both. |\n| `-n` | Number of worker threads |\n| `-pa`\u002F`-pb` | jsonl ledger for A\u002FB (default: `\u003Cextracts>\u002F.run-info\u002Fcrashes-*.jsonl`); fills `containers.pipes_status_a\u002Fb` |\n| `-ra`\u002F`-rb` | run-info json for A\u002FB (default: `\u003Cextracts>\u002F.run-info\u002Frun-info-*.json`); lands in `run_info_a\u002Fb`. Refused unless the matching ledger is named `crashes-\u003Crun.id>.jsonl`; discovery refuses a `.run-info` holding more than one run-info or ledger |\n\nWith `-pa`\u002F`-pb`, `summary.md` and `exceptions\u002Fextract_exceptions_by_pipes_status_*.xlsx`\nsplit `NO_EXTRACT_FILE` into `CRASH` (OOM\u002FTIMEOUT\u002FUNSPECIFIED_CRASH), any other\nrecorded status as-is, `NO_PIPES_RECORD` (ledger has no line: the batch recorded\nno failure), `BATCH_WITHOUT_LEDGER`, and `NO_PIPES_REPORT_SUPPLIED`. A crash\nstatus *with* an extract present is a success whose status was lost — listed\nseparately, not a failure. `run_info_a\u002Fb.pipes_report.joined` says how many\ncontainers matched a ledger row; zero with a non-empty ledger means the wrong\nledger, or a crawl without `-i` (which never sees files that crashed).\n\n## Step 3 — Review Results\n\nReports are written as Excel `.xlsx` files under the reports\ndirectory, plus a `summary.md` with key metrics:\n\n- **Content Quality (Dice Coefficient)** — similarity between A and B\n  per mime type.  Mean dice \u003C 0.95 warrants investigation.\n- **OOV \u002F Languageness Changes** — increased out-of-vocabulary rate\n  or decreased languageness z-score may indicate encoding regressions.\n- **Content Length Ratio Outliers** — files where B is >2× or \u003C0.5×\n  the length of A.\n- **Exception Changes** — new exceptions in B or fixed exceptions.\n- **Embedded Document Count Changes** — gained\u002Flost attachments.\n- **Content Regressions** — lowest-dice files with token-level diffs.\n- **Content Lost \u002F Gained** — files that went empty↔non-empty.\n\n### Interpreting Results\n\n| Metric | Good | Investigate |\n|--------|------|-------------|\n| Mean dice (same mime) | ≥ 0.95 | \u003C 0.90 |\n| New exceptions in B | 0 | > 0 — every one needs explanation |\n| Embedded doc count losses | 0 | > 0 — investigate by mime type |\n| OOV delta | \u003C 0.05 | > 0.10 |\n| Content length ratio | 0.5–2.0 | > 5× or \u003C 0.2× |\n| Exception count | ≤ A | > A |\n| Total files (B) vs (A) | equal or higher | lower — missing embedded docs |\n\n### Encoding-detection evals\n\nFor charset\u002Fencoding-detector changes, the summary reports don't cover it — query\nthe db directly (see the **tika-eval-h2-query** skill). The detected encoding is in\nthe `ENCODINGS_A`\u002F`ENCODINGS_B` tables (`DETECTED_ENCODING`, `ENCODING_DETECTOR`,\n`DECLARED_METADATA`), **not** `PROFILES`. Key signals: per-encoding counts (e.g. CJK\ntotal), A→B flips by direction, and OOV on the flipped files (a flip that *worsens*\nOOV is a regression; one that *improves* it is a fix). Pair on `ID`; map back to the\nsource file via `PROFILES_*.FILE_NAME` (the content hash).\n\n### CRITICAL: Review Checklist\n\nThe purpose of tika-eval is to find regressions BEFORE a release. After\nreading summary.md, report each of these to the user:\n\n1. **New exceptions**: Exact count. If > 0, investigate stack traces.\n   Every new exception is a bug.\n2. **Total files delta**: \"Total files (A)\" vs \"(B)\". If B \u003C A, embedded\n   documents are being lost — aggregate the losses by child mime type.\n3. **Embedded doc count changes**: Report losers from the summary table.\n4. **Dice scores**: Flag any mean \u003C 0.99 for OOXML or \u003C 0.95 for others.\n5. **Content length outliers**: Flag ratio > 3x or \u003C 0.3x.\n6. **Fixed exceptions**: Report the count.\n\n**Do not summarize results as \"looks good\" based on dice scores alone.**\nDice measures text similarity, not attachment completeness. Always check\nthe Total files delta.\n\n**After fixing regressions, re-run the full eval.** A fix for one format\nmay not cover another. Verify the numbers moved and no new issues appeared.\n\n## Building from Source\n\n```bash\n# tika-app\n.\u002Fmvnw clean install -pl tika-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\n\n# tika-eval-app\n.\u002Fmvnw clean install -pl tika-eval\u002Ftika-eval-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\n```\n\nThe zip artifacts are in `\u003Cmodule>\u002Ftarget\u002F\u003Cmodule>-*.zip`.\n\n## Example: Comparing MSG parsing changes\n\n```bash\n# Download the \"before\" snapshot\ncurl -o \u002Ftmp\u002Ftika-app-before.zip \u003Csnapshot-url>\nunzip -qo \u002Ftmp\u002Ftika-app-before.zip -d \u002Ftmp\u002Ftika-app-before\n\n# Build the \"after\" from local changes\n.\u002Fmvnw clean install -pl tika-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\nunzip -qo tika-app\u002Ftarget\u002Ftika-app-*.zip -d \u002Ftmp\u002Ftika-app-after\n\n# Generate extracts\njava -jar \u002Ftmp\u002Ftika-app-before\u002Ftika-app-*.jar \u003Ccorpus> \u002Ftmp\u002Fextracts-a\njava -jar \u002Ftmp\u002Ftika-app-after\u002Ftika-app-*.jar  \u003Ccorpus> \u002Ftmp\u002Fextracts-b\n\n# Build and run tika-eval\n.\u002Fmvnw clean install -pl tika-eval\u002Ftika-eval-app -am -Pfast \\\n  -Dmaven.repo.local=$(pwd)\u002F.local_m2_repo\nunzip -qo tika-eval\u002Ftika-eval-app\u002Ftarget\u002Ftika-eval-app-*.zip -d \u002Ftmp\u002Ftika-eval\n\njava -jar \u002Ftmp\u002Ftika-eval\u002Ftika-eval-app-*.jar Compare \\\n  -a \u002Ftmp\u002Fextracts-a -b \u002Ftmp\u002Fextracts-b \\\n  -d \u002Ftmp\u002Feval-db -r -rd \u002Ftmp\u002Feval-reports\n\n# Review\ncat \u002Ftmp\u002Feval-reports\u002Fsummary.md\n```\n",{"name":5,"description":7},"main","9f8b48425fea3630020924f6ae96e98eabb9ce12",".skills\u002Fdevs\u002Ftika-eval-compare",1037,"https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika","https:\u002F\u002Ftika.apache.org\u002F","https:\u002F\u002Fgithub.com\u002Fapache","https:\u002F\u002Fwww.apache.org\u002F","2026-11-13 03:03:34",{"source":8,"skillName":5,"commands":46,"prompt":56,"zipAvailable":57},[47,50,53],{"tool":48,"command":49},"npx","npx skills add apache\u002Ftika --skill tika-eval-compare",{"tool":51,"command":52},"bunx","bunx skills add apache\u002Ftika --skill tika-eval-compare",{"tool":54,"command":55},"pnpm","pnpm dlx skills add apache\u002Ftika --skill tika-eval-compare","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fdevs\u002Ftika-eval-compare\u002FSKILL.md",true,[59],{"path":60,"resourceType":61,"resourceTypeDesc":62,"sizeBytes":63,"sourceHtmlUrl":64},"run-batch.sh","SCRIPT","脚本",8092,"https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fdevs\u002Ftika-eval-compare\u002Frun-batch.sh",[66,106,132,159,184,208],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":83,"publishedAt":31},"01M6ACFKZR081Z7QX5XB1ZA2R2","file-to-markdown","Turn almost any file into Markdown plus metadata — PDF, Office, HTML, email, archives, images, audio\u002Fvideo, 1000+ formats — powered by Apache Tika, either via the tika-app CLI (zero setup, one file) or a running tika-server (curl, warm process, many calls). Leads with rmeta (structured, embedded-item-aware output) as the default operation rather than flat concatenated text, since you can't tell whether a file has embedded content from its extension. Covers metadata-only triage, type and language detection, OCR, and output-size discipline for agent context. Use whenever a task involves reading the content of a file whose format you don't want to hand-parse.","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fusers\u002Ffile-to-markdown\u002FSKILL.md",[72,77,80],{"categoryNo":73,"categoryName":74,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",{"categoryNo":78,"categoryName":79,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":81,"categoryName":82,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000023","Skill与智能体开发",[84,85,86,87,88,89,90,9,24,91,19,92,93,94,95,96,97,98,99,100,101,102,103,104,105],"file to markdown","file","markdown","PDF","cli","triage","setup","metadata","server","ocr","Video","Audio","text","discipline","only","images","default","either","app","you","hand","parse",{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":116,"publishedAt":31},"01M6ACFHJR3612ZE6RNX1P5WG9","file-to-markdown-docker","Run Apache Tika as a Docker container when you need guaranteed OCR (scanned PDFs, images) or geospatial raster support with zero local install — `apache\u002Ftika:\u003Cversion>-full` bundles Tesseract, GDAL, ImageMagick, and fonts. Also covers the minimal image, port\u002Fvolume\u002Fmemory conventions, the path-identity mount gotcha, and how to confirm OCR actually ran rather than silently returning no text. Powered by Apache Tika. Use when a local `tika-app`\u002F`tika-server` doesn't have Tesseract installed, or you want a disposable, self-contained parsing environment. Companion to the `file-to-markdown` skill, which covers the parsing calls themselves once a server is up.","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fusers\u002Ffile-to-markdown-docker\u002FSKILL.md",[112,113],{"categoryNo":73,"categoryName":74,"taxonomyType":75,"taxonomyTypeDesc":76},{"categoryNo":114,"categoryName":115,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000011","运维与部署",[117,118,84,85,86,119,120,121,122,123,9,19,93,96,124,99,125,126,127,102,128,103,129,130,131],"docker","file to markdown docker","image","memory","support","identity","run","up","self","volume","local","how to","fonts","which","when",{"skillNo":133,"name":134,"nameZh":6,"description":135,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":136,"categories":137,"keywords":139,"publishedAt":31},"01M6ACFE1684DE184VE0D7PQVY","file-forensics","Examine what a file claims about itself and what it actually contains — powered by Apache Tika. True content-based type detection (extensions lie), provenance claims (authors, dates, creating application), revision and tamper signals (PDF incremental updates, tracked changes, hidden slides, zip integrity), hidden and embedded content (attachments, macros), risk indicators (PDF JavaScript actions, encryption), and content digests. Evidence gathering, not verdicts: Tika reports what the file asserts and what parsing observed; it does not attribute authorship or validate signatures. Use for triaging suspicious files, provenance questions, e-discovery-style review, or \"is this file what it claims to be.\"","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fusers\u002Ffile-forensics\u002FSKILL.md",[138],{"categoryNo":73,"categoryName":74,"taxonomyType":75,"taxonomyTypeDesc":76},[140,85,141,87,142,143,144,145,9,24,19,146,147,148,149,150,151,152,153,154,155,156,157,158],"file forensics","forensics","review","suspicious","javascript","validate","actions","encryption","risk","integrity","evidence","reports","files","signatures","signals","revision","zip","updates","claims",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":165,"publishedAt":31},"01M6ACFB92SPADF0FY4490B847","update-site-for-release","Update\u002Fpublish the Apache Tika website (tika-site SVN repo) for a release — step 17 of the Release Process. Handles the 4.x track (Changes page + aggregate javadoc + Antora docs branch) vs the 3.x maintenance track (full per-version apt docs + javadoc). Use for \"update the site\", \"publish the site for X.Y.Z\", \"the website part of the release\".","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fdevs\u002Fupdate-site-for-release\u002FSKILL.md",[],[166,167,168,169,30,170,171,9,19,172,173,174,175,176,177,26,178,179,180,181,182,183],"update","release","update site for release","site","docs","release process","website","apt","publish","page","branch","repo","version","maintenance","full","svn","changes","track",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":196,"publishedAt":31},"01M6ACF9G52NYHXQ0BVSJ3VH6E","tika-eval-h2-query","Query the tika-eval H2 database directly for counts and joins the canned reports do not compute — connection gotchas, key tables, example queries. Use when the xlsx\u002Fsummary.md reports are not enough.","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fdevs\u002Ftika-eval-h2-query\u002FSKILL.md",[190,193],{"categoryNo":191,"categoryName":192,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000004","电子表格",{"categoryNo":194,"categoryName":195,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000010","后端与接口",[19,197,21,198,199,200,26,201,151,202,203,204,205,206,207,30,131],"tika eval h2 query","query","xlsx","database","example","queries","do","summary","compute","connection","tables",{"skillNo":209,"name":210,"nameZh":6,"description":211,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":212,"categories":213,"keywords":214,"publishedAt":31},"01M6ACF7V56CCWHNVR614VBNX5","tika-eval-encoding-regression","Condensed tika-eval pattern for charset-detector regression hunts (\"A picks encoding X, B picks Y\") using one build and two configs — encoding-pair flip queries, OOV\u002Flanguageness\u002FFFFD signals, per-file detector attribution.","https:\u002F\u002Fgithub.com\u002Fapache\u002Ftika\u002Fblob\u002Fmain\u002F.skills\u002Fdevs\u002Ftika-eval-encoding-regression\u002FSKILL.md",[],[19,215,25,216,21,85,217,218,202,219,220,154,221,30],"tika eval encoding regression","regression","attribution","pair","build","pattern","detector"]