Skip to content

feat(v0.3): external RTK composition with staged receipts and secure CCR capture - #1

Merged
snchimata merged 3 commits into
mainfrom
feat/v0.3-rtk-composition
Jul 18, 2026
Merged

feat(v0.3): external RTK composition with staged receipts and secure CCR capture#1
snchimata merged 3 commits into
mainfrom
feat/v0.3-rtk-composition

Conversation

@snchimata

@snchimata snchimata commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the RTK half of v0.3 Phase 7 (F-054 + F-055): compose an external, optional RTK as a command-specific filtering stage before tokenfold's generic pipeline, with honest staged receipts and secure raw capture. RTK is never bundled and never mandatory.

Companion doc updates (Phase 7 status, npm/TS shipped in v0.3.1) live in docs/solution-design/*, which is gitignored, so they are not part of this diff.

What's in it

  • wrap --rtk — spawns rtk <child argv...> (no shell) and treats RTK's output as the input to compress(). Resolved via TOKENFOLD_RTK_BIN or a PATH scan; TOKENFOLD_RTK_DISABLED turns it off.
  • Preflight fail-open — RTK is resolved and version-checked before the child runs; missing/incompatible RTK routes to the existing tokenfold-only path and records an honest unavailable/incompatible stage. Once RTK/the child is spawned, its output and exit code are authoritative — the command is never rerun.
  • No double-filtering — when RTK ran, tokenfold's overlapping command-specific filter pack is skipped; mandatory redaction, generic compression, safety rollback, and never_worse still run (they live inside compress()).
  • Staged raw → RTK → tokenfold receipt — new PipelineReport/PipelineStageReport (additive, #[serde(default)]). RTK savings live only in the RTK stage and total_saved_tokens, never in the top-level saved_tokens.
  • --rtk-capture-raw (CCR) — a per-run, owner-only RTK_TEE_DIR tee (RAII-deleted after its bytes are read), ingested through the redaction/persistence gate. Secret-matched bytes are structurally never persisted (RetrievalStore::store refuses them); missing/truncated captures are labeled unrecoverable, never falsely full.
  • doctor — additive rtk object (status/path/version/compatible/raw_capture/notes); missing is a warning, doctor still exits 0.

Testing

  • 10 integration tests (crates/tokenfold-cli/tests/rtk.rs) driving a fake rtk, covering the ENGINEERING v0.3 gate list: missing/incompatible fail-open, staged receipts, double-filter avoidance, nonzero-child propagation, and CCR complete/secret/absent capture.
  • 3 rtk unit tests (version parsing, capture completeness, RAII dir cleanup).
  • cargo fmt --check, cargo clippy --workspace -D warnings, cargo test --workspace all green.

Adversarial review

A 4-lens review (security / receipt-honesty / fail-open / contract) found one real bug: on the fail-open path the filter pack still runs, so the tokenfold stage was measuring bytes over raw instead of the post-filter compress input, over-crediting the filter's reduction. Fixed (compress_input_bytes = pipeline_input.len()) with a regression test verified to fail without the fix.

Adds PipelineReport + PipelineStageReport and the additive CompressionReport.pipeline
field (serde default, backward-compatible) per INTERFACES.md §1.9/§2. Separates savings
and recoverability across composed stages so RTK's savings are never credited to
tokenfold. F-055.
…CR capture

F-054/F-055: new tokenfold-cli/src/rtk.rs resolves an external, optional rtk executable
(TOKENFOLD_RTK_BIN override or PATH), version-checks it before the child runs, and falls
open to the tokenfold-only path when missing/incompatible. wrap --rtk routes command
output through RTK, skips tokenfold's overlapping command filter (no double-filtering),
keeps mandatory redaction/generic-compress/never_worse, and never reruns a started child.
--rtk-capture-raw opts into a permission-restricted per-run RTK_TEE_DIR tee, ingests the
pre-RTK raw through the redaction/persistence gate (secret-matched bytes never stored),
and deletes the transient capture. doctor --json/human gains an additive rtk object.
Staged raw->RTK->tokenfold receipt via PipelineReport. 10 integration tests with a fake
rtk cover the ENGINEERING v0.3 gate list.
…input

Adversarial review found a receipt-honesty bug: on the wrap --rtk fail-open path
(rtk missing/incompatible), the command filter pack still runs, so pipeline_input is
smaller than raw. build_pipeline_report was passed raw.len() as the tokenfold stage's
input, making its byte savings over-claim the filter's reduction while its token savings
(from report.original_tokens, counted post-filter) did not — internally inconsistent.

Rename post_rtk_bytes -> compress_input_bytes and pass pipeline_input.len() (equal to
raw.len() on the RTK-ran path). CommandReport.raw_output_bytes still reports the true raw
byte count. Adds a regression test driving fail-open + a trusted git-version filter that
removes a deterministic 5 bytes; verified it fails without the fix.
@snchimata
snchimata merged commit 918b428 into main Jul 18, 2026
11 checks passed
@snchimata
snchimata deleted the feat/v0.3-rtk-composition branch July 18, 2026 20: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