V0 6 0 release - #235
Open
docushell-dev wants to merge 37 commits into
Open
Conversation
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
Shared source selection now implements release-prep 7.3 rules 5 and 6. A present top-level artifact_type that is duplicated, non-string, or not exactly ethos.grounding.v1 exits 2 instead of silently falling back to the native loader. Duplicate keys are counted on a strict second pass because serde_json::Value collapses them. Absent, malformed, and non-object roots still reach the native loader so it keeps ownership of its own errors. evidence anchor loses the default_value that made --grounding always present and so prevented shared dispatch. Native input with no flag and with explicit --grounding ethos-json produce byte-identical reports. PDFium-gated tests asked only whether the library file existed. On a host with no pinned PDFium profile a correctly configured library is still refused, so following scripts/fetch-pdfium.sh turned 1 failure into 27. The CLI suites now ask ethos doctor --require-pdfium and skip with an explanation; the in-crate test consults current_platform_key(). Two doctor tests assert the platform-independent contract and check pin-specific wording only where a pin exists. Also restores a clean clippy --all-targets --all-features -D warnings by replacing three map_or(false, ..) with is_some_and and removing a no-op replace. cargo test --workspace is 390 passed 0 failed with PDFium set and unset. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
resolveBinary threw an untyped Error from inside execute's try block, so an unsupported host produced an uncaught stack trace and four of eight npm suites never ran. Launcher failures are now one typed EthosSdkError: unsupported_platform when the host has no packaged binary, vendor_invalid when the payload is missing or malformed. The platform check runs before anything can spawn. sdk.test.js asserts that contract on unsupported hosts, including that no process is spawned, then skips the spawn-backed assertions that need a packaged binary for the target. This is release-prep 11.5 "unsupported platform and missing binary fail before pretending verification ran", now actually executed rather than assumed. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commit 73d53c8 deleted docs/roadmap.md as a completed historical record but left it wired into the gates: - eight contract gates asserted the contract is linked from docs/roadmap.md and errored on FileNotFoundError; - the Makefile invoked .github/scripts/test_roadmap_status.py in 11 targets, and that script does not exist, so make milestone-d-grounding-source-contract - the documented way to run the contract gate - failed outright. Ten contract-gate assertions pinned the dead invocation in place, which is why it never drifted back into repair. Also records structural_provenance in the frozen GroundingSource trait inventory and expected list. It has been on the trait since v0.4.0, so the frozen contract did not describe the declared surface. Failing Python gates drop from 12 to 4, with none newly broken. The four remaining are pre-existing and unrelated to Grounding JSON. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ates docs/writing-a-mapper.md is an end-to-end Grounding JSON guide for parser authors in any language. It covers the parts that actually block a mapper: where page geometry comes from (the PDF, not the parser), coordinate conversion with worked code, ID and reading-order rules, honest capability declaration, the representation-versus-source hash distinction, a self-check recipe, and the frozen rejection-code table. It states plainly that a passing grounding check does not mean the coordinates are right. docs/v0-6-0-release.md is the evidence-based release record: verified build, lint, test, schema, and end-to-end results; the unresolved fingerprint-identity conflict between release-prep 8.1 and ADR-0016; open-source usability findings; and the remaining blockers. docs/v0-6-0-release-prep.md is committed as the scope authority, with the dropped receipt-alternative link removed and supersession notes at 6.4 and 8.1. README.md already linked it, so that link was broken until now. ADR-0016 freezes the fifteen validation error codes as a public compatibility surface - they appear as error.code in a schema-backed artifact and consumers will branch on them - and records the representation-hash rationale. The npm quickstart gains supported hosts, mapper example invocation, page-metadata sourcing, and citation fingerprint selection. Two of those gaps were found by walking the documented path rather than reading it. execution-status.md gains a v0.6.0 entry. The WP-0 public-posture request records the decider's acceptance of the README change so the audit trail stops contradicting itself. requirements-dev.txt declares the jsonschema gate dependency. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The gate still described the v0.4.0 vendor payload from e73477e. The tracked payload was refreshed at the reviewed v0.5.0 release commit bfb7197 ("prepare npm B from frozen v0.5 core"), and that refresh carries its recorded boundary exception in CHANGELOG.md, so the gate - not the payload - was stale. Syncs cli_version and the SUPPORTED_TARGETS hashes from the manifest as committed at bfb7197. binary_sha256 stays independently meaningful because the same test re-hashes the vendored bytes and compares. Also records the WP-3 package additions in EXPECTED_PACKAGE_FILES: index.js, the two generated Grounding declarations, both mapper examples, and their seven pinned fixtures. package.json already ships index.js and examples/, so the packed contents were correct and the expectation was not. The gate's npm README wording assertions stay at 0.4.0. Public install wording is deliberately held there by test_v0_5_0_version_activation until the claims lane advances it. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A clean-room mapper was written for a synthetic acme-pdf-extract parser that shares no field names, coordinate convention, or page-geometry behavior with any shipped fixture, using only docs/writing-a-mapper.md. All four self-check steps passed with no undocumented intervention: byte-identical double run, valid structure with matched source binding, a real claim grounding via representation_sha256, and a one-character change correctly producing mismatch. It ran on darwin:x64 without PDFium. The record states its own limitation plainly - the walkthrough author also wrote the guide, so it proves the procedure is complete and executable, not that it is discoverable. Release-prep 5.1 still requires an actual outsider. Also records the public-version deadlock. Advancing install wording from 0.4.0 to 0.5.0 was attempted and reverted: test_package_registry_source_consistency demands 0.5.0 in the package docs, test_v0_5_0_version_activation forbids 0.5.0 in README and the claims registry, and public_boundary_claims_gate requires the package docs to echo the registry verbatim. No edit satisfies all three. Underneath sits a factual conflict - release-state.json says v0.5.0 is published, the activation guard exists to hold public wording at 0.4.0 - which is release-prep 14's inconsistent-ledger blocker and needs a decider ruling. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docushell-dev
force-pushed
the
v0_6_0Release
branch
from
July 30, 2026 17:10
79ab3f9 to
9d01148
Compare
…hed 0.5.0 Two decider rulings, 2026-07-30. Fingerprint identity is representation_sha256, the hash of the accepted Grounding JSON bytes. release-prep 6.4 and 8.1 are corrected to match ADR-0016, so the authority conflict is gone. The quickstart flow now reads the fingerprint from grounding check instead of reusing the source PDF hash, which would have reported stale against a correct artifact. source.sha256 stays a separate optional PDF binding and is never substituted for the fingerprint. Public install wording advances to the published 0.5.0 baseline. Verified directly against the registries: ethos-doc-core, ethos-verify, and ethos-pdf are 0.5.0 on crates.io (published 2026-07-21), ethos-pdf is 0.5.0 on PyPI, @docushell/ethos-pdf dist-tags.latest is 0.5.0 on npm, and GitHub Release v0.5.0 is latest carrying the macOS arm64 and Linux x64 archives the README names. So release-state.json and execution-status.md were right and the install commands were a release behind - anyone following the README since 2026-07-21 installed 0.4.0. Advances README.md, public-boundary-claims.json, both Python docs, and both npm docs, and retires the pre-publication hold in test_v0_5_0_version_activation.py, which now asserts the advertised commands name the published version and that no surface still points at the previous release. Failing Python gates drop from 3 to 2; the rest are pre-existing and unrelated. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
--source-artifact on verify and verify-batch produced a byte-identical report to
omitting it. The binding was checked and then vanished: verification_report.json
has no field that can express it, and adding one is a report-schema change that
release-prep 5.3 excludes.
That combination is worse than not offering the flag. An operator running
verify --source-artifact and seeing exit 0 would reasonably believe the report is
PDF-bound, while an auditor, customer, or DocuShell receiving that report cannot
distinguish it from a run where no PDF was ever supplied. For a project whose
value is that what was proven is inspectable, an assurance with no artifact behind
it is the wrong default.
Source binding stays on grounding check, which records source_binding as matched,
mismatched, or not_checked in a schema-backed artifact that can be kept and handed
on. Also drops the sourceArtifactPath passthrough from the npm verifyClaims.
Test coverage is preserved, not deleted: the non-PDF rejection moves onto
grounding check, the bound-artifact test still verifies after checking the binding
there, and the batch test keeps its dispatch assertion under an accurate name.
ADR-0016 now records the two additions kept deliberately - --grounding ethos-json
as a shared-loader alias, and in-memory verifyClaims({ citations }) because
requiring a path pushes every JavaScript caller to reimplement temp-file cleanup
worse than the SDK does once.
cargo test 390 passed 0 failed; clippy -D warnings clean; 7 npm suites pass.
Signed-off-by: docushell-dev <doqshell@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fifteen gate scripts were unreachable from CI, and seven make targets referenced roughly 280 scripts deleted in 73d53c8 - including test_roadmap_status.py in eleven places - so those targets could not run at all. That is why the eight contract gates behind them had been red for months without anyone noticing. A gate nobody executes is not governance: it rots silently, then costs whoever finally runs it real time. Ten of the twelve failing Python gates I found this week were governance defects, not code defects. Adds test_gate_reachability.py, which fails when any gate script is unreachable from CI, when a make target invokes a script that does not exist, or when a workflow does. This is the structural fix; everything else here is the one-time cleanup it would have prevented. Wires the twelve genuinely current contract and boundary gates into a new governance-gates job, listed one per line because the reachability check greps those literal paths. Removes eight make targets whose releases and milestones have shipped. Deletes three gates with no surviving subject: the app-answer release-prep guard whose record was deleted, and two pure Makefile guards for removed targets. Correcting one over-deletion mid-flight: milestone-d-internal-contracts was restored after inspection showed it is 883 lines and 37 tests of current policy - contract registry consistency, schema identity, execution-status alignment - of which only four concern the make target. Its three milestone-D closeout-record assertions are pruned because those scripts were intentionally deleted; the contract assertions survive. Gate scripts reachable from CI: 44 of 44. cargo test 390 passed 0 failed. test_rag_framework_examples still fails locally only, needing langchain that its own CI job installs. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Offsets are Unicode scalar indexes. The only prior fixture was "héllo": precomposed U+00E9, all BMP, five scalars, which exercises neither hard case. Both new vectors are where a JavaScript or Python mapper silently emits wrong offsets that still satisfy every structural rule. "a😀b" is three scalars, but "😀".length is 2 in JavaScript, so UTF-16 offsets shift everything after the emoji. "e" plus U+0301 renders as one character while occupying two scalars, so counting graphemes selects only the base letter. Both correct selections are accepted and both wrong ones rejected, plus an end past the final scalar, which must fail rather than panic on the slice. Also accepts release-prep as the scoped decider request, satisfying its own precondition 3.1 - implementation has landed through WP-3 and three rulings are recorded, so the draft header no longer described reality. Publication and public claims stay gated. Widens the ADR-0016 governs clause from WP-1 to the WP-2 and WP-3 surfaces it already decides. Adds the WP-4 double-run determinism record: validation report, JavaScript mapper, and Python mapper each byte-identical across two runs, the two mappers byte-identical to each other and to the packaged fixture. The DocuShell mapper row stays outstanding pending an acceptance commit. cargo test 391 passed 0 failed; clippy -D warnings clean. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
types/index.d.ts is generated by dev/generate-types.js. Cutting sourceArtifactPath from verifyClaims edited the generated file by hand, so the generator still emitted the removed field and types.test.js reported the checked-in declarations as stale. That test had never executed. npm install fails against the Oracle registry mirror configured in the developer environment, so types.test.js and tsc were skipped every time the suite ran, and the drift went unseen. Installing from the public registry surfaces it immediately. npm test now exits 0 across all eight suites plus tsc. This closes the WP-3 done-when clause: existing package consumers still compile, and a clean Node project completes the documented path. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DocuShell had no Grounding JSON at all, so 9.4 acceptance needed one new artifact. Added the bounded shadow lane 9.4 permits rather than migrating the working OpenDataLoader production path, which 9.2 says not to rewrite. DocuShell commit cc652ec on branch ethos-v0-6-0-grounding-shadow, seven passing tests covering every 9.4 criterion. Its existing CLI and PDFium pins already match the Ethos vendor manifest hashes exactly. The record states its own gap: 9.4 requires an exact reviewed commit, and cc652ec sits on a branch. Bind this to a reviewed main SHA before closeout. Separately, measured the Grounding JSON validator instead of the 12 regression comparison against the v0.5.0 verification baseline. That comparison would measure nothing, because Grounding JSON adds a parallel loader and no existing verification code path changes. Release-profile figures: 30 ms and 1 MB for ten elements, 150 ms and 14 MB for ten thousand, 1850 ms and 138 MB for a hundred thousand. Roughly linear at 18 microseconds and 1.4 KB peak RSS per element. Extrapolating to the frozen one-million-element ceiling gives about 18 seconds and 1.4 GB peak RSS. That extrapolation is unmeasured and flagged as such: the validator retains the parsed artifact rather than streaming, so the memory figure deserves confirmation before a ceiling is fixed. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The shadow lane was reviewed by the decider and merged with --ff-only, so the reviewed SHA and the SHA on DocuShell main are identical. Section 9.4 requires an exact reviewed commit, and that requirement is now met rather than deferred. Records both execution modes exercised before merge: without ETHOS_CLI_PATH the four pure-mapper tests run and the three CLI-backed tests skip, keeping DocuShell CI green on runners without an Ethos binary; with the CLI supplied all seven run. Completes the fourth 11.4 double-run row, so all four are evidenced. Notes one optional follow-up that is not a release blocker: DocuShell's parse-pdf image already installs the CLI at /opt/ethos/bin/ethos, so exporting ETHOS_CLI_PATH in that image's test stage would move the three skipped assertions to executed. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ating The previous record extrapolated from a hundred thousand elements and flagged the result as unmeasured. Measured directly: one million elements, five thousand pages, 151 MB artifact takes 26.5 s and 1.29 GB peak RSS. A schema-legal artifact just under the 256 MiB input limit reached 1.42 GB. Three findings the extrapolation could not have given. Wall clock scales with element count, not bytes. Holding elements fixed and raising input 34 percent moved wall clock 1.5 percent, so a per-element ceiling is the meaningful shape. Peak RSS runs six to nine times artifact size, because the validator retains the parsed artifact rather than streaming it. The 256 MiB input limit therefore does not bound memory to a comparable figure. Oversized input fails closed and cheaply: 277 MiB was rejected in 0.0 s at 1 MB RSS with the existing FileTooLarge exit 7, before any parse work, so it cannot be used to exhaust memory. The consequence worth deciding on is that a schema-legal artifact can need roughly 1.5 GB resident. Release-prep 9.2 runs the verifier in a bounded worker; a cap below that turns a resource limit into an opaque OOM kill rather than a clean rejection. Recommends documenting the working set for v0.6.0 over changing a frozen ADR limit, and logs a validator memory guard using the existing MemoryLimitExceeded exit as a v0.7.0 input. The sizing table is now in the mapper guide where integrators will meet it. Suggested ceiling, measured plus roughly 1.5x headroom: 40 microseconds and 2 KB peak RSS per element. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decider accepted 40 microseconds and 2 KB peak RSS per element, roughly 1.5 times the 26.5 microseconds and 1.29 KB measured at the frozen one-million-element limit. This replaces release-prep 12's regression comparison against the v0.5.0 verification baseline, which would have measured an unchanged code path. Wall clock is enforced by a new test over 100,000 elements, a tenth of the frozen limit and representative because cost is linear. The test is release-only, since a debug build runs about an order of magnitude slower than the profile the ceiling describes, and opt-in through ETHOS_CHECK_VALIDATOR_CEILING, because wall-clock assertions flake on shared runners and a flaky gate trains people to ignore red. Peak RSS stays recorded in the baseline document rather than asserted in-process; measuring it portably would cost more than it proves. Decider also chose to document the working set rather than reduce a frozen limit. A schema-legal artifact at the element ceiling needs roughly 1.5 GB resident, because the validator retains the parsed artifact rather than streaming it. The sizing table now sits in the mapper guide where integrators meet it before running a check, so a memory-capped worker can be sized rather than OOM-killed. Lowering the element ceiling was rejected as a compatibility decision, and a validator memory guard using the existing MemoryLimitExceeded exit is logged as a v0.7.0 input alongside streaming validation. cargo test 392 passed 0 failed; clippy -D warnings clean. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two release prerequisites that engineering completion does not cover. Core version activation moves the workspace, internal crate pins, Cargo.lock, pyproject.toml, and the Python package to 0.6.0 so release artifacts build at the right version. Public install wording and the vendored npm payload deliberately do not move. Activation is not publication: until v0.6.0 reaches the registries, advertising it would hand users an install command that cannot succeed, and the npm package ships a vendored 0.5.0 binary that a version bump would misdescribe. test_v0_6_0_version_activation.py guards that split, mirroring how the v0.5.0 activation was governed. It asserts lockstep core versions, that no install command names the unpublished version while prose about the plan stays allowed, that the npm payload holds at the published version unless a recorded boundary exception refreshes it, and that the release ledger still describes what is live rather than what is being prepared. The v0.5.0 guard keeps the published-baseline assertions and hands core lockstep to this one. The wording request covers the gap that engineering cannot close: README currently calls Grounding JSON a proposal, which publication would make false, and the WP-0 posture record explicitly withheld availability wording. The draft states what it does not claim, keeps the mapper requirement and the geometry limitation visible rather than smoothing them away, and marks the outsider-walkthrough evidence row as unfilled so approval is not read as covering it. cargo test 392 passed 0 failed; npm suite green. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Approved as written, applied at publication only. Nothing reaches a public surface now, and test_v0_6_0_version_activation.py keeps every install command naming the published 0.5.0 until v0.6.0 reaches the registries. Approving before the outsider walkthrough is sound because the two answer different questions. This wording claims a capability, that any parser can reach the verifier by writing one mapper, and three working mappers already evidence it. The walkthrough tests documentation quality. A poor result there means the guide needs work; it does not make the capability claim false. The walkthrough still gates publication under release-prep 5.1, which is a separate gate. The revision clause allows changes from walkthrough findings or business need through the normal claims lane, without re-approving from scratch. That matches how the claims registry has actually been maintained across v0.2, v0.3, and v0.5. Revisions may not widen the claim past the recorded limits; removing one of those is a new approval. Also supersedes the WP-0 posture withhold for these two items only, and corrects a stale line in that record which still described the claims registry as unreconciled after it was advanced to the published 0.5.0 baseline. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… blind Validation was quadratic for any artifact declaring spans with char offsets. Each span rescanned the element array to resolve its owning element, and every element, span, table, and cell rescanned the page array to bbox-check against its page. The per-cell page lookup was loop-invariant on top of that. Measured on the CLI before the fix, with span count tracking element count and each span referencing the last element: 2.0 s at 16,000 records, 19.4 s at 32,000, and 128.8 s at 64,000 for a 15 MB artifact. A control holding byte size identical and pointing spans at the first element stayed linear, isolating the quadratic term from parse cost. After indexing, the same three inputs take 0.32 s, 0.62 s, and 1.25 s, and growth is linear again. The fix reuses structures the function already half-built. Page ids were tracked in two byte-identical HashSets cloning every id; those collapse into one borrowed map that rejects duplicates as it is built, answers the membership checks, and serves the bbox lookups. The element map is built only inside the spans branch, so artifacts declaring no spans do not pay for it. The accepted ceiling could not have caught this. Its measurement and its enforcing test both use capabilities all false, the one shape where none of these scans run, and the record generalised that to "cost is linear". Re-measured both shapes: the elements-only figures are unchanged, and spans plus char offsets costs 13.0 us and 2.66 KB per element at the frozen one-million-element limit, against 26.5 us and 1.29 KB. Wall clock now passes the 40 us ceiling on both. Peak RSS does not: 2.66 KB per element is 33% over the accepted 2 KB, so the record carries it as Outstanding with the two options rather than quietly re-baselining a number that was never measured against this shape. The second ceiling test covers spans and char offsets; tables are not separately asserted and the record says so. Both tests were unreachable from CI, gated behind an env var that appeared in no workflow and no make target, so a regression would have merged green. make validator-ceiling-check now runs both and the test job invokes it. The integrator sizing table in the mapper guide previously published elements-only numbers while claiming coverage of every capability shape. It now carries both shapes separately, and the worker-sizing guidance moves from roughly 1.5 GB to roughly 3 GB, because a cap set from the old figure is killed by a schema-legal artifact rather than rejecting it. cargo test 397 passed 0 failed; clippy -D warnings clean; both ceiling tests pass. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tions Making write_output atomic broke every output target that is not a regular file. NamedTempFile::new_in creates in the destination's parent directory and persist renames over the destination, so --out /dev/stdout and --out /dev/null failed with a usage error because /dev is not writable, and a symlinked or FIFO destination was replaced by a regular file instead of being written through. A symlink pointing at the real report silently became a regular file while the intended target kept its old contents. Eleven call sites regressed; only verify-batch already carried the limitation. Atomicity is worth keeping, so the write is now conditional on the destination kind. symlink_metadata classifies without following, regular files and paths that do not exist yet take the temp-file-and-rename path, and anything else is written through with fs::write. Rename cannot be atomic for those targets in any case, and it was never claimed for them. Mode is restored to what fs::write produced. NamedTempFile creates 0600 and persist keeps it, which silently removed group and other read access from every report; a CI artifact collector or a service account reading as another user would have lost access. An existing file now keeps its own mode and a new file gets 0644. Four tests cover the behaviours that regressed: atomic replacement with no temporary left behind, write-through to a symlink target with the link intact, mode preservation and the 0644 default, and a character-device destination. None existed before, which is why a mechanism change across twelve call sites landed unnoticed. write_batch_output became a pure passthrough once the logic moved and is removed; its one caller now calls write_output directly. cargo test 397 passed 0 failed; clippy -D warnings clean. Verified end to end: /dev/stdout and /dev/null exit 0, a symlink survives with its target written, a FIFO survives and its reader receives the bytes, new files are 0644, and an existing 0664 file stays 0664. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Five directories under crates/ held a README and nothing else. They were not workspace members, had no Cargo.toml, and were referenced from no manifest, but in a public repository they read as crates that partly exist. ethos-rag was the sharpest case: a working ethos rag chunk command ships today, implemented inside ethos-cli, so the empty directory pointed readers at the wrong place. Removed, with the roadmap intent kept in the architecture crate map. The architecture document described a dependency graph the manifests do not have. It drew PDF -> LAY -> TAB and routed rag, render, security, and mcp through the build as crates with edges. In the real workspace ethos-layout and ethos-tables both depend on ethos-core alone, not on ethos-pdf and not on each other, which is load-bearing rather than incidental: it is what proves layout and table logic run on quantized geometry and never touch a live PDFium handle. The graph now matches cargo metadata and says so. The public-architecture block also labelled command groups as crate names; ethos rag has no crate, so that is now stated rather than implied. check_verify_dependency_boundary.py printed an unqualified ok that reads like the invariant-4 enforcer. It inspects Cargo.toml metadata only and cannot see source imports, so a parser-internal use statement passes it. Verified: with such an import added to ethos-verify, the workspace build, the workspace test job, clippy with -D warnings, and this script all pass, and only cargo check -p ethos-verify fails. The message now says manifest check only, and verify-alpha-tree runs the two per-package compile checks so the boundary is exercised locally and not just by one CI job. semantic_unverified is false on every check the current implementation emits, because only literal checkers exist and non-literal claims fail closed as unsupported instead. The threat model states this; the schema, which is what an integrator reads, did not, so a downstream reader could take a constant field for a check that ran and passed. The description now says it. cargo test 397 passed 0 failed; clippy -D warnings clean; schema examples validate. Signed-off-by: docushell-dev <doqshell@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PDFium reports page dimensions with rotation already applied (FPDF_GetPageWidthF/FPDF_GetPageHeightF) but returns text coordinates in unrotated user space with a bottom-left origin. The conversion only flipped the origin, which is correct for /Rotate 0 and 180, where the box does not transpose, and mixes two coordinate spaces for 90 and 270. The result was coordinates outside the reported page, including negative ones. crop_element then rejected them with "resolved element bbox exceeds page bounds" — correctly, per the contract that fails closed on negative and out-of-page bboxes — so no rendered crop could be produced for any document whose text falls outside the transposed box. A /Rotate 270 A4 page reported an 842x595 box while text y ran to 767, giving y0 = 595 - 767 = -172. Introduces PageSpace, carrying the display box plus rotation, and applies both the rotation and the origin flip at the two conversion sites (qrect_from_pdfium_char_box and char_origin). Both corners are transformed before min/max is taken, since rotation can swap which corner holds each axis extreme. The unrotated media box is derived from the reported display box, because 90/270 transpose it, so no additional PDFium symbol is required and caller-provided PDFium is unaffected. Rotation 0 and 180 output is unchanged. Covered by three unit tests: an exact-value regression guard for rotation 0, the real /Rotate 270 geometry that produced the negative coordinate, and a containment property across all four rotations. The two rotation-sensitive tests were confirmed to fail against the previous behavior. The existing synthetic-rotation-90 fixture asserted page dimensions, text, and spans but never bbox geometry, which is why this was not caught earlier; extending it to assert containment would close that gap permanently. Not verified here: the PDFium-gated integration tests skip without ETHOS_PDFIUM_LIBRARY_PATH, so parses_rotation_90_pdf_when_pdfium_is_configured did not execute on this machine. Run the gated suite on a host with PDFium before release. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.