herbarium-labels 0.1.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. herbarium_labels-0.1.0a2/.env.example +6 -0
  2. herbarium_labels-0.1.0a2/.github/workflows/publish.yml +66 -0
  3. herbarium_labels-0.1.0a2/.github/workflows/tests.yml +42 -0
  4. herbarium_labels-0.1.0a2/.gitignore +40 -0
  5. herbarium_labels-0.1.0a2/AGENTS.md +51 -0
  6. herbarium_labels-0.1.0a2/CHANGELOG.md +28 -0
  7. herbarium_labels-0.1.0a2/CONTRIBUTING.md +21 -0
  8. herbarium_labels-0.1.0a2/HANDOFF.md +69 -0
  9. herbarium_labels-0.1.0a2/LICENSE +21 -0
  10. herbarium_labels-0.1.0a2/MANIFEST.in +25 -0
  11. herbarium_labels-0.1.0a2/MANIFEST.sha256 +61 -0
  12. herbarium_labels-0.1.0a2/NOTICE.md +10 -0
  13. herbarium_labels-0.1.0a2/PKG-INFO +135 -0
  14. herbarium_labels-0.1.0a2/PLAN.md +695 -0
  15. herbarium_labels-0.1.0a2/README.md +99 -0
  16. herbarium_labels-0.1.0a2/SECURITY.md +9 -0
  17. herbarium_labels-0.1.0a2/VALIDATION.md +38 -0
  18. herbarium_labels-0.1.0a2/docs/ARCHITECTURE.md +41 -0
  19. herbarium_labels-0.1.0a2/docs/EVIDENCE.md +19 -0
  20. herbarium_labels-0.1.0a2/docs/HANDWRITING.md +16 -0
  21. herbarium_labels-0.1.0a2/docs/MIGRATION.md +53 -0
  22. herbarium_labels-0.1.0a2/docs/PROMPT_CHANGES.md +39 -0
  23. herbarium_labels-0.1.0a2/docs/PUBLISHING.md +129 -0
  24. herbarium_labels-0.1.0a2/docs/TESTING.md +123 -0
  25. herbarium_labels-0.1.0a2/docs/WORKFLOW.md +45 -0
  26. herbarium_labels-0.1.0a2/docs/diagrams/architecture.mmd +19 -0
  27. herbarium_labels-0.1.0a2/docs/diagrams/architecture.svg +82 -0
  28. herbarium_labels-0.1.0a2/docs/diagrams/workflow.mmd +27 -0
  29. herbarium_labels-0.1.0a2/docs/diagrams/workflow.svg +85 -0
  30. herbarium_labels-0.1.0a2/examples/synthetic/README.md +25 -0
  31. herbarium_labels-0.1.0a2/examples/synthetic/input/synthetic.jpg +0 -0
  32. herbarium_labels-0.1.0a2/examples/synthetic/output/images/synthetic.jpg +0 -0
  33. herbarium_labels-0.1.0a2/examples/synthetic/output/json/synthetic.json +36 -0
  34. herbarium_labels-0.1.0a2/examples/synthetic/output/review.pdf +97 -0
  35. herbarium_labels-0.1.0a2/examples/synthetic/output/thumbnails/synthetic.jpg +0 -0
  36. herbarium_labels-0.1.0a2/examples/synthetic/output/transcriptions.xlsx +0 -0
  37. herbarium_labels-0.1.0a2/examples/synthetic/review-preview.png +0 -0
  38. herbarium_labels-0.1.0a2/examples/synthetic/sheet_reading.json +29 -0
  39. herbarium_labels-0.1.0a2/examples/synthetic/verification.json +18 -0
  40. herbarium_labels-0.1.0a2/library/catalog.json +40 -0
  41. herbarium_labels-0.1.0a2/library/profiles/alphonse-luisier.json +8 -0
  42. herbarium_labels-0.1.0a2/library/profiles/carlos-zimmermann.json +8 -0
  43. herbarium_labels-0.1.0a2/library/profiles/frere-sennen.json +8 -0
  44. herbarium_labels-0.1.0a2/library/profiles/goncalo-sampaio.json +8 -0
  45. herbarium_labels-0.1.0a2/library/schemas/catalog.schema.json +257 -0
  46. herbarium_labels-0.1.0a2/pyproject.toml +40 -0
  47. herbarium_labels-0.1.0a2/requirements-dev.in +6 -0
  48. herbarium_labels-0.1.0a2/requirements-dev.lock +814 -0
  49. herbarium_labels-0.1.0a2/requirements.lock +683 -0
  50. herbarium_labels-0.1.0a2/setup.cfg +4 -0
  51. herbarium_labels-0.1.0a2/src/herbarium_labels/__init__.py +2 -0
  52. herbarium_labels-0.1.0a2/src/herbarium_labels/__main__.py +3 -0
  53. herbarium_labels-0.1.0a2/src/herbarium_labels/bryophytes.py +419 -0
  54. herbarium_labels-0.1.0a2/src/herbarium_labels/cache.py +71 -0
  55. herbarium_labels-0.1.0a2/src/herbarium_labels/catalog.py +103 -0
  56. herbarium_labels-0.1.0a2/src/herbarium_labels/demo.py +107 -0
  57. herbarium_labels-0.1.0a2/src/herbarium_labels/legacy.py +50 -0
  58. herbarium_labels-0.1.0a2/src/herbarium_labels/sennen.py +195 -0
  59. herbarium_labels-0.1.0a2/src/herbarium_labels.egg-info/PKG-INFO +135 -0
  60. herbarium_labels-0.1.0a2/src/herbarium_labels.egg-info/SOURCES.txt +68 -0
  61. herbarium_labels-0.1.0a2/src/herbarium_labels.egg-info/dependency_links.txt +1 -0
  62. herbarium_labels-0.1.0a2/src/herbarium_labels.egg-info/entry_points.txt +2 -0
  63. herbarium_labels-0.1.0a2/src/herbarium_labels.egg-info/requires.txt +18 -0
  64. herbarium_labels-0.1.0a2/src/herbarium_labels.egg-info/top_level.txt +1 -0
  65. herbarium_labels-0.1.0a2/tests/test_cache.py +197 -0
  66. herbarium_labels-0.1.0a2/tests/test_core.py +139 -0
  67. herbarium_labels-0.1.0a2/tests/test_distribution.py +58 -0
  68. herbarium_labels-0.1.0a2/tests/test_english_api.py +155 -0
  69. herbarium_labels-0.1.0a2/tests/test_sdk.py +69 -0
  70. herbarium_labels-0.1.0a2/tools/check_dist.py +121 -0
@@ -0,0 +1,6 @@
1
+ # Reference only; this file is not loaded automatically.
2
+ ANTHROPIC_API_KEY=
3
+ TRANSCRIPTION_MODEL=claude-haiku-5-5
4
+ # USD per million tokens; fill in only with verified rates.
5
+ TRANSCRIPTION_PRICE_IN=
6
+ TRANSCRIPTION_PRICE_OUT=
@@ -0,0 +1,66 @@
1
+ name: Publish reviewed distributions
2
+ on:
3
+ workflow_dispatch:
4
+ inputs:
5
+ target:
6
+ description: Package index (validate on TestPyPI first)
7
+ required: true
8
+ type: choice
9
+ default: testpypi
10
+ options:
11
+ - testpypi
12
+ - pypi
13
+ permissions:
14
+ contents: read
15
+ jobs:
16
+ build:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: '3.12'
23
+ - name: Require a tag matching the package version
24
+ run: |
25
+ python - <<'PY'
26
+ import os, tomllib
27
+ from pathlib import Path
28
+ version = tomllib.loads(Path('pyproject.toml').read_text())['project']['version']
29
+ expected = 'refs/tags/v' + version
30
+ if os.environ['GITHUB_REF'] != expected:
31
+ raise SystemExit('Run this workflow on the release tag: ' + expected)
32
+ PY
33
+ - run: python -m pip install --require-hashes -r requirements-dev.lock
34
+ - run: python -m build --no-isolation
35
+ - run: python -m twine check --strict dist/*
36
+ - run: python tools/check_dist.py dist/*
37
+ - name: Test the actual wheel
38
+ run: |
39
+ python -m venv .wheel-venv
40
+ .wheel-venv/bin/python -m pip install --require-hashes -r requirements.lock
41
+ .wheel-venv/bin/python -m pip install --no-deps dist/*.whl
42
+ .wheel-venv/bin/python -m unittest discover -s tests -v
43
+ - uses: actions/upload-artifact@v4
44
+ with:
45
+ name: release-distributions
46
+ path: dist/*
47
+ if-no-files-found: error
48
+ publish:
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ environment: ${{ inputs.target }}
52
+ permissions:
53
+ id-token: write
54
+ steps:
55
+ - uses: actions/download-artifact@v4
56
+ with:
57
+ name: release-distributions
58
+ path: dist/
59
+ - name: Publish to TestPyPI
60
+ if: inputs.target == 'testpypi'
61
+ uses: pypa/gh-action-pypi-publish@release/v1
62
+ with:
63
+ repository-url: https://test.pypi.org/legacy/
64
+ - name: Publish to PyPI
65
+ if: inputs.target == 'pypi'
66
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,42 @@
1
+ name: Offline tests and distributions
2
+ on: [push, pull_request]
3
+ permissions:
4
+ contents: read
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os: [ubuntu-latest, windows-latest]
11
+ python: ['3.11', '3.12', '3.13', '3.14']
12
+ runs-on: ${{ matrix.os }}
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: ${{ matrix.python }}
18
+ - run: python -m pip install --require-hashes -r requirements.lock
19
+ - run: python -m pip install --no-deps -e .
20
+ - run: python -m unittest discover -s tests -v
21
+ distribution:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: '3.12'
28
+ - run: python -m pip install --require-hashes -r requirements-dev.lock
29
+ - run: python -m build --no-isolation
30
+ - run: python -m twine check --strict dist/*
31
+ - run: python tools/check_dist.py dist/*
32
+ - name: Install and test the wheel
33
+ run: |
34
+ python -m venv .wheel-venv
35
+ .wheel-venv/bin/python -m pip install --require-hashes -r requirements.lock
36
+ .wheel-venv/bin/python -m pip install --no-deps dist/*.whl
37
+ .wheel-venv/bin/python -m unittest discover -s tests -v
38
+ - uses: actions/upload-artifact@v4
39
+ with:
40
+ name: distributions
41
+ path: dist/*
42
+ if-no-files-found: error
@@ -0,0 +1,40 @@
1
+ .env
2
+ .env.*
3
+ !.env.example
4
+ .secrets/
5
+ *.pem
6
+ *.key
7
+ __pycache__/
8
+ *.py[cod]
9
+ .venv/
10
+ .wheel-venv/
11
+ build/
12
+ dist/
13
+ *.egg-info/
14
+ data/
15
+ runs/
16
+ transcricao/
17
+ *.zip
18
+ *.partial
19
+
20
+ # Local verification artifacts
21
+ demo-output/
22
+ .testpypi-venv/
23
+ transcription/
24
+ input/
25
+ output/
26
+ demo-output*/
27
+ wheel-demo/
28
+ testpypi-wheel/
29
+
30
+ # Explicitly public synthetic fixtures (not collection images).
31
+ !examples/
32
+ !examples/synthetic/
33
+ !examples/synthetic/input/
34
+ !examples/synthetic/input/synthetic.jpg
35
+ !examples/synthetic/output/
36
+ !examples/synthetic/output/**
37
+ !examples/synthetic/review-preview.png
38
+ !examples/synthetic/README.md
39
+ !examples/synthetic/sheet_reading.json
40
+ !examples/synthetic/verification.json
@@ -0,0 +1,51 @@
1
+ # Repository Guidelines
2
+
3
+ These instructions apply throughout the repository. Explicit user requests take precedence over implementation guidance here. Documentation must be written in English; use English code and interfaces. Preserve literal evidence in its source language. English prompt translations and API changes are documented in `docs/PROMPT_CHANGES.md` and `docs/MIGRATION.md`; their live scientific effects remain to be evaluated.
4
+
5
+ ## Context and session startup
6
+
7
+ - Read `README.md`, `HANDOFF.md`, `VALIDATION.md`, and section 25 of `PLAN.md` before changing code.
8
+ - Existing workflows have been used in research and accepted by researchers. Preserve that evidence and its limits; do not describe the project as unvalidated.
9
+ - Build a useful open library that complements Hespi. Its value does not depend on outperforming another project.
10
+ - Treat `PLAN.md` as an iterative roadmap. Distinguish implemented, proposed, and tested features.
11
+
12
+ ## Code and compatibility
13
+
14
+ - Python 3.11+; source in `src/herbarium_labels/`, tests in `tests/`, catalogue in `library/`.
15
+ - The English API uses `bryophytes.py` with `transcribe`, `report`, `batch`, and offline `demo`. Preserve legacy JSON reading, original literal text, Excel/PDF/ZIP integrity, and the migration rules in `docs/MIGRATION.md`.
16
+ - `sennen.py` processes a full sheet and lower crop with multiple labels. Do not replace that capability with a cropped-input assumption.
17
+ - `catalog.py` validates people, sources, samples, and attributions; it does not identify writers or inject examples into prompts.
18
+ - Keep changes small, documented, and testable. Preserve scientific safeguards. Further prompt/contract changes require justification and validation; offline tests do not establish paleographic equivalence.
19
+ - Prioritize content/configuration caching and per-task aggregation before increasing parallelism. Preserve originals and human corrections.
20
+
21
+ ## Scientific fidelity and handwriting
22
+
23
+ - Preserve literal spelling, lines, and uncertainties. Normalization and interpretation are separate layers.
24
+ - Do not fill missing data or confuse institution, collector, determiner, taxonomic author, and writer.
25
+ - Separate person, observed hand, sample, and attribution. A label can contain several hands.
26
+ - Jesuit, curator, and researcher are documentary metadata, not visual handwriting classes.
27
+ - Keep Sampaio, Sennen, Zimmermann, and Luisier profiles as drafts until curated. Do not invent aliases, biographies, examples, or attributions.
28
+ - Brotéria and similar references require verifiable editions/locations. A list of people does not establish label authorship.
29
+ - Confirmation requires a sample, source, evidence, and reviewer. Automatic similarity is not confirmed authorship.
30
+
31
+ ## Security and data
32
+
33
+ - Never commit `.env`, keys, uploads, backups, databases, or private results. Read API keys from the environment and never log their values.
34
+ - The code's MIT license does not license images or collections. Check local, API, and redistribution permissions separately.
35
+ - Work in this library. TSennen/Taxa references are read-only context; do not change research deployments, live databases, or containers without explicit scope.
36
+ - Ordinary tests must be offline and synthetic. Paid calls require authorized images and an agreed budget.
37
+ - Never execute model output as code, a formula, or a path. Preserve escaping, path validation, and Excel text cells.
38
+
39
+ ## Verification and publication
40
+
41
+ - Run `python -m unittest discover -s tests -v` after relevant changes; use `PYTHONPATH=src` when the package is not installed.
42
+ - Check catalogue/schema consistency after changes. Visually inspect reports after layout changes. Use a simulated provider before live integration.
43
+ - Record results/limits in `VALIDATION.md`, changes in `CHANGELOG.md`, and concrete next steps in `HANDOFF.md`.
44
+ - Preserve `LICENSE` and `NOTICE.md`. Before release, review dependency locks, secrets, distribution contents, and data permissions.
45
+ - Follow `docs/PUBLISHING.md` and existing publication authorization. Resolve only genuinely missing details; do not invent an account, repository URL, authors, or CI results.
46
+
47
+ ## Maintainer control
48
+
49
+ Only the maintainer may apply repository changes, commit, or push unless they explicitly authorize that action. Do not interpret a request for advice or preparation as permission to apply changes. Prepare remaining proposals as patches in an isolated temporary directory and test there; leave the working tree and Git state unchanged. SSH is unnecessary when the maintainer handles remote operations.
50
+
51
+ Deliver requested review images and reports inside the repository under `examples/synthetic/` and `docs/diagrams/`; temporary-directory links alone are insufficient. Synthetic examples must be identified as such, with no private collection material.
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## Unreleased — English API proposal 0.1.0a2 — 2026-10-25
4
+
5
+ - Renamed public code/interfaces to English: `bryophytes.py`, `BryophyteReading`, `SheetReading`, CLI commands/options, JSON fields, catalogue statuses, messages, and report artifacts. This intentionally breaks old Python imports/command names; schema version is `0.2.0`.
6
+ - Added read-only legacy JSON/status adapters with conflict detection; preserve original file bytes, source-language literal text, uncertain dates, and human corrections.
7
+ - Translated prompt instructions and documented hashes, editorial-marker changes, and the need for live scientific comparison. Original-language examples remain unchanged.
8
+ - Added offline `demo` with synthetic deliveries and verification flags, thirteen regressions (42 tests total), a testing guide, migration guide, architecture overview, and workflow diagrams in Mermaid/SVG.
9
+ - Renamed public documentation files to English and retained maintainer-only apply/commit/push control.
10
+
11
+
12
+ - Proposed Pillow requirement raised to `>=13,<14` with a regenerated runtime lock after 37 advisory records were reported for Pillow 12.3.0; validated in an isolated review copy before maintainer application.
13
+ - Maintainer-only apply/commit/push policy recorded; remaining changes are supplied as an unapplied patch.
14
+
15
+ - English documentation, contributor instructions, package description, and release guidance, preserving scientific prompts and existing Portuguese CLI/data contracts.
16
+ - Release preparation: separate development-tool lock, explicit source-distribution contents, distribution checks, and manual Trusted Publishing workflow. Remote execution/publication is recorded only when it actually occurs.
17
+ - Bryophyte cache checks SHA-256 of image and configuration before requests. Legacy JSON remains readable in reports; incompatible results require a new output directory and are preserved.
18
+ - Exclusive transcription/batch output lock; atomic writes and valid human corrections preserved. Reports using new JSON reject changed image content.
19
+ - Per-task aggregation, including known usage from incomplete responses; concurrency remains six tasks.
20
+ - Universal runtime dependency lock with hashes and all extras; clean Python 3.12 installation and installed SDK tests using mock HTTP transport without paid calls.
21
+ - Ten regression/SDK tests added to the original fourteen, for 24 offline tests before the release-preparation checks. Scientific prompts and Sennen full-sheet processing preserved.
22
+
23
+ ## 0.1.0a1 — local preparation
24
+
25
+ - Extracted bryophyte and Sennen workflows.
26
+ - Environment configuration and separation from the private project.
27
+ - Documentary schemas for people, hands, and attributions; draft profiles.
28
+ - Offline tests and an iterative roadmap.
@@ -0,0 +1,21 @@
1
+ # Contributing
2
+
3
+ ## Set up and test
4
+
5
+ Use Python 3.11+ and a virtual environment. Follow the installation commands in [README.md](README.md). Run `python -m unittest discover -s tests -v`; if the package is not installed, set `PYTHONPATH=src`. Tests must use synthetic data and simulated providers. Live calls require image permissions and an agreed budget.
6
+
7
+ Before release-related work, install `requirements-dev.lock` with `--require-hashes` and follow [the release guide](docs/PUBLISHING.md). No formatter, linter, or numerical coverage threshold is currently enforced. Use four-space indentation, `snake_case` functions/modules, `PascalCase` models, and focused regression tests for behavior changes.
8
+
9
+ ## Propose a change
10
+
11
+ Explain the use case, current behavior, and expected result. Keep changes small. Use the English commands/models and preserve legacy JSON reading, source-language evidence, and full-sheet Sennen processing. Consult docs/MIGRATION.md before changing a public interface. Changes to prompts or schemas require justification, a changelog entry, and evaluation of their effects. Read [AGENTS.md](AGENTS.md) for repository-specific invariants.
12
+
13
+ Use concise imperative commit subjects, such as `Validate cache identity before transcription`. This is guidance for new commits: the local repository has no established commit-history convention yet.
14
+
15
+ A pull request should describe the behavior change, relevant issue if one exists, tests run and their results, and known limitations. Include before/after report samples when layout changes; use synthetic data and document visual inspection. Update `CHANGELOG.md`, `VALIDATION.md`, and `HANDOFF.md` where relevant.
16
+
17
+ ## Scientific and documentary contributions
18
+
19
+ Write documentation in English. Preserve original-language names, quotations, and literal transcriptions. Public identifiers, commands, messages, and documentation are English. Separate interpretation from literal evidence. A profile requires sources; a transcription review does not establish handwriting authorship. Draft profiles stay drafts until curated.
20
+
21
+ Use only synthetic or authorized data. Do not submit credentials, private research results, or restricted images. The MIT code license does not grant image/data rights. When reporting quality, include versions, datasets, denominators, methods, and limitations; see [research evidence](docs/EVIDENCE.md).
@@ -0,0 +1,69 @@
1
+ # Development handoff
2
+
3
+ Updated: 2026-10-25. Version: local alpha `0.1.0a2`. This document distinguishes the working tree from the remaining unapplied release proposal.
4
+
5
+ ## Maintainer intent and control
6
+
7
+ Share a useful open library built from research workflows, complementary to Hespi. Research use and acceptance by researchers were reported by the maintainer; preserve that evidence and its limits. English documentation is required for international reuse.
8
+
9
+ The selected source destination is `MattyDroidX/herbarium-labels`; the SSH remote was configured at the maintainer's request. The intended package route is TestPyPI followed by PyPI.
10
+
11
+ **Only the maintainer applies changes, commits, or pushes unless explicitly delegated.** This instruction also covers repository patch application. After this restriction was given, remaining edits/builds were prepared in a temporary review copy. Do not modify the working tree or Git state to finish the proposal without authorization. No commit, push, tag, or package upload has been made by the assistant. SSH access is unnecessary for preparation when the maintainer handles remote operations.
12
+
13
+ ## Provenance and current implementation
14
+
15
+ See `NOTICE.md` for original TSennen/Taxa source references and hashes. The private application, deployments, databases, uploads, backups, and research outputs must remain separate. Personal workstation paths are not required in public documentation.
16
+
17
+ - `src/herbarium_labels/bryophytes.py`: `transcribe`, `report`, `batch`, `demo`; Claude/Pydantic, per-image JSON, Excel, optional PDF, ZIP.
18
+ - `src/herbarium_labels/sennen.py`: full sheet plus lower crop, multiple labels, structured fields, advisory suggestions; no database writes or automatic human approval.
19
+ - Environment key/model/prices; unknown cost without configured tariffs. Optional HEIF dependency, rotation/path/collision checks, atomic JSON.
20
+ - `catalog.py`: sources, people, hand samples, and attributions with reference/evidence checks.
21
+ - `library/`: catalogue/schema and four draft people, without invented roles, aliases, samples, or confirmed attributions.
22
+ - Content/configuration cache checks before requests; incompatible/legacy JSON requires a fresh output directory while remaining readable by reports.
23
+ - Per-task results and central aggregation, known incomplete-response usage, six workers, exclusive transcription/batch output lock.
24
+ - Runtime lock with hashes and all extras. Extraction originally preserved the prompts. The English API proposal now translates them deliberately; equivalent live scientific quality remains untested.
25
+
26
+ ## English release preparation
27
+
28
+ Documentation, package description/docstrings, contributor instructions, release guide, source manifest, distribution checker/tests, and CI/publication workflows were prepared. The source tree and earlier local artifacts were modified before the maintainer-only application rule. Remaining translations, dependency correction, and refreshed manifests are supplied as an **unapplied patch** against that tree.
29
+
30
+ The review proposal changes Pillow from `>=11.3,<13` to `>=13,<14` after the advisory check reported 37 known vulnerability records for locked Pillow 12.3.0. It regenerates the runtime lock and locks the build backend with development tools. The preceding documentation-only proposal passed 29 offline tests on both source and installed wheel, metadata/content/license checks, and advisory scans with no known vulnerabilities reported for the current Linux/Python 3.12 dependency selection. Details are in `VALIDATION.md`; these changes must not be described as already applied to the maintainer's working tree.
31
+
32
+ ## Verification and practical evidence
33
+
34
+ Original extraction: 14 synthetic/offline tests passed. Cache increment: 24 passed, including installed Anthropic 1.17.0 with mock HTTP transport. Release checks added five distribution regressions, for 29 tests before testing the remaining proposal.
35
+
36
+ The real research workbook was inspected read-only: 549 records per sheet, 549 embedded images, literal text throughout, 306 records with uncertainty, no formula cells, valid local links. This is not a complete paleographic review and does not mean 306 errors. Private results are not packaged. Historical agreement/cost figures and limits remain in `docs/EVIDENCE.md`.
37
+
38
+ See `VALIDATION.md` for current local commands, package-installation checks, advisory findings, and outstanding remote checks. Do not invent successful CI or uploaded package URLs.
39
+
40
+ ## Known limitations and future work
41
+
42
+ - No example retrieval, profile injection, automatic handwriting attribution, generic segmentation, native review events/import, or canonical v2 integration.
43
+ - Filename-stem collisions remain blocked. Occurrence IDs, manifests, cross-occurrence deduplication, and automatic candidate revisions remain proposed.
44
+ - Current interfaces are English: `bryophytes.py`, `transcribe`/`report`/`batch`/`demo`, English fields/reports/statuses. Legacy data is read in memory; old imports/commands are renamed. Preserve literal evidence in its source language. Review `docs/MIGRATION.md` and `docs/PROMPT_CHANGES.md`.
45
+ - Use fresh output after image/configuration/codec changes. Legacy identity is not inferred. Stale locks require checking for an active process before removal; standalone reports do not acquire a write lock.
46
+ - Lost responses may require repeat paid requests. Failure without usage cannot prove zero billing.
47
+ - PDF Unicode/long-text behavior and complete visual report inspection remain pending. No layout change was introduced in the release documentation work.
48
+ - Live provider/model execution and real HEIF decoding remain unverified in the extracted alpha; paid integration needs permitted images and a defined budget.
49
+ - Remote matrix runs, TestPyPI/PyPI account setup, package-name availability, private reporting configuration, and publication still need actual execution.
50
+
51
+ ## Next steps
52
+
53
+ 1. Maintainer reviews/applies the remaining patch; confirm manifest hashes and rerun the documented checks on the applied tree.
54
+ 2. Maintainer reviews the public-file allowlist and actual Git author identity, then commits/pushes without overwriting existing remote history.
55
+ 3. Inspect actual Linux/Windows and Python-version CI results.
56
+ 4. Configure independent TestPyPI/PyPI Trusted Publishers for `publish.yml` and environments `testpypi`/`pypi`.
57
+ 5. Maintainer tags the reviewed version, publishes to TestPyPI, and verifies the uploaded wheel in a clean environment before triggering PyPI.
58
+ 6. Record real URLs, hashes, and results. Update the verified private reporting route.
59
+ 7. Continue the scientific roadmap: long/Unicode reports with visual inspection; permitted live integration; sourced, rights-cleared handwriting curation.
60
+
61
+ Do not change the private research environment to prepare this library.
62
+
63
+ ## English API continuation
64
+
65
+ The cumulative proposal now includes the preceding English-release preparation plus the English code/API migration. Apply only the latest cumulative patch against the unchanged working tree; do not apply it on top of the earlier patch. Version is `0.1.0a2`, schemas `0.2.0`; documentation filenames are English. No working-tree or Git mutation occurred during this continuation.
66
+
67
+ New regression checks cover legacy alias conflicts, preserved literal/JSON bytes, English schemas/statuses/CLI, legacy locks, and the offline demo. The demo creates inspectable files and verification flags; it is not a paleographic accuracy experiment. Architecture/workflow SVG and Mermaid files are in `docs/diagrams/`; use `docs/TESTING.md` to reproduce the checks.
68
+
69
+ Final local verification for this continuation: 42 tests passed against source and the installed English wheel in a fresh Python 3.12 environment. Console entry point and module import/version were checked; wheel/source metadata/allowlist checks passed. Short synthetic PDF and both SVG diagrams were rendered and visually inspected. Prompts were deliberately translated and require a separate authorized scientific comparison; no live call was performed.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TSennen contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,25 @@
1
+ include LICENSE NOTICE.md README.md CHANGELOG.md CONTRIBUTING.md SECURITY.md
2
+ include AGENTS.md HANDOFF.md PLAN.md VALIDATION.md
3
+ include pyproject.toml MANIFEST.in MANIFEST.sha256
4
+ include requirements.lock requirements-dev.in requirements-dev.lock
5
+ include .env.example .gitignore
6
+ recursive-include src/herbarium_labels *.py
7
+ recursive-include tests *.py
8
+ recursive-include tools *.py
9
+ recursive-include docs *.md *.mmd *.svg
10
+ recursive-include library *.json
11
+ recursive-include .github/workflows *.yml
12
+ global-exclude *.pyc *.pyo *.partial
13
+ prune .venv
14
+ prune .git
15
+ prune build
16
+ prune dist
17
+ prune data
18
+ prune runs
19
+ prune transcription
20
+ # Exclude legacy delivery directories as well.
21
+ prune transcricao
22
+
23
+ # Public synthetic demonstration assets only.
24
+ recursive-include examples/synthetic *.md *.jpg *.png *.pdf *.xlsx *.json
25
+ prune review
@@ -0,0 +1,61 @@
1
+ 2d9c8cc7f1cf4f9400b512c198a29a24f9416b87312c8e54692935b178e1e032 .env.example
2
+ b185065a55e41da7718b95da3a9e285934e823fbcd4830e9da2c88359e0164d0 .github/workflows/publish.yml
3
+ bc2f26644e776453c64ce944f1f1a79c71ba38c0d632392ea19c03ae62b18396 .github/workflows/tests.yml
4
+ 5352eac374e50f302006d5b6660003b3a46c9eff611cfca37b0bbe32902f66a6 .gitignore
5
+ 50ca332b781fdd9ab098af06e50bbc6280e9c7110c091f649b393e0906a6deea AGENTS.md
6
+ 8732556d779662e374427784963f6623a09e364a72f5a43633436a1c5af294c4 CHANGELOG.md
7
+ bf976e5cf53fc72e4e57675ebf4b9497e8cc710f7bc29a8dad0b29350c199a6c CONTRIBUTING.md
8
+ a0896eee15a683292d8d95d08e1d5213c362096e218f9ba77d17b9995a4dd628 HANDOFF.md
9
+ 87b4fbde824f5f414e45c5f6108b35a5b360bffca826d9dc8724ef4666a77490 LICENSE
10
+ c7aca297579144770d5273e6b4d572b51ef7551298056bf2816ad3bbb7501f63 MANIFEST.in
11
+ 18be0de7a5fbaab80b6940fa4e65c27331b1bdb4c0638f12f4aa6ef526b91c95 NOTICE.md
12
+ 18fe55ac09a9efc790f7247ffbd69604c6db20e5190bc45b7b68e60d92165151 PLAN.md
13
+ 1766b784df8becd4d2f8b9f1d8e12301a4c7cb98753a0e553c0bd7466c6f714b README.md
14
+ 5b44e8ba84c350f12bedca21001fb14ddb342c4699bdd90380309e853d3a0783 SECURITY.md
15
+ 188500e684b41fa88d448919a2f50b2d19456ea1667b2b03a290f111c0fbdb2c VALIDATION.md
16
+ 6f7f9e0e95e61cb0c9367ef513e14d127e53b78d1e0ef0868a299df01e656887 docs/ARCHITECTURE.md
17
+ f169bf3452b2b199a168a71d3b5dadb05debeeead968ab2af5df24d08cbd345d docs/EVIDENCE.md
18
+ c8369d0d3344e94cf0aaf5b3b98c4b071f5b7fe806e5d437516ddf8636706acf docs/HANDWRITING.md
19
+ 917219a4136d044b85e640701a1d1477f4ef9ac05bdceb05216c7007350c10e2 docs/MIGRATION.md
20
+ 66356991f2e69dd450c3fca553082455d25990e314c4c9cb1fce99cb55da8b2e docs/PROMPT_CHANGES.md
21
+ 592a489ce4caae6530e71bff3eb84b3cc3620af0be36f91b503953807b0bd174 docs/PUBLISHING.md
22
+ 41d1d3629ebdc57e5fb48f1de67c93f52db24ee13daf86aff9861df938134394 docs/TESTING.md
23
+ 629075787e75b2a4f318712c11a920c027c3e41b10c0bfe1d9928c02b0fe9742 docs/WORKFLOW.md
24
+ 9c59b64db69aaeb4d2c29129bdc52a2abf0c7a75e75133075ecd9a6d484f404c docs/diagrams/architecture.mmd
25
+ 44a26b5919de22d61a0110e8a4ace53ea4ab922a13c279ccff036d5b9b945279 docs/diagrams/architecture.svg
26
+ 6680eef6e698ff9e0e370aba2b0c7e487bc521c101888ff6d312e95503ed05d0 docs/diagrams/workflow.mmd
27
+ 3ea663850bc8eb0564672cf3bff9dbab594db513f069b09c3b9958484b77adca docs/diagrams/workflow.svg
28
+ 5b407ceb5691965be5a56c068e39b5af5810dffa4564f6a0dadf7d0236f13076 examples/synthetic/README.md
29
+ 2a605e637d402bc44d4f8117ea514ba012f99f24bba1795b64d96ddef69de056 examples/synthetic/input/synthetic.jpg
30
+ f1cc5b57b0011f8177b87f3f0616352224b72531057520bbca572e81df380fe0 examples/synthetic/output/images/synthetic.jpg
31
+ 3369cf57bc4b1af3cdbdf462b1159fce000837cb2cc576dba83fab7318c29d22 examples/synthetic/output/json/synthetic.json
32
+ cac493d2a5ad9b9d8f45ec5274501758ba817f9957cca10dfaf7b85e0913be8d examples/synthetic/output/review.pdf
33
+ 3bc7b1af3f9a884d959e6e75b38996e35703a1956ac898d458e3c9af7057a4c4 examples/synthetic/output/thumbnails/synthetic.jpg
34
+ 09064400d7a30a28f28c5be07cf12023447a96afb9ecadfe2811cfc3ae9fc9e1 examples/synthetic/output/transcriptions.xlsx
35
+ e3cb2a8882e5cdd57bb48cc3807b2ccab7966139bcb41f2e6e7ac3d01a22f3e7 examples/synthetic/review-preview.png
36
+ 042d690468c559ee01c9a55ffe57ca1dc8c8b94d64112dd574b432ef6d60fb05 examples/synthetic/sheet_reading.json
37
+ f26717d8e57324fd2161f5a8320402b2873755014421ca4c25488cfa44f06350 examples/synthetic/verification.json
38
+ e8af59bf26a4acf42dafb13b2c07509558059c0ae059d2255f151cad99e4405a library/catalog.json
39
+ 6e95ee5f4f484aa49cc1547ece497c2b11e6fffa02fea74fdbe6997f648bf215 library/profiles/alphonse-luisier.json
40
+ f2ae1bfa268561f374b269d423ac3c207376d0b3a58db3790bb95df6db394822 library/profiles/carlos-zimmermann.json
41
+ c4264ddeb13c8b83edd9346bd1ca41601aef81da56b872dc1373443be23a282c library/profiles/frere-sennen.json
42
+ 03cbf2f0994a8064ceed8cb36a29fbff06454f232d959d0169653c6f7a006691 library/profiles/goncalo-sampaio.json
43
+ 57bf10ed8d8317c1907b3e5e98ff2d365deb200f866c989e840262c5f9b8e509 library/schemas/catalog.schema.json
44
+ 81ba2f857519e00afc9a374461467f9924e6f083f641793e203290e6ae19f37d pyproject.toml
45
+ 121ada4b3d8652f028b786191454b38229a9e893f26ac11a3920000ae3f57875 requirements-dev.in
46
+ e263bdef1d3e233022de1e9e260c6d69940ce1cc66c8a784dd5fd3b11fa9b8f5 requirements-dev.lock
47
+ f3f7aefa1be0893d0a09ba39f68c2998ef7eb03ab2fc7d0c806b253c45f36c1c requirements.lock
48
+ 047c3266d21009af45efbbade5060d552cc50197516115d90f3dc1aa33fe1bf3 src/herbarium_labels/__init__.py
49
+ f75ffa1398f4ec6a1e17c6e148cab25c51701d1a2497298b94ff9ac17397f549 src/herbarium_labels/__main__.py
50
+ 359a7076344648139ea9de880473da22aa921a9f593aed2f830e55dfac47aeb6 src/herbarium_labels/bryophytes.py
51
+ 9c9d8c110db72701e3d8de4d8087c1eba6c7531c12ce748f50d539167e2a4c29 src/herbarium_labels/cache.py
52
+ 7d648e7b0ab829d108899e8b4d9e23694e6f3ff4c3fe3ce82752853adc776a7c src/herbarium_labels/catalog.py
53
+ 35ac05e730a945a85a3406970b6a53df5af61a0a4e4538f63880a8242201ab99 src/herbarium_labels/demo.py
54
+ ec3a5dce709aaba2f1ec26e0eacd802dd95d169057fce10e96c8635469bebb7b src/herbarium_labels/legacy.py
55
+ 7ac02a25aaa424002b1a174cf21a2f189958b082dfac03aeeded87fcd8cadd47 src/herbarium_labels/sennen.py
56
+ 3f349ad2a207001e3a76a1e7de927eb0768f064407f5f442b9a090ddf54f2b95 tests/test_cache.py
57
+ c3dbbde1f202116784c37f57fe86a99fcdb6e28d645f4ce2823994e902c4602a tests/test_core.py
58
+ 189daf3aa4780bc5194e29e6880755d551b0e0fa7b1ae6fc6beece60d3f5da78 tests/test_distribution.py
59
+ 1746c6b6e45d717e4d2a1a9e82ebd2e731391810f4acb90c9a833e123a60f8d5 tests/test_english_api.py
60
+ c6816d5fe69519c7c0b98c41fd58c469d34a3de6551d4ad421bd99871153b466 tests/test_sdk.py
61
+ 86d24497c7574bda03b8eebd2a99e2b9fe0488e6c9d4ebe428fc24571ae3e586 tools/check_dist.py
@@ -0,0 +1,10 @@
1
+ # Provenance
2
+
3
+ Derived from the local TSennen/Taxa project under the MIT license. The original copyright notice is preserved in `LICENSE`.
4
+
5
+ - `.ocr-teste/cristiana/briofitas.py`, original SHA-256: `EFFDB20DF0EB80C7AAF749D6880D81516D626CEFF4A9202E8985AB5877D8A31C`.
6
+ - `app/transcription.py`, original SHA-256: `5742CE748D59F2CD185692B432EDC0B820708957E1C4D5AEF785A49B90F1B7FF`.
7
+
8
+ Adaptations include packaging, environment-based keys/prices, optional HEIF support, rotation/path/collision validation, atomic JSON writes, content/configuration cache checks, per-task aggregation, output locking, and offline tests. The original extraction preserved scientific prompts and fields. The English API proposal renames fields and translates prompt instructions; the original-language evidence remains unchanged. See `docs/PROMPT_CHANGES.md` for the changes and evaluation limits.
9
+
10
+ No secrets, `.env`, accounts, private Git history, databases, uploads, backups, specimen data, or actual research outputs were copied into the public distribution. The TSennen/Taxa application and deployment remain separate. The software license does not license collection images or research data.
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: herbarium-labels
3
+ Version: 0.1.0a2
4
+ Summary: Botanical label transcription and documented historical handwriting profiles
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/MattyDroidX/herbarium-labels
7
+ Project-URL: Issues, https://github.com/MattyDroidX/herbarium-labels/issues
8
+ Keywords: herbarium,botany,transcription,natural-history,handwriting
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ License-File: NOTICE.md
21
+ Requires-Dist: Pillow<14,>=13
22
+ Requires-Dist: pydantic<3,>=2.11
23
+ Provides-Extra: claude
24
+ Requires-Dist: anthropic<2,>=1.17; extra == "claude"
25
+ Provides-Extra: reports
26
+ Requires-Dist: openpyxl<4,>=3.1.5; extra == "reports"
27
+ Requires-Dist: reportlab<5,>=4.4; extra == "reports"
28
+ Provides-Extra: heif
29
+ Requires-Dist: pillow-heif<2,>=1.1.1; extra == "heif"
30
+ Provides-Extra: all
31
+ Requires-Dist: anthropic<2,>=1.17; extra == "all"
32
+ Requires-Dist: openpyxl<4,>=3.1.5; extra == "all"
33
+ Requires-Dist: reportlab<5,>=4.4; extra == "all"
34
+ Requires-Dist: pillow-heif<2,>=1.1.1; extra == "all"
35
+ Dynamic: license-file
36
+
37
+ # herbarium-labels
38
+
39
+ An open Python library for transcribing botanical labels and producing reviewable JSON, Excel, PDF, and ZIP files. It extracts workflows used in research and TSennen/Taxa, with results reviewed and accepted by researchers according to the maintainer. It complements Hespi and other tools.
40
+
41
+ ## Current scope
42
+
43
+ Proposed English API alpha **0.1.0a2**, prepared as an unapplied patch for maintainer review. Code, commands, schemas, messages, and report interfaces are English; literal label text always retains its source language.
44
+
45
+ - **Bryophytes:** PO/LISU-specific reading, JSON persistence, cache verification, and reports.
46
+ - **Sennen:** full sheet plus lower crop, separate labels and structured fields through a Python API.
47
+ - **Catalogue:** documentary validation of people, hands, samples, sources, and attributions; four uncurated drafts.
48
+ - **Offline demonstration:** synthetic images and simulated responses, with delivery checks.
49
+
50
+ Example retrieval, profile injection, automatic handwriting attribution, generic segmentation, and native review import are future work. English interfaces do not imply general paleographic accuracy for every collection. See [the architecture](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/ARCHITECTURE.md), [workflow](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/WORKFLOW.md), and [roadmap](https://github.com/MattyDroidX/herbarium-labels/blob/main/PLAN.md).
51
+
52
+ ## Install locally
53
+
54
+ Requires Python 3.11+. From the reviewed source tree:
55
+
56
+ ```sh
57
+ python -m venv .venv
58
+ # Linux/macOS:
59
+ . .venv/bin/activate
60
+ # Windows PowerShell: .venv\Scripts\Activate.ps1
61
+ python -m pip install --require-hashes -r requirements.lock
62
+ python -m pip install --no-deps -e .
63
+ ```
64
+
65
+ The lock includes all extras and Pillow 13. An alternative is `python -m pip install -e ".[all]"`, which resolves declared version ranges. Extras: `claude`, `reports`, `heif`, and `all`. Local verification used Linux/Python 3.12; the configured remote matrix is not a claim of completed CI.
66
+
67
+ Source destination: [MattyDroidX/herbarium-labels](https://github.com/MattyDroidX/herbarium-labels). No TestPyPI/PyPI upload is claimed. The wheel contains runtime modules; the repository/source archive also include documentation, tests, tools, and the draft catalogue.
68
+
69
+ ## Try the entire delivery offline
70
+
71
+ No API key, network connection, or real collection images are needed:
72
+
73
+ ```sh
74
+ python -m herbarium_labels demo --output ./demo-output --pdf
75
+ python -m unittest discover -s tests -v
76
+ ```
77
+
78
+ Use a new directory for each demo. Open `demo-output/verification.json`: `all_passed` should be `true`. Inspect `demo-output/input/transcription/transcriptions.xlsx`, `review.pdf`, and `demo-output/input/transcriptions.zip`. The mock supplies known fixture readings; this demonstrates the pipeline, not OCR/paleographic quality. [The testing guide](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/TESTING.md) explains every artifact and check.
79
+
80
+ ## Transcribe authorized images
81
+
82
+ Set `ANTHROPIC_API_KEY` privately in the environment. `.env.example` is a reference, not automatically loaded configuration. `TRANSCRIPTION_MODEL` selects the model; verify availability before a paid run. Optional input/output prices are USD per million tokens; without both, estimated cost is unknown.
83
+
84
+ ```sh
85
+ herbarium-labels transcribe --images ./input --output ./output
86
+ herbarium-labels report --images ./input --output ./output
87
+ herbarium-labels batch --images ./input --pdf
88
+ ```
89
+
90
+ `python -m herbarium_labels` is equivalent. `transcribe` writes readings; `report` creates reports without an API request; `batch` requires a reading for every input, then produces a delivery ZIP. Its fixed output directory is `input/transcription/`; the archive is `input/transcriptions.zip`. Originals are unchanged.
91
+
92
+ Inputs are nonrecursive JPG, JPEG, PNG, HEIC, or HEIF files. HEIF requires its extra. Duplicate stems are rejected. New JSON uses English keys and schema version `0.2.0`. Existing Portuguese JSON is normalized in memory for reading/reporting without changing its bytes. The old Python module/CLI names have been renamed; see [migration](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/MIGRATION.md).
93
+
94
+ ## Resume without replacing human work
95
+
96
+ Cache identity includes image/configuration hashes: prompt, model, schema, transformation, SDK, and codecs. The selected batch is checked before requests. Incompatible or unverifiable existing results stop transcription; preserve them and use a new output directory. Legacy results are not automatically promoted into verified cache.
97
+
98
+ `.transcription.lock` prevents overlapping transcriptions/batches; an existing legacy lock also blocks a new run. Check for an active process before removing stale locks. Standalone `report` must run without another writer. Lost responses may require another paid request; exactly-once execution is not guaranteed.
99
+
100
+ ## Process a Sennen sheet
101
+
102
+ ```python
103
+ from pathlib import Path
104
+ from herbarium_labels.sennen import read_labels
105
+
106
+ reading, usage = read_labels(Path("sheet.jpg").read_bytes())
107
+ for label in reading.labels:
108
+ print(label.origin, label.literal_text)
109
+ ```
110
+
111
+ The collection-specific lower crop complements the full sheet. The function returns suggestions/readings, not database edits or human approval. See [architecture](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/ARCHITECTURE.md) for the independent catalogue and host-review boundaries.
112
+
113
+ ## Validate and build
114
+
115
+ ```sh
116
+ python -m unittest discover -s tests -v
117
+ python -m pip install --require-hashes -r requirements-dev.lock
118
+ python -m build --no-isolation
119
+ python -m twine check --strict dist/*
120
+ python tools/check_dist.py dist/*
121
+ ```
122
+
123
+ See [testing](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/TESTING.md), [validation records](https://github.com/MattyDroidX/herbarium-labels/blob/main/VALIDATION.md), [contribution instructions](https://github.com/MattyDroidX/herbarium-labels/blob/main/CONTRIBUTING.md), and [maintainer-controlled publication](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/PUBLISHING.md). Only the maintainer applies changes, commits, or pushes unless explicitly delegated.
124
+
125
+ ## Scientific safeguards and rights
126
+
127
+ Preserve literal spelling, line breaks, and uncertainties; normalization is separate. Institution, collector, determiner, taxonomic author, and writer are distinct roles. Draft profiles do not establish authorship. See [handwriting curation](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/HANDWRITING.md) and [practical evidence](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/EVIDENCE.md).
128
+
129
+ Prompt instructions were translated for the English API. Their policies are documented and tested structurally, but equivalent scientific performance has not been demonstrated by the offline suite. Live evaluation needs permitted images and a defined budget; see [prompt changes](https://github.com/MattyDroidX/herbarium-labels/blob/main/docs/PROMPT_CHANGES.md).
130
+
131
+ Code is MIT-licensed; [LICENSE](https://github.com/MattyDroidX/herbarium-labels/blob/main/LICENSE) and [NOTICE.md](https://github.com/MattyDroidX/herbarium-labels/blob/main/NOTICE.md) preserve provenance. Image/API/redistribution rights are independent. A live provider run, real HEIF decoding, and complete long/Unicode report validation remain pending; see [security](https://github.com/MattyDroidX/herbarium-labels/blob/main/SECURITY.md) and [validation limits](https://github.com/MattyDroidX/herbarium-labels/blob/main/VALIDATION.md).
132
+
133
+ ## Checked-in demonstration files
134
+
135
+ Open [examples/synthetic/README.md](examples/synthetic/README.md) to inspect the synthetic input image, rendered PNG preview, PDF, workbook, JSON, and verification results directly from the repository. These are simulated fixtures, not research or OCR-quality evidence.