<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<meta name="description" content="temprs — Engineering report. Stack-based temporary file manager in Rust: 4,767 production Rust lines + 57,945 test Rust lines, 5,644 tests (5,059 unit + 585 integration), v2.9.12, 290 commits, 5 direct deps, two binaries (tp + temprs), zsh completion + two man pages.">
<title>temprs — Engineering Report</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="hud-static.css">
<link rel="stylesheet" href="tutorial.css">
<style>
.tutorial-main { max-width: 76rem; }
.bar-wrap { background:var(--bg-primary);border:1px solid var(--border);border-radius:2px;height:18px;position:relative;overflow:hidden; }
.bar-fill { height:100%;border-radius:1px;transition:width 1.2s cubic-bezier(.22,1,.36,1); }
.bar-fill.green { background:linear-gradient(90deg,#39ff14,#20c00a);box-shadow:0 0 8px rgba(57,255,20,.4); }
.bar-fill.cyan { background:linear-gradient(90deg,#05d9e8,#0891b2);box-shadow:0 0 8px rgba(5,217,232,.4); }
.bar-fill.yellow { background:linear-gradient(90deg,#ffb800,#e8a000);box-shadow:0 0 8px rgba(255,184,0,.35); }
.bar-fill.magenta{ background:linear-gradient(90deg,#d300c5,#a000a0);box-shadow:0 0 8px rgba(211,0,197,.35); }
.bar-pct { position:absolute;right:6px;top:0;line-height:18px;font-size:10px;font-weight:700;color:#fff;text-shadow:0 0 4px #000;font-family:'Orbitron',sans-serif; }
.file-table { width:100%;border-collapse:collapse;margin:0.6rem 0;font-size:12px; }
.file-table th { background:var(--bg-secondary);color:var(--cyan);font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;text-align:left;padding:7px 10px;border:1px solid var(--border); }
.file-table td { padding:6px 10px;border:1px solid var(--border);color:var(--text-dim);vertical-align:middle; }
.file-table tr:hover td { background:var(--bg-hover); }
.file-table td:first-child { font-family:'Share Tech Mono',monospace;color:var(--accent-light);font-weight:600;white-space:nowrap; }
.file-table .num { text-align:right;font-family:'Share Tech Mono',monospace; }
.file-table .total-row td { background:var(--bg-secondary);font-weight:700;color:var(--text);border-top:2px solid var(--cyan); }
.file-table code { font-size:11px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }
.stat-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));gap:0.75rem;margin:1.2rem 0; }
.stat-card { border:1px solid var(--border);border-top:3px solid var(--cyan);background:var(--bg-card);padding:1rem 1.2rem;border-radius:2px;text-align:center; }
.stat-card .stat-val { font-family:'Orbitron',sans-serif;font-size:28px;font-weight:900;color:var(--cyan);line-height:1.1;text-shadow:0 0 20px var(--cyan-glow); }
.stat-card .stat-val.accent { color:var(--accent);text-shadow:0 0 20px var(--accent-glow); }
.stat-card .stat-val.green { color:var(--green);text-shadow:0 0 20px rgba(57,255,20,.3); }
.stat-card .stat-label { font-family:'Orbitron',sans-serif;font-size:9px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--text-muted);margin-top:0.5rem; }
@keyframes glow-pulse { 0%,100%{text-shadow:0 0 20px var(--cyan-glow)}50%{text-shadow:0 0 40px var(--cyan-glow),0 0 80px var(--cyan-dim)} }
.stat-card .stat-val { animation:glow-pulse 3s ease-in-out infinite; }
.mapping-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(20rem,1fr));gap:0.65rem;margin:0.8rem 0; }
.mapping-card { border:1px solid var(--border);border-left:3px solid var(--magenta);background:var(--bg-card);padding:0.6rem 0.9rem;border-radius:2px; }
.mapping-card h4 { font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--magenta);margin:0 0 0.3rem; }
.mapping-card p { margin:0;font-size:11px;color:var(--text-dim);line-height:1.5; }
.mapping-card code { font-size:10.5px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }
.section-rule { border:none;border-top:1px dashed var(--border);margin:2rem 0; }
.feature-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(22rem,1fr));gap:0.65rem;margin:0.8rem 0; }
.feature-card { border:1px solid var(--border);border-left:3px solid var(--cyan);background:var(--bg-card);padding:0.7rem 1rem;border-radius:2px; }
.feature-card h4 { font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--cyan);margin:0 0 0.3rem; }
.feature-card p { margin:0;font-size:11px;color:var(--text-dim);line-height:1.55; }
.feature-card code { font-size:10.5px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }
.feature-card ul { margin:0.3rem 0 0;padding-left:1.2rem;font-size:11px;color:var(--text-dim);line-height:1.6; }
.feature-card li code { font-size:10px; }
</style>
</head>
<body>
<div class="app tutorial-app" id="reportApp">
<div class="crt-scanline" id="crtH" aria-hidden="true"></div>
<div class="crt-scanline-v" id="crtV" aria-hidden="true"></div>
<header class="tutorial-header">
<div class="tutorial-header-inner">
<div>
<h1 class="tutorial-brand">// TEMPRS — ENGINEERING REPORT</h1>
<nav class="tutorial-crumbs" aria-label="Breadcrumb">
<span class="current">Engineering Report</span>
<span class="sep">/</span>
<a href="index.html">Temprs Docs</a>
<span class="sep">/</span>
<a href="https://github.com/MenkeTechnologies/temprs" target="_blank" rel="noopener noreferrer">GitHub</a>
<span class="sep">/</span>
<a href="https://crates.io/crates/temprs" target="_blank" rel="noopener noreferrer">crates.io</a>
</nav>
<p style="margin:0.35rem 0 0;font-family:'Share Tech Mono',monospace;font-size:11px;color:var(--text-dim);letter-spacing:0.03em;opacity:0.75;">
Stack-based temporary file manager in Rust · v2.9.12 · clap CLI · <code>fs2</code> flock-protected master record · null-byte delimited storage · atomic renames
</p>
</div>
<div class="tutorial-toolbar">
<button type="button" class="btn btn-secondary" id="btnTheme" title="Toggle light/dark">Theme</button>
<button type="button" class="btn btn-secondary active" id="btnCrt" title="CRT scanline overlay">CRT</button>
<button type="button" class="btn btn-secondary active" id="btnNeon" title="Neon border pulse">Neon</button>
</div>
</div>
</header>
<main class="tutorial-main">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 1: EXECUTIVE SUMMARY -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">>_</span>EXECUTIVE SUMMARY</h2>
<p class="tutorial-subtitle">temprs is a single-binary stack-based temporary file manager written in Rust. The CLI is built on <code>clap</code>; persistence runs through a null-byte delimited master record with atomic renames and <code>flock</code>-protected exclusive access. Every operation that takes an <code>INDEX</code> also accepts a tag name (<code>@name</code>) — resolution falls through numeric parse to tag lookup. Two binaries (<code>tp</code> and <code>temprs</code>) ship from one Cargo target. <strong>4,767 production Rust lines + 57,945 test Rust lines + 5,644 tests (5,059 unit + 585 integration)</strong> back the surface: stack mutation (push, pop, shift, unshift, insert, remove, move, swap, duplicate, reverse, sort), inspection (head, tail, wc, size, path, info), transformation (find-and-replace, grep, concat, diff), and editor handoff. CI runs <code>check + test + fmt + clippy + doc + release build</code> on every push.</p>
<div class="stat-grid">
<div class="stat-card"><div class="stat-val">4,767</div><div class="stat-label">Production Rust Lines</div></div>
<div class="stat-card"><div class="stat-val accent">5,644</div><div class="stat-label">Tests (Verified Passing)</div></div>
<div class="stat-card"><div class="stat-val green">2.9.12</div><div class="stat-label">Crates.io Version</div></div>
<div class="stat-card"><div class="stat-val">2</div><div class="stat-label">Binaries (<code>tp</code>+<code>temprs</code>)</div></div>
<div class="stat-card"><div class="stat-val">5</div><div class="stat-label">Direct Dependencies</div></div>
<div class="stat-card"><div class="stat-val">99</div><div class="stat-label">Transitive Crates</div></div>
<div class="stat-card"><div class="stat-val">290</div><div class="stat-label">Git Commits</div></div>
</div>
<div style="margin:1.2rem 0;">
<p style="font-size:11px;color:var(--text-muted);letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px;font-family:'Orbitron',sans-serif;font-weight:700;">Source distribution — 62,712 Rust lines total</p>
<div class="bar-wrap" style="height:26px;">
<div class="bar-fill cyan" style="width:7.6%;"></div>
<span class="bar-pct" style="font-size:12px;">4,767 production / 57,945 test · 92.4% test code</span>
</div>
<p style="font-size:10px;color:var(--text-muted);margin-top:4px;">Production source: <code>src/lib.rs</code>, <code>src/main.rs</code>, <code>src/util/{consts,utils,mod}.rs</code>, <code>src/model/{mod,app,state/mod}.rs</code>, and the CLI definition in <code>src/model/opts.rs</code> (lines 1–259). Test source: <code>tests/integration.rs</code> + 7 contract/edge test files + inline test modules in <code>src/model/opts.rs</code>, <code>src/model/apply_permutation_tests.rs</code>, <code>src/model/state/tests.rs</code>.</p>
</div>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 2: SOURCE LAYOUT -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">~</span>SOURCE LAYOUT</h2>
<p class="tutorial-subtitle">Module-level breakdown of the Rust source tree. <code>opts.rs</code> dominates the line count because the inline test module holds 4,449 combinatorial CLI argument tests — the production CLI definition itself is the first 259 lines of that file.</p>
<table class="file-table">
<thead>
<tr>
<th>File</th>
<th class="num">Lines</th>
<th class="num">Tests</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr><td>src/main.rs</td><td class="num">9</td><td class="num">—</td><td>Binary entrypoint (shared by <code>tp</code> + <code>temprs</code>)</td></tr>
<tr><td>src/lib.rs</td><td class="num">10</td><td class="num">—</td><td>Library root; re-exports model + util</td></tr>
<tr><td>src/util/mod.rs</td><td class="num">4</td><td class="num">—</td><td>Util module declaration</td></tr>
<tr><td>src/util/consts.rs</td><td class="num">918</td><td class="num">87</td><td>Flag names, error strings, banner, separators (inline tests for string contracts)</td></tr>
<tr><td>src/util/utils.rs</td><td class="num">2,204</td><td class="num">174</td><td>Stack ops, master-record I/O, flock, filesystem helpers (inline unit tests)</td></tr>
<tr><td>src/model/mod.rs</td><td class="num">9</td><td class="num">—</td><td>Model module declaration</td></tr>
<tr><td>src/model/app.rs</td><td class="num">1,102</td><td class="num">6</td><td>Top-level dispatch — matches parsed flags to handlers (smoke tests)</td></tr>
<tr><td>src/model/state/mod.rs</td><td class="num">252</td><td class="num">—</td><td><code>State</code> struct — in-memory mirror of the master record</td></tr>
<tr><td>src/model/state/tests.rs</td><td class="num">1,980</td><td class="num">181</td><td>State invariants — serialization round-trips, name uniqueness, edge cases</td></tr>
<tr><td>src/model/opts.rs (CLI def)</td><td class="num">259</td><td class="num">—</td><td>Production clap definition + cyberpunk help template</td></tr>
<tr><td>src/model/opts.rs (tests)</td><td class="num">46,866</td><td class="num">4,449</td><td>Combinatorial flag parsing tests — long form, short form, value parsing, mutual exclusion</td></tr>
<tr><td>src/model/apply_permutation_tests.rs</td><td class="num">1,531</td><td class="num">162</td><td>Stack mutation permutation suite — move/swap/sort/reverse correctness</td></tr>
<tr><td>tests/integration.rs</td><td class="num">5,900</td><td class="num">530</td><td>End-to-end CLI tests — spawn the binary, pipe stdin, assert stdout/stderr/files</td></tr>
<tr><td>tests/edge_cases.rs</td><td class="num">428</td><td class="num">23</td><td>Edge-case CLI scenarios — malformed input, overflow, exotic UTF-8</td></tr>
<tr><td>tests/contract_input_output_master.rs</td><td class="num">267</td><td class="num">9</td><td>Master record input/output contract pins</td></tr>
<tr><td>tests/contract_algebra_round4.rs</td><td class="num">256</td><td class="num">6</td><td>Stack-algebra round-trip contract pins</td></tr>
<tr><td>tests/contract_name_invariants.rs</td><td class="num">193</td><td class="num">8</td><td>@name resolution invariants</td></tr>
<tr><td>tests/tag_alignment_after_mutation.rs</td><td class="num">214</td><td class="num">3</td><td>Name/path parallel-array sync across shift, pop, rev, remove</td></tr>
<tr><td>tests/master_record_tag_roundtrip.rs</td><td class="num">116</td><td class="num">3</td><td>Multi-tag master-record round-trip alignment</td></tr>
<tr><td>tests/expire_invalid_hours.rs</td><td class="num">194</td><td class="num">3</td><td><code>--expire</code> rejects negative / NaN / inf hours</td></tr>
<tr><td>benches/benchmarks.rs</td><td class="num">144</td><td class="num">—</td><td>Criterion benchmarks — stack ops, master record round-trips</td></tr>
<tr class="total-row"><td>TOTAL Rust</td><td class="num">62,856</td><td class="num">5,644</td><td>All <code>cargo test</code> functions pass on every push to main</td></tr>
</tbody>
</table>
<p style="font-size:11px;color:var(--text-muted);margin-top:0.5rem;">
Note: the test-attribute grep yields <code>4,449 + 162 + 181 + 174 + 87 + 6 + 530 + 23 + 9 + 6 + 8 + 3 + 3 + 3 = 5,644</code> across inline + tests/ files.
</p>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 3: DEPENDENCIES -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">$</span>DEPENDENCIES</h2>
<p class="tutorial-subtitle">Five direct runtime dependencies, one dev dependency. The lock file resolves to 99 transitive crates. Selection criteria: foundational, audit-friendly, durable — no flavor-of-the-month wrappers.</p>
<table class="file-table">
<thead>
<tr><th>Crate</th><th>Version</th><th>Role</th><th>Why</th></tr>
</thead>
<tbody>
<tr><td>clap</td><td>4.6</td><td>CLI parser</td><td>De facto standard for Rust CLIs; derives long/short, mutual exclusion, value validation, custom help template</td></tr>
<tr><td>fs2</td><td>0.4</td><td>File locking</td><td><code>FileExt::lock_exclusive</code> / <code>unlock</code> — the master record's concurrency guard</td></tr>
<tr><td>log</td><td>0.4</td><td>Logging facade</td><td>Decouples library logging from the binary's choice of backend</td></tr>
<tr><td>simple_logger</td><td>5.2</td><td>Log backend (active)</td><td>The runtime logger: <code>State::new</code> calls <code>simple_logger::init_with_level(Info)</code> — zero-config, on by default</td></tr>
<tr><td>env_logger</td><td>0.11</td><td>Log backend (declared)</td><td>Declared in <code>Cargo.toml</code> for an env-driven backend; the binary's default init path uses <code>simple_logger</code></td></tr>
<tr><td>criterion (dev)</td><td>0.8</td><td>Benchmark harness</td><td>Statistical benchmarking with regression detection — gated to <code>[dev-dependencies]</code></td></tr>
</tbody>
</table>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 4: STORAGE FORMAT -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">&</span>STORAGE FORMAT</h2>
<p class="tutorial-subtitle">The persistent state of the stack lives in a single master record file. The format is null-byte delimited so any filename — including ones with newlines, tabs, spaces, or other shell-hostile characters — round-trips losslessly. Writes are atomic; reads are <code>flock</code>-protected.</p>
<div class="mapping-grid">
<div class="mapping-card"><h4>Field delimiter</h4><p><code>\0</code> separates fields within a record (filename, optional tag).</p></div>
<div class="mapping-card"><h4>Record delimiter</h4><p><code>\0\0</code> separates records. Stack order = file order.</p></div>
<div class="mapping-card"><h4>Atomic writes</h4><p>Write to <code>temprs-stack.tmp</code>, <code>rename()</code> to <code>temprs-stack</code>. A crash leaves the previous master intact.</p></div>
<div class="mapping-card"><h4>Exclusive locking</h4><p><code>fs2::FileExt::lock_exclusive</code> on <code>temprs-stack.lock</code> wraps every read-modify-write. Concurrent shells serialize.</p></div>
<div class="mapping-card"><h4>Auto-recovery</h4><p>Empty or malformed records on read are silently skipped and dropped on the next write.</p></div>
<div class="mapping-card"><h4>Storage root</h4><p><code>$TMPDIR/temprs</code> by default; override with <code>TEMPRS_DIR</code>. The master record (<code>temprs-stack</code>) lives alongside the <code>tempfile<timestamp></code> payloads.</p></div>
<div class="mapping-card"><h4>Purge safety</h4><p><code>tp -c</code> refuses to clear unless the directory basename contains <code>temprs</code> — a guard against <code>TEMPRS_DIR=$HOME</code> wiping unrelated files.</p></div>
</div>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 4b: DISPATCH & RESOLUTION -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">%</span>DISPATCH & RESOLUTION</h2>
<p class="tutorial-subtitle">One code path drives both binaries. <code>main()</code> constructs a <code>TempApp</code> and calls <code>run()</code>; <code>run()</code> reads the parsed <code>clap</code> matches and dispatches each flag in turn. Index/name resolution is a single fall-through function shared by every operation that takes an <code>INDEX</code>.</p>
<div class="mapping-grid">
<div class="mapping-card"><h4>Entry point</h4><p><code>src/main.rs</code> — <code>TempApp::new()</code> then <code>app.run()</code>. <code>tp</code> and <code>temprs</code> are the same binary built from one target.</p></div>
<div class="mapping-card"><h4>Startup (<code>State::new</code>)</h4><p>Init logger (<code>simple_logger</code>, Info), resolve <code>TEMPRS_DIR</code> (default <code>$TMPDIR/temprs</code>), create the directory if absent, then take an exclusive <code>flock</code> on <code>temprs-stack.lock</code>.</p></div>
<div class="mapping-card"><h4>CLI definition</h4><p><code>src/model/opts.rs</code> (lines 1–259) — the <code>clap</code> <code>Command</code> with grouped cyberpunk help template, short + long forms, value names, and <code>allow_hyphen_values</code> so negative indices parse.</p></div>
<div class="mapping-card"><h4>Dispatch</h4><p><code>TempApp::run</code> in <code>src/model/app.rs</code> — a flat sequence of <code>get_flag</code> / <code>get_one</code> / <code>get_many</code> checks, each calling the matching handler.</p></div>
<div class="mapping-card"><h4>Index/name resolution</h4><p><code>resolve_idx</code> — <code>parse::<i32>()</code> first (negatives mapped via <code>util_transform_idx</code>), then a name lookup by <code>position()</code> over the parallel names vector. Unresolved arguments terminate with <em>Invalid specified index</em>.</p></div>
<div class="mapping-card"><h4>Parallel arrays</h4><p>The stack is two aligned vectors — file paths and optional tags — kept in sync across every mutation. <code>tests/tag_alignment_after_mutation.rs</code> pins that sync across shift, pop, rev, and remove.</p></div>
</div>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 5: CAPABILITY SURFACE -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">@</span>CAPABILITY SURFACE</h2>
<p class="tutorial-subtitle">Grouped by behavior. Every operation that takes <code>INDEX</code> also accepts a tag name — the resolver tries numeric parse first, then falls through to tag lookup. Negative indices count from top of stack.</p>
<div class="feature-grid">
<div class="feature-card"><h4>I/O</h4><p>Push stdin or load a file; read by index or tag; verbose mode also tees to stdout.</p><ul><li><code>tp</code> — push stdin to top</li><li><code>tp FILE</code> — load file to top</li><li><code>tp -i N</code> — write stdin into N</li><li><code>tp -o N</code> — read N to stdout</li><li><code>tp -A N</code> — append stdin to N</li><li><code>tp -v</code> — verbose (also tee to stdout)</li><li><code>tp -q</code> — quiet (suppress create output)</li></ul></div>
<div class="feature-card"><h4>Stack mutation</h4><p>Position-based operations with the full Perl/Ruby array vocabulary.</p><ul><li><code>-p</code> pop, <code>-u</code> unshift, <code>-s</code> shift</li><li><code>-a N</code> insert at N, <code>-r N</code> remove</li><li><code>-M src dst</code> move</li><li><code>-S a b</code> swap</li><li><code>-x N</code> duplicate to top</li></ul></div>
<div class="feature-card"><h4>Bulk</h4><p>Whole-stack transforms and housekeeping.</p><ul><li><code>--rev</code> reverse</li><li><code>--sort name|size|mtime</code></li><li><code>-c</code> purge all</li><li><code>--expire HOURS</code> purge by mtime (fractional)</li></ul></div>
<div class="feature-card"><h4>Listing</h4><p>Five list modes; pick the verbosity you need.</p><ul><li><code>-l</code> names only</li><li><code>-L</code> names + contents</li><li><code>-n</code> numbered names</li><li><code>-N</code> numbered + contents</li><li><code>-k</code> stack size</li></ul></div>
<div class="feature-card"><h4>Inspection</h4><p>Read partial content or metadata without consuming the file.</p><ul><li><code>--head N [LINES]</code></li><li><code>--tail N [LINES]</code></li><li><code>--wc N</code>, <code>--size N</code></li><li><code>--path N</code> — raw FS path for <code>$(...)</code> substitution</li><li><code>-I N</code> — metadata block</li></ul></div>
<div class="feature-card"><h4>Search & transform</h4><p>In-place edits and cross-stack search.</p><ul><li><code>-g PAT</code> — grep all tempfiles (exit 0/1)</li><li><code>--replace N PAT REPL</code> — in-place find/replace, prints replacement count</li></ul></div>
<div class="feature-card"><h4>Combine & compare</h4><p>Treat the stack as a working set; combine or diff entries.</p><ul><li><code>-C a b c</code> — concat to stdout in given order</li><li><code>-D a b</code> — unified diff of two tempfiles</li></ul></div>
<div class="feature-card"><h4>Tagging</h4><p>Optional string aliases. Travel with files through every mutation.</p><ul><li><code>-w NAME</code> — tag on creation</li><li><code>-R src NAME</code> — rename</li><li>Names display as <code>@name</code> in listings</li></ul></div>
<div class="feature-card"><h4>Editor</h4><p>Direct handoff to <code>$EDITOR</code>.</p><ul><li><code>-e N</code> — open INDEX in <code>$EDITOR</code></li><li>Fallback: <code>vi</code></li><li>Works with <code>-1</code> for top of stack</li></ul></div>
</div>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 6: TEST POSTURE -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">!</span>TEST POSTURE</h2>
<p class="tutorial-subtitle">Test suites cover three layers: argument parsing, in-memory state invariants, and end-to-end CLI behavior. Aggregate: <strong>5,644 tests pass</strong> in <code>cargo test</code>, all green in CI.</p>
<table class="file-table">
<thead>
<tr><th>Suite</th><th>File</th><th class="num">Tests</th><th>Coverage</th></tr>
</thead>
<tbody>
<tr><td>CLI parsing</td><td>src/model/opts.rs</td><td class="num">4,449</td><td>Combinatorial — every flag, long + short, value validation, mutual exclusion</td></tr>
<tr><td>Stack permutations</td><td>src/model/apply_permutation_tests.rs</td><td class="num">162</td><td>Move/swap/sort/reverse correctness across every starting permutation</td></tr>
<tr><td>State invariants</td><td>src/model/state/tests.rs</td><td class="num">181</td><td>Serialization round-trips, name uniqueness, malformed-record recovery</td></tr>
<tr><td>Integration</td><td>tests/integration.rs</td><td class="num">530</td><td>End-to-end — spawn binary, pipe stdin, assert stdout/stderr/exit/files</td></tr>
<tr><td>Inline (consts + utils + app)</td><td>src/util + src/model/app.rs</td><td class="num">267</td><td>String contracts, stack op smoke tests, master-record I/O, flock</td></tr>
<tr><td>Edge cases</td><td>tests/edge_cases.rs</td><td class="num">23</td><td>Malformed input, overflow, exotic UTF-8 boundary conditions</td></tr>
<tr><td>Contract pins</td><td>tests/contract_*.rs + tag/master/expire</td><td class="num">32</td><td>Master-record I/O, stack-algebra, @name invariants, parallel-array alignment, --expire input validation</td></tr>
<tr class="total-row"><td>TOTAL</td><td>cargo test</td><td class="num">5,644</td><td>Wall-clock: ~3.4s (lib 0.35s + integration 3.01s); CI gate on every push to main</td></tr>
</tbody>
</table>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 7: SHIP SURFACE -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">#</span>SHIP SURFACE</h2>
<p class="tutorial-subtitle">What you get when you <code>cargo install temprs</code> or unpack a release artifact.</p>
<div class="mapping-grid">
<div class="mapping-card"><h4>Binaries</h4><p><code>tp</code> — short form. <code>temprs</code> — long form. Identical builds from one Cargo target; LTO + <code>panic=abort</code> + <code>strip=true</code> + <code>codegen-units=1</code> for release.</p></div>
<div class="mapping-card"><h4>zsh completion</h4><p><code>completions/_temprs</code> — dynamic resolution of stack indices, filenames, and <code>@name</code> tags. Tab-completion sees the live stack, not a static word list.</p></div>
<div class="mapping-card"><h4>man pages</h4><p>Two groff manuals under <code>man/man1/</code>: <code>temprs.1</code> (320 lines, short reference, <code>man temprs</code>) and <code>temprsall.1</code> (653 lines, full manual, <code>man temprsall</code>). Install to a directory on <code>$MANPATH</code>.</p></div>
<div class="mapping-card"><h4>Library crate</h4><p><code>temprs</code> is also published as a library (<code>src/lib.rs</code>) — downstream crates can <code>use temprs::*</code> for state, dispatch, and stack ops without forking the binary.</p></div>
<div class="mapping-card"><h4>CI workflow</h4><p><code>.github/workflows/ci.yml</code> runs check + test + fmt + clippy + doc + release build on every push to <code>main</code> and every pull request. Runnable manually via workflow dispatch.</p></div>
<div class="mapping-card"><h4>Benchmarks</h4><p><code>benches/benchmarks.rs</code> — criterion harness over stack ops and master-record round-trips; run with <code>cargo bench</code>.</p></div>
</div>
<hr class="section-rule">
<!-- ═══════════════════════════════════════ -->
<!-- SECTION 8: VERSION HISTORY -->
<!-- ═══════════════════════════════════════ -->
<h2 class="tutorial-title"><span class="step-hash">v</span>VERSION HISTORY</h2>
<p class="tutorial-subtitle">Current crate: <code>temprs 2.9.12</code>. 290 commits on <code>main</code>. Last five commits:</p>
<table class="file-table">
<thead><tr><th>Hash</th><th>Subject</th></tr></thead>
<tbody>
<tr><td>eacc0c1e40</td><td>docs: expand + reconcile index/report/reference</td></tr>
<tr><td>8443bc0305</td><td>docs: reconcile stale references and counts with current code</td></tr>
<tr><td>d1d80f31a7</td><td>docs: refresh engineering report/index HTML stats to current source of truth</td></tr>
<tr><td>4938e65dcb</td><td>tests: master-record: parallel-array alignment across multi-tag round-trips</td></tr>
<tr><td>d0f25ce2a7</td><td>docs: refresh stale counts — CI job list adds Docs gates + Polish gates</td></tr>
</tbody>
</table>
<p style="font-size:11px;color:var(--text-muted);margin-top:0.7rem;">Full history: <a href="https://github.com/MenkeTechnologies/temprs/commits/main" target="_blank" rel="noopener noreferrer">github.com/MenkeTechnologies/temprs/commits/main</a></p>
<hr class="section-rule">
<p style="text-align:center;font-family:'Orbitron',sans-serif;font-size:10px;letter-spacing:2px;color:var(--text-muted);text-transform:uppercase;margin:2rem 0 0;">
>>> JACK IN. PUSH YOUR DATA. OWN YOUR TEMP FILES. <<<
</p>
</main>
</div>
<script src="hud-theme.js" defer></script>
</body>
</html>