fix: serve original PDF uploads (passthrough) and persist artifacts on disk#107
Conversation
Serve uploaded PDFs passthrough instead of the placeholder artifact: DefaultDocumentConversionService now seeds the artifact store with the original upload bytes when the source declares PDF (extension or content type) and carries the %PDF- magic header, and DefaultConversionWorker completes the job without generating a placeholder when seeded bytes exist. Non-PDF sources keep the placeholder path; real docx/hwp conversion remains future work and is documented as such. Add FileSystemArtifactStore, a disk-backed ArtifactStore that persists artifact bytes plus a minimal metadata sidecar (docId, size, sha256, storedAt) under clearfolio.artifact-store.root-dir (default data/artifacts) so artifacts survive restarts, with an in-memory cache on the read path. ArtifactStoreConfig selects filesystem (default) or in-memory mode via clearfolio.artifact-store.mode; InMemoryArtifactStore remains for tests and the in-memory mode. HMAC artifact-token gating and HTTP Range serving are unchanged. Tests: PDF passthrough serves original bytes (magic-byte verified), non-PDF still gets the placeholder, disk store round-trips and survives re-instantiation, metadata sidecar contents, IO failure paths, store mode selection, plus coverage for previously unexercised constructor and state-store branches (JaCoCo line/branch miss = 0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGVapDZ3k7V7zKYk16P4C
OWNER MUST CONFIRM: the demo UI footer asserts HYOSUNG copyright (ViewerUiController); if HYOSUNG holds rights, replace this LICENSE choice accordingly. Merging is the owner's license decision. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGVapDZ3k7V7zKYk16P4C
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGVapDZ3k7V7zKYk16P4C
OpenCode Review Overview
Pull request overviewOpenCode reviewed the current-head bounded evidence and found no blocking issues. FindingsNo blocking findings. SummaryApproval sufficiency: bounded evidence supplied affirmative approval evidence for changed files, coverage/docstring posture, risk surfaces, and current-head verification; approval is not based merely on the absence of known blockers.
Changed-File Evidence Mapflowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Changed file (17 files)"]
S1 --> I1["repository behavior"]
I1 --> R1["Review risk: Changed file (17 files)"]
R1 --> V1["required checks"]
|
There was a problem hiding this comment.
Pull request overview
OpenCode reviewed the current-head bounded evidence and found no blocking issues.
Findings
No blocking findings.
Summary
Approval sufficiency: bounded evidence supplied affirmative approval evidence for changed files, coverage/docstring posture, risk surfaces, and current-head verification; approval is not based merely on the absence of known blockers.
Verification posture: CodeGraph evidence was initialized and bounded current-head evidence reviewed for changed-file evidence including ARCHITECTURE.md, src/main/java/com/clearfolio/viewer/artifact/ArtifactStore.java, src/main/java/com/clearfolio/viewer/artifact/FileSystemArtifactStore.java, src/main/java/com/clearfolio/viewer/artifact/InMemoryArtifactStore.java, src/main/java/com/clearfolio/viewer/config/ArtifactStoreConfig.java, and 11 more.
Linter/static: workflow/static review evidence is bounded by the current-head GitHub Checks gate and changed-file evidence.
TDD/regression: coverage execution evidence and focused changed hunks were reviewed from bounded-review-evidence.md.
Coverage: coverage execution evidence reports test coverage as not applicable because no supported changed source files or package manifests were found.
Docstring coverage: coverage execution evidence reports docstring coverage as not applicable because no supported changed source files or package manifests were found.
DAG: CodeGraph/source-backed behavior map connects ARCHITECTURE.md to the affected review, runtime, or workflow path and required checks.
PoC/execution: coverage-evidence job executed on the current head and reported PASS.
DDD/domain: workflow and repository-governance invariants were reviewed against changed files in bounded evidence.
CDD/context: CodeGraph evidence, changed-file history, and focused hunks were reviewed from bounded-review-evidence.md.
Similar issues: changed-file history evidence was reviewed for comparable local precedents.
Claim/concept check: bounded evidence, repository source, current-head workflow evidence, and, where numeric, scientific, statistical, or literature-backed claims are affected, original-paper/formula evidence and parameter-recovery expectations were used for claims.
Standards search: standards and external-source checks are delegated to configured OpenCode web_search/Context7/DeepWiki sources when applicable; no evidence-backed standards blocker is present in bounded evidence.
Compatibility/convention: changed workflow/script conventions, object naming, and reserved-word safety for schema/API/config/code surfaces were checked in bounded evidence.
Breaking-change/backcompat: deployment evidence and changed-file history were checked for backward-compatibility risk.
Performance: changed surfaces were checked for performance risk in bounded evidence.
Developer experience: changed automation, review, test, setup, and maintenance surfaces were checked for helpful or obstructive DX impact in bounded evidence.
User experience: connected user, operator, API, CLI, documentation, review-comment, status-check, rendering, and workflow-reader behavior was checked for contradictions against code, docs, and tests in bounded evidence.
Visual/DOM: Playwright visual, DOM locator, ARIA snapshot, console, and responsive evidence were checked when a web UI surface was present; for non-web surfaces, API/CLI/log/docs/workflow interaction evidence was reviewed instead.
Accessibility/i18n: accessibility, localization, and human-readable text surfaces were checked where UI, CLI, API message, docs, logs, or review text changed.
Supply-chain/license: dependency, package, model, container, and external-tool changes were checked in bounded evidence.
Packaging: package, build, test, lint, and security contracts were checked in bounded evidence.
Security/privacy: workflow-token, review-gate, and repository-automation security/privacy boundaries were checked in bounded evidence.
- Result: APPROVE
- Reason: PR introduces a disk-backed artifact store and PDF passthrough, with complete tests and no blocking issues.
- Head SHA:
344fbc06bd7583621b3f827a5150a6bcb16313b9 - Workflow run: 29174205435
- Workflow attempt: 1
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Changed file (17 files)"]
S1 --> I1["repository behavior"]
I1 --> R1["Review risk: Changed file (17 files)"]
R1 --> V1["required checks"]
What / Why
Clearfolio's conversion pipeline was a stub:
DefaultConversionWorkeralways calledPdfBoxArtifactGenerator.generatePdf(job), so every upload — including PDFs — was served as a one-page placeholder containing only the filename and hash, andInMemoryArtifactStorelost every artifact on restart. This PR makes uploaded PDFs actually viewable and makes artifacts survive restarts, which unblocks real sheet-music viewing for the BandScope integration.1. PDF passthrough (originals are served, not placeholders)
DefaultDocumentConversionServicenow detects PDF sources (extension or content type, confirmed by the%PDF-magic header) and seeds the artifact store with the original upload bytes at submit time.DefaultConversionWorker.performDefaultConversionserves seeded bytes as the artifact and only generates the placeholder when nothing was seeded.ARCHITECTURE.mdand the worker Javadoc. No conversion is faked.2. Disk-backed artifact store (survives restarts)
FileSystemArtifactStorepersists artifact bytes plus a minimal metadata sidecar (docId,sizeBytes,sha256,storedAt) under a configurable root:InMemoryArtifactStoreis kept for tests andmode: in-memory;ArtifactStoreConfigselects the implementation.ArtifactControllerTest/ArtifactLinkServiceTestcases pass untouched).3. LICENSE
The repo had no LICENSE while
docs/security/2026-07-02-license-policy.jsonitself blocks unlicensed components. This adds an Apache-2.0LICENSEwith copyright holder "ContextualWisdomLab" (separate commit so it can be dropped independently). The HYOSUNG footer text was deliberately not modified.Tests
DefaultConversionWorkerTest,DefaultDocumentConversionServiceTest).pdfwithout%PDF-magic and non-PDF uploads still get the placeholder pathFileSystemArtifactStoreTest)ArtifactStoreConfigTest,ArtifactStorePropertiesTest)Gates (all green locally)
mvn -q -DskipTests compile(warning/deprecated budget 0,-Werror)mvn -q verify/mvn testcom.clearfolio.viewer.*: 0 missed lines / 0 missed branchesmvn -q -DskipTests javadoc:javadoc: no warningsmarkdownlinton changed docspython3 scripts/check_sbom_license_policy.py …: 0 violations (engineering-review mode)SUCCEEDED→ artifact bytes byte-identical to upload; missing token → 401;Range: bytes=0-4→ 206/5 bytes;data/artifacts/<jobId>.pdf+.meta.propertieswritten to disk; docx upload → placeholder PDFBandScope integration note
BandScope currently integrates via viewer-contract +
pdfjs-diston its own; once this lands it can swap to clearfolio's/api/v1/convert/jobs+ signed/artifacts/{docId}.pdfendpoints for real sheet-music viewing.🤖 Generated with Claude Code
https://claude.ai/code/session_01RjGVapDZ3k7V7zKYk16P4C