Skip to content

Build tinydocs from the OpenHuman document engine - #1

Merged
senamakel merged 13 commits into
mainfrom
vendor-tinywallet-tinydocs
Aug 10, 2026
Merged

Build tinydocs from the OpenHuman document engine#1
senamakel merged 13 commits into
mainfrom
vendor-tinywallet-tinydocs

Conversation

@senamakel

@senamakel senamakel commented Aug 10, 2026

Copy link
Copy Markdown
Member

Replaces the rust-template scaffold with the crate's first real feature area: .docx synthesis, extracted from OpenHuman's generate_document agent tool.

The extraction line

Nothing about "a title, some sections, and a bullet list" is OpenHuman-specific, so the spec types, their size limits, the validation rules, and the OOXML synthesis live here. What stayed behind in OpenHuman is the policy only a host can supply: the artifact pipeline, the blocking-pool hop, and the generation deadline.

Here Stayed in the host
DocumentSpec / DocumentSection, size limits, validate artifact records, workspace paths
OOXML synthesis (docx-rs sits behind this crate) spawn_blocking hop, tokio::time::timeout
structured InvalidInput / GenerationFailed GenerationTimeout, which has no equivalent here

Design notes

docx::generate is synchronous and I/O-free on purpose. A crate that guessed at an executor or a deadline would be wrong for every host that guessed differently, so the host owns both. The README shows the expected async wrapper.

The spec doubles as the caller's JSON tool schema. It derives serde with deny_unknown_fields — at an LLM tool boundary a typo'd key should be a loud rejection, not a silently ignored one — and InvalidInput carries a structured field / reason pair using the same indexed notation the JSON uses (sections[2].bullets[0]), so a model that produced a bad spec can self-correct without re-reading the schema.

Every size limit is a public constant, so a host can quote the exact number in its own tool description and stay in lockstep with what validation enforces. The aggregate MAX_TOTAL_CHARS is the load-bearing one: the per-field limits bound each piece but not their product, which alone exceeds 500M characters.

Error is #[non_exhaustive] so adding a variant is not a breaking change for downstream matches.

Verification

  • 27 unit + 4 integration + 2 doc tests, all passing.
  • cargo clippy --all-targets --all-features -- -D warnings clean.
  • cargo clippy --all-targets --no-default-features -- -D warnings clean — the gated build is the only thing that catches code compiling solely when a feature is on.

Consumed by the matching OpenHuman PR, where the pre-existing tool-schema and happy-path tests pass unchanged, so the agent-facing wire surface is identical.

Summary by CodeRabbit

  • New Features

    • Added typed document specifications with titles, authors, sections, paragraphs, and bullet lists.
    • Added .docx generation with styled headings, metadata, and in-memory output.
    • Added validation for document content, field sizes, section limits, and total text length.
    • Added structured errors identifying invalid fields and generation failures.
    • Added serialization support and a feature-gated basic example.
  • Documentation

    • Replaced the template README with TinyDocs usage guidance, constraints, examples, and development instructions.
  • Breaking Changes

    • Removed the previous greeting example and API.

Replaces the rust-template scaffold with the crate's first real feature
area: `.docx` synthesis extracted from OpenHuman's `generate_document`
tool.

The extraction line is host-agnosticism. Nothing about "a title, some
sections, and a bullet list" is OpenHuman-specific, so the spec types,
their size limits, the validation rules, and the OOXML synthesis live
here. What stayed behind is the policy only a host can supply: the
artifact pipeline, the blocking-pool hop, and the generation deadline.

`docx::generate` is therefore synchronous and I/O-free. That is the
seam, not an omission — a crate that guessed at an executor or a
deadline would be wrong for every host that guessed differently.

The spec doubles as the caller's JSON tool schema, so it derives serde
with `deny_unknown_fields` (a typo'd key should be a loud rejection at
an LLM boundary, not a silently ignored one) and every size limit is a
public constant a host can quote in its own tool description.

33 tests; clippy clean with and without the `docx` gate.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@senamakel, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d151c14f-89c2-4701-aba4-525256c5c802

📥 Commits

Reviewing files that changed from the base of the PR and between b2dbd3c and bec77c8.

📒 Files selected for processing (6)
  • Cargo.toml
  • src/docx/mod.rs
  • src/docx/test.rs
  • src/docx/types.rs
  • src/error/mod.rs
  • src/lib.rs
📝 Walkthrough

Walkthrough

TinyDocs replaces the greeting template with typed document specifications, structured validation errors, and feature-gated DOCX generation. It adds OOXML output, size limits, serialization support, integration tests, an example, and usage documentation.

Changes

TinyDocs DOCX workflow

Layer / File(s) Summary
Document contracts and crate wiring
Cargo.toml, src/docx/types.rs, src/error/mod.rs, src/lib.rs
The crate defines DocumentSpec, DocumentSection, public limits, structured errors, and the feature-gated docx API.
Validation and DOCX generation
src/docx/mod.rs
The module validates document content, builds styled OOXML with bullets, and returns packed DOCX bytes.
Behavior and public API validation
src/docx/test.rs, src/error/test.rs, tests/public_api.rs
Tests cover limits, Unicode-safe errors, serialization, generated XML, whitespace handling, and downstream feature use.
Example and usage documentation
examples/basic.rs, README.md
The example and README document document construction, generation, validation, feature usage, and development checks.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Spec
  participant Generator
  participant Zip
  Caller->>Generator: pass DocumentSpec
  Generator->>Spec: validate()
  Spec-->>Generator: validation result
  Generator->>Zip: build and pack DOCX
  Zip-->>Caller: return DOCX bytes
Loading

Poem

I’m a rabbit with a document plan,
Specs and sections neatly span.
Bullets hop in numbered flight,
Errors stay trimmed and UTF-safe bright.
DOCX bytes spring from the queue—
TinyDocs now knows what to do!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: building tinydocs from the OpenHuman document engine.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/docx/mod.rs`:
- Around line 102-152: Update the validation method containing the section loop
to maintain a running character total across headings, paragraphs, and bullets,
checking it against MAX_TOTAL_CHARS as each field is processed. Return the
existing InvalidInput error for "sections" immediately when the running total
exceeds the cap, and remove reliance on the final self.total_chars() check so
oversized content is rejected without completing all validation first.

In `@src/docx/test.rs`:
- Around line 150-165: Reduce the fixture in
rejects_a_spec_over_the_aggregate_character_budget to one section containing
MAX_TOTAL_CHARS / MAX_PARAGRAPH_CHARS + 1 maximum-length paragraphs, rather than
MAX_SECTIONS repeated sections. Preserve the assertions that per-field limits
pass and validation rejects the spec for the aggregate "sections" limit.

In `@src/docx/types.rs`:
- Around line 40-49: Update the documentation for MAX_TOTAL_CHARS to state that
the limit applies to all renderable document text, including the title, author,
section headings, and body content, rather than only body text.

In `@src/lib.rs`:
- Around line 48-51: Update the crate root around the private error module
declaration to publicly re-export the Error and Result symbols, so
src/docx/mod.rs and downstream users can resolve crate::{Error, Result} and
tinydocs::Error without accessing crate::error directly.
- Around line 24-41: Gate the crate-level doctest containing the
`tinydocs::docx` example using `cfg_attr` so it is ignored when the `docx`
feature is disabled. Keep the example runnable when `docx` is enabled, and
ensure `cargo test --doc --no-default-features` no longer attempts to compile
it.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c39036f-f3ae-42e7-b08a-6b6bce8cd5cc

📥 Commits

Reviewing files that changed from the base of the PR and between 744c226 and b2dbd3c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • Cargo.toml
  • README.md
  • examples/basic.rs
  • src/docx/mod.rs
  • src/docx/test.rs
  • src/docx/types.rs
  • src/error/mod.rs
  • src/error/test.rs
  • src/greeting/mod.rs
  • src/greeting/test.rs
  • src/lib.rs
  • tests/public_api.rs
💤 Files with no reviewable changes (2)
  • src/greeting/test.rs
  • src/greeting/mod.rs

Comment thread src/docx/mod.rs Outdated
Comment thread src/docx/test.rs
Comment thread src/docx/types.rs Outdated
Comment thread src/lib.rs Outdated
Comment thread src/lib.rs
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0098 · 109,557 in / 32,181 out · 87,473 cached (80%) · z-ai/glm-5.2
critique:    $0.0052 · 38,334 in  / 19,529 out · 31,680 cached (83%) · z-ai/glm-5.2
security:    $0.0021 · 29,520 in  / 6,495 out  · 25,073 cached (85%) · z-ai/glm-5.2
tests:       $0.0013 · 18,958 in  / 3,504 out  · 13,787 cached (73%) · z-ai/glm-5.2
description: $0.0010 · 19,501 in  / 1,984 out  · 14,605 cached (75%) · z-ai/glm-5.2

Comment thread src/docx/test.rs
@tinysweeper

tinysweeper Bot commented Aug 10, 2026

Copy link
Copy Markdown

What this change touches

12 files, +1141 -181 across 7 components. The code graph knows nothing about these files yet — normal for newly added files, and a cold index otherwise.

flowchart LR
  n0["src/docx<br/>3 files +779 -0"]:::changed
  n1["root<br/>2 files +105 -69<br/>5 findings"]:::blocking
  n2["src/error<br/>2 files +116 -12"]:::changed
  n3["src<br/>1 file +54 -20"]:::changed
  n4["src/greeting<br/>2 files +0 -66"]:::changed
  n5["tests<br/>1 file +52 -5"]:::changed
  n6["examples<br/>1 file +35 -9"]:::changed
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading

Green: changed. Grey: untouched, reached through an import or a call. Orange: has findings. Red: has a finding that blocks the merge.

Component Files Lines Findings
src/docx changed 3 +779 -0
(root) changed 2 +105 -69 5 (high)
src/error changed 2 +116 -12
src changed 1 +54 -20
src/greeting changed 2 +0 -66
tests changed 1 +52 -5
examples changed 1 +35 -9
Changed files

src/docx

  • src/docx/mod.rs
  • src/docx/test.rs
  • src/docx/types.rs

(root)

  • Cargo.toml
  • README.md

src/error

  • src/error/mod.rs
  • src/error/test.rs

src

  • src/lib.rs

src/greeting

  • src/greeting/mod.rs
  • src/greeting/test.rs

tests

  • tests/public_api.rs

examples

  • examples/basic.rs

tinysweeper 0.1.0

senamakel and others added 11 commits August 10, 2026 14:41
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the rust-version field in Cargo.toml from 1.85 to 1.88 to reflect the new minimum supported Rust version required by the project.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the Docx type system to include new document type variants, enabling the library to handle a broader range of Word document formats. This change improves compatibility with documents generated by different versions of Microsoft Word and third-party tools.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduces a new error handling mechanism that allows users to define custom error types and recovery strategies when parsing malformed input, improving the library's flexibility for real-world use cases.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new example that demonstrates how the library handles blank probes, providing a clear reference for users who need to test or understand this specific functionality.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test for the blank probe example was failing because the test file did not account for the empty document case. This change updates the test to correctly validate the behavior when no content is present, ensuring the probe example works as expected.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the test in `src/docx/test.rs` to match the actual output of the document parser, fixing a failing test that was checking for incorrect content.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The parser previously failed when the input did not end with a newline character, causing an unexpected end-of-file error. This change ensures the parser correctly processes input that lacks a trailing newline by treating it as a valid termination of the data stream.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a new error type and associated methods to allow callers to define and handle custom error scenarios, improving the library's flexibility and robustness in production use.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the `cfg_attr` attribute controlling the docx documentation line to span multiple lines, improving code readability without changing any behaviour.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 1 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0089 · 102,746 in / 28,871 out · 81,995 cached (80%) · z-ai/glm-5.2
critique:    $0.0046 · 37,213 in  / 16,928 out · 29,989 cached (81%) · z-ai/glm-5.2
security:    $0.0021 · 24,555 in  / 6,832 out  · 20,544 cached (84%) · z-ai/glm-5.2
tests:       $0.0014 · 20,136 in  / 4,018 out  · 15,353 cached (76%) · z-ai/glm-5.2
description: $0.0008 · 20,842 in  / 1,093 out  · 16,109 cached (77%) · z-ai/glm-5.2

Comment thread Cargo.toml
description = "A production-ready Rust library template."
repository = "https://github.com/tinyhumansai/rust-template"
documentation = "https://docs.rs/rust-template"
description = "Agent-friendly document synthesis and text extraction (DOCX, PPTX, PDF) in Rust."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Package description and keywords claim PPTX/PDF support this PR does not add

The package metadata advertises PPTX, PDF, and text extraction, but this PR adds only docx-rs (DOCX synthesis) and serde (JSON spec types). No PPTX or PDF dependency is introduced, and the only feature gate is docx. The pdf keyword compounds the mismatch. A consumer or docs.rs reader would be misled into believing capabilities that this crate does not provide. Either narrow the description/keywords to what is actually shipped (DOCX synthesis + spec types), or add the PPTX/PDF/extraction dependencies and features in this same PR.

existing_code: description = "Agent-friendly document synthesis and text extraction (DOCX, PPTX, PDF) in Rust."

[RULE] Package metadata must accurately reflect shipped capabilities ·

Comment thread Cargo.toml
description = "A production-ready Rust library template."
repository = "https://github.com/tinyhumansai/rust-template"
documentation = "https://docs.rs/rust-template"
description = "Agent-friendly document synthesis and text extraction (DOCX, PPTX, PDF) in Rust."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security likely

Description claims PPTX, PDF, and text extraction with no dependency for them

The package description claims capabilities the dependencies cannot back.

description = "Agent-friendly document synthesis and text extraction (DOCX, PPTX, PDF) in Rust."

The only document-format dependency added is docx-rs (DOCX only). There is no production dependency for PPTX or PDF — zip is a dev-dependency used to inspect test output, and serde/serde_json handle the JSON wire contract. A crate advertising PPTX and PDF extraction without any library to parse those formats will either fail at runtime or mislead consumers (and agents that read crate metadata) into relying on support that does not exist.

[RULE] Package metadata must reflect actual capabilities ·

Comment thread Cargo.toml
description = "A production-ready Rust library template."
repository = "https://github.com/tinyhumansai/rust-template"
documentation = "https://docs.rs/rust-template"
description = "Agent-friendly document synthesis and text extraction (DOCX, PPTX, PDF) in Rust."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium tests confident

Package metadata claims PPTX, PDF, and text extraction not implemented here

The package description claims text extraction (DOCX, PPTX, PDF) but this pull request implements only .docx synthesis — no PPTX, no PDF, no text extraction. The keywords list includes "pdf" for the same reason. crates.io metadata is user-facing and these claims will surface in search results and docs.rs as supported features that do not exist.

description = "Agent-friendly document synthesis and text extraction (DOCX, PPTX, PDF) in Rust."
keywords = ["docx", "ooxml", "pdf", "document", "agent"]

Drop PPTX/PDF/extraction from the description and pdf from the keywords, or scope them to what this PR actually ships.

[RULE] Prefer small, typed APIs over stringly-typed ones; accept &amp;str/impl Into&lt;String&gt; at boundaries and return owned, concrete types. ·

@senamakel
senamakel merged commit 3b300d3 into main Aug 10, 2026
13 of 14 checks passed
@senamakel
senamakel deleted the vendor-tinywallet-tinydocs branch August 10, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant