Skip to content

v0.1.3 — DOCX/XLSX/PDF report renderers (issue #14)

Choose a tag to compare

@techeretic techeretic released this 29 Aug 08:08
· 47 commits to master since this release

Completes issue #14 — the three binary report formats — on top of v0.1.2. Reports now render as Markdown (source of truth), HTML, DOCX, XLSX, or PDF via preferences.default_report_format. Each format is a deterministic view over the same provenance-tracked (claims, gaps): the content — every claim, gap, and provenance entry — is identical across all five (SD-06); only the presentation differs. No new security advisories: this is a feature release. 323/323 tests (was 308, +15), typecheck + build green, conformance unaffected (no new egress surface).

What landed

  • DOCX / XLSX / PDF renderers (src/orchestrator/{docx,xlsx,pdf}.ts) — deterministic views over the same (claims, gaps):
    • DOCX — WordprocessingML package: headings, a clearly-labeled model-narrative Summary, findings grouped by task with inline claim refs, an explicit Gaps section, and a bordered provenance table. XML-escaped; multi-line claim text keeps its line breaks.
    • XLSX — SpreadsheetML: one worksheet per section (Summary when present, Findings, Gaps, Provenance), inline strings; the report title is carried.
    • PDF — a minimal PDF 1.4: A4 pages, base-14 Helvetica (regular/bold/oblique) with WinAnsiEncoding, word-wrapped paginated flow (a provenance entry never splits across pages), a page-number footer, and a correct xref table.
  • Zero new dependencies. The containers are hand-rolled (the repo keeps the npm install clean — 3 runtime deps): a deterministic STORE ZIP writer (zip.ts — fixed timestamps + part order, so identical input → byte-identical archive) underpins DOCX/XLSX, plus shared XML escaping (xml.ts).
  • The binary seam. These are byte artifacts, not text — none has a lossless UTF-8 string form — so the string-based pipeline gained a binary path:
    • renderReportArtifact(format, input): Buffer (all five) alongside the text-only renderReport; OrchestratorResult / LoopResult / ReportToolResult carry the artifact in-band as reportArtifactB64 (base64).
    • The File Manager gains a byte-exact writeBinary (magic-prefix validated: ZIP PK for docx/xlsx, %PDF- for pdf; journaled as base64 so undo is byte-exact; same confinement/ignore/confirmation gates + audit). A text write() to a binary name is refused fail-closed.
    • Wired through createOrchestrator, the loop's multi-round consolidation + narrate re-renders (both handle binary), and the plugin's sandy.report.
  • Fail-closed, unchanged law. REPORT_FORMATS now lists all five, so loadSandyConfig admits them; the format check stays fail-closed as defense in depth (a schema/renderer divergence is a ConfigError, never a silent Markdown fallback). Provenance/claims are identical across formats (SD-06).

Verification

  • npm run typecheck && npm test && npm run build → 323/323 (was 308, +15), green.
  • The hand-written PDF's structure (xref offsets, startxref, object numbering) was validated directly with a no-dependency check — every xref offset points exactly at its N 0 obj header, single- and multi-page.
  • Conformance (in-process + the Docker/Firejail boundary × mode matrix) is unaffected: all rendering and writing is in-process through the existing confined File Manager — no new network/egress surface.