Skip to content

Release 0.7.0 — quality improvements: security, correctness, performance, structure#5

Merged
DanMeon merged 8 commits into
mainfrom
feature/quality-improvements
Jun 2, 2026
Merged

Release 0.7.0 — quality improvements: security, correctness, performance, structure#5
DanMeon merged 8 commits into
mainfrom
feature/quality-improvements

Conversation

@DanMeon

@DanMeon DanMeon commented Jun 2, 2026

Copy link
Copy Markdown
Owner

This branch lands the quality-improvements line and bumps the version to 0.7.0. Every item was implemented in a focused session and independently verified (code-reviewer / security-auditor) with the full unit suite, ruff, and pyright passing.

Security (S1–S3)

  • Codegen now runs untrusted, LLM-generated scripts in an OS-level Docker sandbox by default, and fails closed (CodegenSecurityError) when none is available — no more silent execution in a non-isolating subprocess.
  • DockerBackend hardened: non-root user, CapDrop=["ALL"], read-only rootfs + tmpfs /tmp, network disabled, no-new-privileges, optional gVisor/seccomp.
  • SubprocessBackend demoted to explicit trusted/dev-only opt-in (python -I + reduced builtins, process-group kill, fail-closed rlimits); clearly documented as not a security boundary.
  • Script cache is integrity-protected (0700/0600 perms + per-user HMAC, full 256-bit signature).

Correctness

  • thinking=True is now honored in direct extraction and suggest_schema (was codegen-only). (C1)
  • Codegen records that fail validation are surfaced instead of silently dropped. (C2)
  • Non-UTF-8 CSVs raise ReaderError and honor a configurable csv_encoding. (C3)
  • Extraction error handling no longer mislabels internal bugs as LLM failures. (C4)
  • Confidence scoring no longer silently defaults a missing level. (B2)

Performance

  • Chunked single-sheet extraction runs concurrently under a bounded semaphore. (P1)
  • Anthropic prompt caching marks only the static system prompt, not variable sheet data. (P2)
  • Chunk sizing greedy bin-packs rows by measured token cost so each chunk fits the budget. (P4)
  • find_empty_rows drops a full-range set allocation. (B1)

Structure

  • Extractor decomposed into a thin facade over ExtractionPipeline; report assembly and concurrency moved into extraction/. (A1)
  • Reader dispatch centralized into READER_REGISTRY, shared by the extractor and MCP server; dead ExcelReader protocol replaced by WorkbookReader + ReaderOptions. (A2, A3)
  • Public API and behavior unchanged — pure refactor.

⚠️ Breaking change

Codegen with the default config now requires a sandbox. Without xlstruct[docker] installed, codegen fails closed with CodegenSecurityError. To restore the previous unsandboxed behavior in a trusted environment:

  • ExtractorConfig(codegen_sandbox="subprocess"), or
  • execution_backend=SubprocessBackend(trusted=True)

Direct (non-codegen) extraction is unaffected. Existing codegen caches are invalidated once (signature widened).

Verification

Each session was reviewed by a fresh reviewer agent. Unit suite: 444 passed, 15 skipped; ruff and pyright clean. (3 GCS integration tests require a local fake-gcs-server and are environment-only failures.)

🤖 Generated with Claude Code

DanMeon and others added 8 commits June 1, 2026 12:35
…d CSV reading

- honor extended thinking in direct extraction and suggest_schema (was codegen-only)
- surface codegen records that fail validation instead of dropping them silently
- run chunked single-sheet extraction concurrently with a bounded semaphore
- cache the static system prompt only, not the variable user message
- narrow extraction error handling to the LLM call so our own bugs aren't mislabeled
- wrap CSV decode failures as ReaderError and thread a configurable csv_encoding
- fast-fail empty sheets and drop the silent confidence default

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The .serena/ and .claude/ directories are machine-local agent tooling, and
docs/improvement-plan.md carries a security PoC that must never be published —
ignore all three and stop tracking the previously-committed Serena files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hardening)

LLM-generated codegen scripts are untrusted code and the subprocess AST scan was
never a real boundary. Codegen now runs in an OS-level Docker sandbox by default
and fails closed (CodegenSecurityError) when none is available; the non-isolating
subprocess backend is now an explicit trusted/dev-only opt-in. Also hardens the
Docker backend (non-root, CapDrop ALL, read-only rootfs + tmpfs, seccomp/gVisor
knobs) and adds per-user HMAC integrity + 0700/0600 perms to the script cache.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he budget

split() used a sampled total with proportional row splitting, so chunks could
exceed token_budget when token density varied by position or rows differed in
size; it now greedy bin-packs rows by measured cost with the header reserved.
Also harden token counting to encode_ordinary so cell content like
"<|endoftext|>" no longer raises, and drop the full-range set allocation in
find_non_empty_rows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Structural, behavior-preserving refactor (A1/A2/A3 from the internal plan).
Public API, method signatures, and *_sync wrappers are unchanged; the full
unit suite stays green (434 passed, 15 skipped).

A2/A3 — reader dispatch:
- Add reader/dispatch.py: READER_REGISTRY (extension -> reader adapter),
  get_source_ext, read_workbook. Adding a new format is now a one-place change.
- Extractor._load_workbook and mcp_server._load_sheet share read_workbook,
  removing the duplicated .csv/else branch and the MCP server's private-member
  access (Extractor._get_source_ext via reportPrivateUsage).
- Replace the dead ExcelReader protocol (matched no implementation) with a real
  contract: ReaderOptions (options object) + WorkbookReader. Concrete reader
  .read() signatures are unchanged (they are directly tested).

A1 — Extractor decomposition (conservative, mechanical extraction):
- extraction/concurrency.py: run_concurrent() folds the near-identical
  bounded-concurrency + progress closures from extract_batch/extract_workbook.
- extraction/report.py: build_extraction_report() extracts the provenance/
  confidence assembly out of extract().
- Extractor._resolve_auto_mode() dedupes AUTO routing across the configured and
  streaming paths.

The test-locked private surface (_load_workbook, _engine, _chunk_splitter,
_run_codegen, _get_codegen, _run_sheet_extraction, _get_source_ext) stays on
Extractor. extractor.py: 1,121 -> 1,020 lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cstring

- Extractor.extract(source, schema) (legacy path) now calls
  _require_non_empty_sheet() before extraction, matching the config path. A
  0-row sheet raises ReaderError(READER_PARSE_FAILED) instead of wasting an LLM
  call and failing confusingly later (B3). Adds a regression test.
- ExecutionBackend docstring: SubprocessBackend is no longer the default after
  the S1 sandbox hardening; Docker is the default for untrusted codegen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Behavior-preserving: all orchestration moves to a new ExtractionPipeline and
Extractor delegates with its public API unchanged. ExtractionResult moves to
its own module (re-exported) to break the facade-pipeline import cycle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump the version (pyproject, __init__.__version__, lockfile) and finalize the
CHANGELOG for the quality-improvements line: codegen sandboxing (S1-S3),
correctness fixes (C1-C4), performance (P1/P2/P4), and the Extractor →
ExtractionPipeline refactor (A1-A3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DanMeon DanMeon merged commit ad0eaee into main Jun 2, 2026
8 checks passed
@DanMeon DanMeon deleted the feature/quality-improvements branch June 2, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant