Skip to content
David Gary

Machine Learning Engineer · Applied AI

Charlotte, NC · Remote (US)

Open to contract & full-time · Remote (US)

Brief

I build and ship production machine learning: model training, evaluation, and the systems that serve it.

I build applied machine learning end to end: training and evaluating models, then shipping them behind production APIs and infrastructure. My work spans document AI and information extraction, reinforcement learning for 3D bin-packing, and research on parameter-efficient fine-tuning and model compression, alongside the systems, security tooling, and full-stack products I've shipped.

I care about shipping under production constraints: evaluation gates before a model promotes, integrity verification, and numbers that survive a second look. Each entry below lists its results, and each one is something I can show you.

Looking for

Machine learning engineering roles: applied ML, model training and evaluation, and ML systems/platform work. Contract or full-time, remote (US).

Experience

9 roles

Apr 2024 – Present

Remote

Machine Learning Engineer · Watermark Inc.

Led development of QuillParse, Watermark's AI-powered document parsing and data-extraction product.

  • Built a document preprocessing pipeline that extracts structured data from unstructured text, improving model performance by 30%
  • Trained and evaluated successive model versions, reaching a 25% gain in data-extraction accuracy over the baseline
  • Served the trained model from a production API for real-time document parsing by enterprise clients

Aug 2023 – Apr 2024

Remote

Software Engineer · Watermark Inc.

Web development and internal tooling for an AI-driven document parsing startup.

  • Maintained the main company website and shipped new customer-facing features
  • Built internal tools the research team used to organize and search historical documents
  • Annotated training data and evaluated model performance for the early AI product

Apr 2024 – Present

Remote

Software & Machine Learning Engineer · Freelance

Evenings & weekends, alongside my full-time ML role at Watermark.

Contract ML and product engineering across retail platforms, EdTech recommender systems, and production ML tooling.

Jan 2024 – Apr 2024

Remote

Contract Software Engineer · Narrative.ai

Back-end and front-end contract work on a real-time logistics platform: ingesting carrier events, tracking shipment state, and surfacing live status in a production operations dashboard.

  • Built back-end services for real-time shipment tracking, including status ingestion, state management, and carrier data integration
  • Maintained production databases, owning schema design and query optimization for high-volume logistics data
  • Built front-end components for the operations dashboard that surface live shipment status

May 2022 – Aug 2023

Charlotte, NC

Graduate Research Assistant · University of North Carolina at Charlotte

Research on second-order optimization and continual reinforcement learning. I built reproducible experiment frameworks and migrated legacy MATLAB pipelines to Python.

  • Built a research framework to evaluate second-order optimization algorithms
  • Implemented and experimentally tested replay buffer designs for continual reinforcement learning
  • Converted several legacy MATLAB codebases to Python so other researchers could maintain and extend them

Jan 2022 – May 2022

Remote

Machine Learning Engineering Intern · Hexagon Solutions

ML engineering for cybersecurity products including network log analysis, vulnerability visualization, and feature work on a unified security posture dashboard.

  • Scripted network log analysis tools to provide feedback for reinforcement learning agents
  • Performed data analysis and visualization to identify and mitigate security vulnerabilities
  • Added features to a web application that gives clients a unified view of their security posture

Aug 2021 – May 2022

Saint George, UT

Undergraduate Research Assistant · Utah Tech University

Contributed to university-wide scheduling systems and led security research on ML-assisted side-channel analysis.

  • Contributed to implementation of university-wide course scheduling and classroom placement system
  • Led a data-analysis project to find optimal course-enrollment sequences for students
  • Led security projects applying machine learning to side-channel attacks

Research

5 studies

Independent research on optimization, model compression, and efficient fine-tuning, done on a single consumer GPU. Each study tests a claim the field takes for granted, and each one is backed by code and experiments I can walk you through.

Study 01Optimization theory

When Does It Pay to Learn the Geometry?

Python · PyTorch · Optimization · Random Matrix Theory · Decision Theory · Second-Order Methods · Muon

Modern optimizers (Adam, Muon, Shampoo, SOAP) differ mainly in how much curvature structure they estimate: a diagonal, a spectral norm, a Kronecker factor, a full matrix. Estimating more structure buys a better preconditioner but pays for it in estimation noise, and the field picks a rung by folklore. This project asks when the trade actually pays, and answers it as decision theory over the lattice of matrix subalgebras: an exact variance penalty for each rung, a proof that the cost of estimating an eigenbasis is O(1) rather than blowing up as the eigengap closes, and an adaptivity phase transition at the BBP threshold of random matrix theory. In a rank-one spiked-Wigner idealization of the curvature estimate, and under an explicit spike-to-bulk condition near 0.69, isotropic Muon-style preconditioning is exactly asymptotically minimax optimal below that threshold, so within that model no estimator does better in the worst case over spike directions. Just above the threshold the picture reverses: an oracle-shrunk estimator beats the trivial rung at every signal level tested, so the unprofitable window belongs to the plug-in estimator rather than to the problem. The theory then becomes a method: the Geometry Ladder is an anytime optimizer that walks the lattice from trivial to diagonal to Kronecker to full, promoting only when a concentration bound certifies the richer rung has earned its noise, with hysteresis to prevent thrashing. Putting that method through its own pre-registered protocol is where the interesting result is: on convex problems, on MLPs, and on CIFAR-10 with ResNet-18, the certified rule almost never promotes the wide layers where richer structure was supposed to pay. Built on sketchy, a PyTorch library with Nystrom sketching, Hessian-vector-product and Gauss-Newton oracles, and Muon, all on a single RTX 3060.

  • Result: Threshold theory, proved and numerically verified: exact variance penalties for each preconditioner structure, an eigenbasis-estimation cost that stays O(1) as the eigengap closes rather than exploding, and an adaptivity phase transition at the BBP threshold. In a rank-one spiked-Wigner model of the curvature estimate, and under an explicit spike-to-bulk condition near 0.69, isotropic Muon-style preconditioning is exactly asymptotically minimax optimal below the threshold. A companion non-asymptotic bound needs no imported random-matrix result and stays positive at every finite dimension for signal-to-noise up to 0.98, at a constant of 1/4 rather than 1.
  • Result: Measured that low-rank curvature corrections are redundant with Muon's orthogonalization: on CIFAR ResNet layers the correction redirects the Muon step by a median of 0.84 in relative Frobenius norm, range 0.65 to 1.06, with no accuracy improvement, and the redundancy is structural. Concurrent Muon work from 2025 and 2026 reached the same conclusion independently.
  • Result: The Geometry Ladder promotes between structures only when a concentration bound certifies the richer rung has earned its estimation noise, with hysteresis against thrashing, and it carries a proved oracle inequality and a no-thrashing guarantee.
  • Result: Put that rule through a pre-registered test on real networks and recorded the negative. On CIFAR-10 with ResNet-18, two seeds, 30 epochs, the ladder left all 13 preconditioned wide convolutions on the trivial rung in both seeds and promoted only on narrow downsample layers, so the pre-registered hypothesis that the Kronecker rung would be a destination rather than a transient is refuted. Ladder, fixed trivial and fixed Kronecker arms all land within about a tenth of a point of each other. The one gap that clears the noise runs the other way: a fixed diagonal metric averaged 88.4% test accuracy against 89.6% for no preconditioning at all, keeping its sign on both seeds and on the tuning run. At two seeds that is suggestive rather than settled, and the wide-layer non-promotion is confounded by a curvature reservoir shared with the forced-Kronecker arm, so what the experiment shows is that the certified selector rarely selects structure, not that structure would not pay.
  • Result: Machine-checked the paper's algebraic backbone in Lean 4 and Mathlib: ten sorry-free theorems covering the Cauchy-Schwarz positivity behind the adaptivity threshold, the point-mass floor used by the finite-dimension lower bound, the trace collapse behind the full-matrix variance penalty, the Kronecker multiplicity discount, and the closed-form zero-noise sign law. The scope boundary is explicit and narrower than it looks: the GOE and Wick moment constants those variance results collapse to enter as hypotheses rather than as Lean-proved facts, and the probabilistic and asymptotic core, meaning the chi-squared and total-variation computations, the BBP threshold, the minimax argument and every large-dimension limit, is outside Lean scope.

Write-up · 27-page draft paper with proofs appendix; closed-form constants numerically verified, core algebraic identities machine-checked in Lean 4 and Mathlib.

Study 02Statistics · model compression

Certifying Compression · Evaluation That Survives Being Watched

Python · PyTorch · Statistics · E-processes · Model Compression · Quantization · LLMs

Evaluation is supposed to be a fixed-n experiment: fix the sample size in advance, run it, read the number. In practice nobody does that. You watch the number, and when it looks good enough you stop, which quietly voids the guarantee you thought the number carried. This project builds an evaluation that survives being watched. certify is an anytime-valid ε-equivalence test (a betting confidence sequence with a GROW-boundary e-process) that answers whether a compressed model stays within ε of its source, with a wrong-verdict probability that holds below α no matter when you stop, so an evaluator can peek after every batch, stop the moment the verdict lands, and lose nothing. Matching sample-complexity bounds say what that costs. Two specializations make it usable in deployment: a worst-slice test whose ability to detect a broken subgroup does not degrade with the number of slices, so detection pays no Bonferroni penalty, and a frontier mode that certifies which of K candidate compressions are ε-equivalent and returns the cheapest, paying the expensive source-model cost once rather than K times. An audit applies it to eight architecture families under bitsandbytes and released GPTQ/AWQ checkpoints. The statistical pieces specialize known results, and the paper says so plainly; the contribution is assembling them into a certifier a practitioner can actually deploy.

  • Result: Anytime-valid by construction: the wrong-verdict probability stays below α however long the evaluation runs, so watching the number and stopping when the verdict lands costs nothing, where a fixed-n test would be invalidated by the same peek
  • Result: Two deployable specializations. The worst-slice test tracks a product e-process over predictable slices, so its error stays at alpha for any number of slices G and its detection bound is independent of G at a fixed worst-slice frequency, where a fixed-n test pays a Bonferroni log G. In a synthetic sweep from G = 2 to G = 128 the two cross near G = 16, so the e-process is the cheaper instrument only above that; certifying that every slice is equivalent, rather than detecting that one is broken, still pays a union bound and the paper proves that cost unavoidable. The frontier mode returns the cheapest epsilon-equivalent config from a menu of K compressions and shares each expensive source pass across candidates, measured at a 2.18x saving on a three-config menu of released Qwen2.5-1.5B quantizations.
  • Result: Audit of 29 model-and-method pairs across eight architecture families, 0.36B to 3.8B, at epsilon = 5% top-1 disagreement with alpha = 0.05. Every 4-bit pair certifies NOT_EQUIVALENT, 17 of 17, covering all eight families and both bitsandbytes NF4 and released GPTQ and AWQ checkpoints. 8-bit is neither uniformly lossless nor cleanly architectural: of 12 pairs, 4 certify equivalent, 3 certify not equivalent, and 5 stay undecided at the 4,000-token budget, with Qwen2.5-3B int8 certified not equivalent while its 0.5B and 1.5B siblings remain undecided.
  • Result: Matching sample-complexity upper and lower bounds for the Bernoulli discrepancy model, sharing the leading constant as alpha goes to zero, with the oracle second-order term proved to be a bounded constant rather than a growing one. Its value is the renewal boundary overshoot, measured in simulation at 0.26 nats and flat across log(1/alpha) from 3 to 20; the plug-in adaptivity constant, worth 1.3 to 1.9 nats, is open.

Write-up · Draft paper, 16 pages with proofs appendix.

Study 03Reinforcement learning · combinatorial optimization

Completability · What a Feasibility Mask Cannot Promise

Python · PyTorch · Reinforcement Learning · PPO · Combinatorial Optimization · CP-SAT · Constrained Search

Learned 3D bin packers handle constraints (no stacking on glass, orientation locks, unload order) by masking illegal placements at each step. The mask is the field's standard interface, and it is sound as far as it goes: every placement it permits is legal. This study asks what it cannot promise, and the answer is completability. A greedily legal placement can still strand the items that come after it, and no per-step mask can see that coming. Three theorems locate the boundary exactly. When the packer has recourse (it can always open another bin), dead-ends cannot exist and masking is genuinely sufficient, which is why the entire prior benchmark regime, including my own, never noticed the gap. At fixed capacity that escape hatch is gone, dead-ends appear (shown by a machine-verified minimal counterexample), and the interface that would restore sufficiency requires deciding viability, which is strongly NP-hard. The empirical half builds the fixed-capacity benchmark the theory demands, on instances that are provably packable, and shows the failure is severe, diagnosable, and partly fixable: a learned viability critic recovers much of what the policy's own value head throws away. The packing engine this work grew out of ships in Packomation.

  • Result: Constraint-agnostic packing: randomizing declarative feasibility masks during training yields one policy that transfers across constraint sets, and a lookahead head that chooses which of the next k buffered items to place raises the strict win rate over the best-of-four heuristic from 0.10 to 0.69 as constraints tighten, while staying 90-95% proven-optimal against a CP-SAT lower bound
  • Result: Isolated where the advantage comes from: matched-budget ablations rule out extra search as the explanation, and an online-PPO baseline at the same budget never clears the bar, so the gain is lookahead selection rather than reinforcement learning in general
  • Result: Three theorems locate the boundary of what a feasibility mask can promise: with recourse, dead-ends cannot exist and masking is sufficient; at fixed capacity they can, shown by a machine-verified minimal counterexample; and the interface that would restore sufficiency requires deciding viability, which is strongly NP-hard
  • Result: Showed the gap is real rather than theoretical: on provably packable instances a masked packer's success collapses from 100% to near zero as the container tightens, placing 92% of items while finishing 5% of instances, and a CP-SAT cross-check confirms the instances themselves are easy
  • Result: A learned viability critic beats the policy's own value head as search guidance by +0.218 [+0.188, +0.250] at the 9-item training size, survives zero-shot transfer to 12 to 25 item instances at +0.115 pooled, and replicates zero-shot on a structurally different tight non-guillotine distribution at around +0.20. Pushed further the edge decays with scale: +0.077 at 30 items, +0.055 at 35, and +0.008 by 40, inside seed noise. Measured on instance-disjoint held-out data the critic predicts completability at AUC 0.975 with a spread of 0.002 across four seeds, over 17,646 states from 150 instances.
  • Result: Re-measured that critic's headline AUC under an honest protocol and the number moved. The in-split figure came from a state-level random permutation over a pool where sibling rollout states of one instance appear across three rollouts, scored on the same fold used to pick the training epoch, on class-balanced data. On 17,646 states from 150 fresh instances with the natural prior, the same four critics score 0.9752 plus or minus 0.0018 against 0.9903 plus or minus 0.0012 on their own validation folds. Calibration moves the same way, ECE 0.033 honest against 0.021 in-split. The gap is composite, mixing split leakage, checkpoint selection and the class prior, and no run separates them; instance-disjoint evaluation is simply the protocol this quantity requires.
  • Result: Tested what the viability estimate is worth at rollout with no search. Taking its argmax over the policy's top-8 shortlist raises success by +0.104 [+0.078, +0.130] against a matched control that expands the same shortlist, drops the one-step dead ends, then picks by the policy's own logits, over 960 fixed-capacity instances; widening to a top-16 shortlist gives +0.108 [+0.067, +0.150]. The arms that implement the interface the theory actually prescribes, where the estimate restricts and the policy still decides, score only +0.026 to +0.043, inside the per-cell critic-seed spread, and a single hand-built feasibility feature matches them. The payoff is in ranking by completability, not in filtering by it. Both runs use one critic seed where the other headline numbers here average four, and the pooled effect is uneven across the grid, about +0.24 in the loose cells and at or below +0.08 in the tight ones.

Write-up · Paper drafted.

Study 04Model compression

Spectral Compressibility · What Training-Free Metrics Actually Predict

Python · PyTorch · Random Matrix Theory · Model Compression · Quantization · GPTQ · Statistics

A theory-plus-audit research project (speccomp) on what makes a neural network layer compressible, and whether the field's training-free sensitivity heuristics actually measure it. The central audit finding is that popular training-free scores (Hessian trace as in HAWQ, heavy-tailed exponent α) predict weight-space compressibility but not output-space damage; used for mixed-precision bit allocation, they perform worse than random. A cheap one-backward empirical-Fisher gradient predictor does better. It ranks ahead of HAWQ's double-backward Hessian score on all 11 cells of a 4-model sweep spanning round-to-nearest quantization, pruning and low-rank compression, and ties it under GPTQ (WikiText-2 perplexity 60.5 against 60.1, one run per arm) for one backward pass instead of an 8-probe Hutchinson trace. A Gauss–Newton analysis explains the gap, and its falsifiable probe-noise-vs-data-alignment prediction held up in testing. The headline claim survived a pre-registered confirmatory test on a 7-model, 5-architecture panel, with thresholds proposed before the confirmatory sweep and never changed after seeing data. The pre-registered primary hypothesis did not survive, which is the more useful half of the result. All numbers reproduce from the experiments directory; everything runs on a single 12 GB GPU.

  • Result: Established that the field's training-free sensitivity scores (Hessian trace, heavy-tailed α) predict weight-space compressibility but not output-space damage: used to allocate bits, they do worse than random
  • Result: A one-backward empirical-Fisher predictor matches HAWQ's double-backward Hessian score under GPTQ on WikiText-2 (perplexity 60.5 against 60.1, one run per arm, no variance estimate) for one backward pass instead of an 8-probe Hutchinson trace, and ranks ahead of it on all 11 cells of a 4-model sweep spanning round-to-nearest quantization, pruning and low-rank compression. The blunt finding underneath: uniform allocation still beats both on every quantization cell, so the field's contest between sensitivity scores is being run below a bar that a trivial baseline already clears.
  • Result: Pre-registered confirmatory test on a 7-model, 5-architecture panel, 168 shape-controlled cells with correlations computed within each architecture and pooled: the one-backward weight-error times gradient-RMS predictor beat the HAWQ Hessian by delta rho = +0.243 [+0.201, +0.285], winning on four of the five architectures and tying on the fifth (Bloom, 0.514 against 0.518). The pre-registered primary hypothesis failed, and that is the result worth reporting: the forward-only activation-RMS signal that beat HAWQ on a single model was inferior across the panel (delta rho = -0.179), and forward-only activation statistics are the cheap default the field reaches for.
  • Result: A Gauss-Newton analysis explains the gap, and its sharpest prediction held on OPT-350M across 3 seeds on the quantization cells: as the Hutchinson probe count rises from 1 to 64 the Hessian score's correlation with output error climbs from 0.21 to 0.50 and is still short of the one-backward gradient's 0.57, so part of the advantage is probe noise that more probes remove and part is the residual gap the data-alignment term predicts. A second prediction, on per-method isotropy, held only partly: the fit was worst for low-rank as expected, but the stable-rank proxy was too crude to order the methods.

Write-up · Draft workshop paper.

Study 05Parameter-efficient fine-tuning

Does the Basis Matter? Permutation Controls for Frequency-Domain PEFT

Python · PyTorch · PEFT / LoRA · Wavelets · Fourier Analysis · LLMs · Permutation Tests · Experimental Design

Frequency-domain PEFT methods (FourierFT, WaveFT, and wavelet LoRA variants) report gains and credit the transform basis: a Fourier or wavelet basis is said to encode structure that a plain low-rank update misses. That attribution had never been tested against a matched control. This project builds one. A basis permutation is orthogonal and energy-preserving, so permuting the basis ordering holds parameter count, sparsity support, and effective rank fixed while destroying the structure the transform is supposed to exploit; whatever the real basis buys over its permuted null is structure, and whatever it does not is decoration. Running the control on FourierFT and WaveFT with RoBERTa-base on GLUE splits the reported gains into three parts, and the basis is the smallest of them. Magnitude does most of the work, and the collapse everyone reads as proof that the transform is necessary belongs to one method. FourierFT's no-transform variant craters to the frozen baseline (CoLA MCC 0.000) because its scaling constant is calibrated to a normalized inverse FFT, so dropping the transform without rescaling mis-sizes the update by a factor near 768; magnitude-matching alone recovers 70-87% of that collapse. WaveFT needs no such correction, since its inverse DWT is orthonormal, and its no-transform variant does not collapse on any of the three tasks (CoLA MCC 0.554 against 0.557 for the true basis). The field's headline evidence for transform necessity is a normalization artifact. Density, the high-rank sparse parameterization, is the real but small remainder, worth 0.026 accuracy on MRPC and 0.072 MCC on CoLA. What is left for the basis itself is close to nothing: a permuted Fourier basis is statistically indistinguishable from the true one on MRPC and CoLA (paired p = 0.91 and 0.67), and permuting the wavelet basis is harmless on all three. There is one exception and it is reported as one: FourierFT on RTE, where the true basis wins in 3 of 3 seeds by a mean 0.041 accuracy margin, paired p = 0.03. Two things keep it modest. The wavelet grid on RTE runs the other way, and a magnitude-matched independent orthogonal basis lands inside the permuted band. At three seeds this is the one place the basis may carry real structure, not a demonstration that it does. A budget sweep over 50, 200 and 1,000 Fourier coefficients per adapted projection, 1,200 to 24,000 adapter parameters, finds no crossover where structure starts to matter, including at the tightest budget where a structural prior has the most room to help. The project began as WaRA, a PyTorch library for wavelet-domain LoRA, which remains the instrument.

  • Result: Decomposed FourierFT's apparent need for its transform on RoBERTa-base across MRPC, CoLA and RTE, 3 seeds each. Magnitude does most of the work: a no-transform update that differs only by a per-layer rescale to the transform's Frobenius norm recovers 82%, 87% and 70% of the collapse. The remainder is density, the dense reach a transform buys across every weight coordinate, worth 0.026 on MRPC (mean of accuracy and F1) and 0.072 MCC on CoLA. Running the same controls on WaveFT sharpened the result: its inverse DWT is orthonormal, so its no-transform update is already magnitude-matched, never collapses, and leaves no density gap.
  • Result: What remains for the basis is close to nothing on two tasks of three: a permuted Fourier basis is indistinguishable from the true one on MRPC and CoLA (paired p = 0.91 and 0.67), and permuting the wavelet basis is harmless on all three. The single exception is FourierFT on RTE, where the true basis wins in 3 of 3 seeds by a mean 0.041 margin (p = 0.03), tempered by the wavelet grid running the other way there and by a magnitude-matched independent orthogonal basis landing inside the permuted band. No capacity threshold appears across the 50, 200 and 1,000 coefficient budgets, including the tightest, where a structural prior has the most room to help.
  • Result: The control is exact by construction: permutations are orthogonal and energy-preserving, so they hold parameter count, sparsity support, and effective rank fixed while destroying the structure the transform is meant to exploit
  • Result: A companion analysis on one fully fine-tuned SAM ViT-B update supports the account: the update's weight matrices sit inside their row-and-column permutation null across all 48 layers tested (mean z = +0.14), while the same model's patch-token activation grids beat their spatial-permutation null on wavelet low-frequency energy in all 60 image-by-block grids (mean z = +19.1). One model and one update, so it illustrates why a structured basis can be inert in weight space while being well motivated on activations, rather than establishing it.
  • Result: WaRA, the wavelet-domain PEFT library the study is built on, ships weight-space and activation-space variants against a parameter-matched LoRA baseline

Write-up · Paper drafted, 12 pages.

Projects

11 exhibits
Exhibit 01ML & Applied AI

Packomation · Spatial Capture & Learned 3D Bin Packing

Python · PyTorch · Reinforcement Learning · FastAPI · ONNX · React Native · TypeScript · iOS · ARKit · LiDAR

An iOS app that scans a room or shipment and generates an optimized 3D load plan, for both personal moves and freight or container loading. A local Expo native module bridges RoomPlan LiDAR scans and ARKit tap-to-measure for item dimensioning, with camera intake (react-native-vision-camera) and a capture chain for devices without a rear depth sensor: a TrueDepth hold-up measurement, an Object Capture path for small items, then a marker-assisted estimate and a model prior. Planning ships classical 2D/3D heuristics in TypeScript (BL/BAF/BSSF/BLSF plus an NFDH shelf packer) alongside the learned packer developed in the completability study above, served from a FastAPI /plan endpoint with a TypeScript client and an ONNX export path for on-device inference. A checkpoint is only promoted into the product if it beats the heuristic baseline on an offline eval, so the app never ships a policy that packs worse than the code it replaced. Freight mode adds pallet-first planning, container presets, load sequencing, a deterministic scorecard with a profile-aware re-ranker, and carrier handoff docs.

  • Result: Eval-gated promotion: a checkpoint reaches the product only if it beats the heuristic baseline on bin count and volume utilization, and the promoted checkpoint and its training configs are committed alongside the app
  • Result: Sensor-agnostic capture: RoomPlan and ARKit where the hardware allows, a marker-assisted estimate and a model prior where it doesn't, with each item's dimension source and confidence tracked through to the plan
  • Result: Every dimension in a plan carries its provenance. A pure arbitration module ranks the three automatic sources, an explicit capture the user performed on that item (AR tap-to-measure, TrueDepth hold-up, or Object Capture) over an auto-matched LiDAR room feature over a passive label-and-media estimate, and it emits the dimension source, the estimation confidence, the matched feature id, the sensor confidence and the capture asset together so those fields cannot disagree. Typed values outrank all three: on save the intake form compares what is left in the fields against what the module resolved and stamps the source as manual whenever they differ, so a record never claims a sensor produced numbers a person typed over.
  • Result: Weights are imputed rather than demanded, because the safety and loadability kernels score a weightless load a perfect 1.00. Intake multiplies a per-category bulk-density prior by the item's own measured volume, a choice made because within-category density held steadier than mass across the demo fixtures: the two wardrobes come out at 75 and 76 kg/m3 while weighing 90 and 110 kg, the two dining tables at 47 and 53 kg/m3 while weighing 45 and 75 kg. The priors are fitted to those fixtures rather than validated against real weighings, and nine of the twenty-six categories rest on a single observation. A test holds them within 25% of every categorised demo row, so changing the demo data forces the priors to keep up, and the inventory screen and the plan grade each carry a count of how many weights were estimated rather than supplied.
Fig. 01 · Learned packer fitting a 2-bedroom load (18 items, 7 fragile) into a 14-ft truck under automatic stacking constraints
Exhibit 02Systems & Security

Macforensic · Forensic Imaging Tool for macOS

Python · Systems · Forensics · Security

A macOS command-line tool for forensically sound disk acquisition, built for investigators who need a verifiable raw image with a defensible chain of custody. It enumerates and risk-flags source devices via diskutil, streams a block device to a .dd image, and records a JSON case manifest plus an append-only acquisition event log, with a PyInstaller DMG build for field-portable distribution.

  • Result: Verify-after-write: SHA-256 computed during acquisition, then the written image is re-hashed to confirm it matches
  • Result: Three policy modes (forensic-safe / lab / emergency-live) guard live acquisition of the currently booted disk; a map-apfs subtool documents containers, physical stores, and volumes
  • Result: Optional detached Ed25519 signatures and RFC3161 timestamping of the hash sidecar
  • Result: pytest suite covers the imager, verifier, and APFS topology mapping
Macforensic: Forensic Imaging Tool for macOS
Fig. 02 · Device survey with per-disk risk flags, then APFS container mapping; read-only, no root
Exhibit 03Client Work

Spirit · Learning Platform (EdTech SaaS)

Next.js · TypeScript · PostgreSQL · Prisma · Stripe · Mux · Clerk · Railway

A full-stack EdTech platform for a spiritual-education client, live in production at learnwithspirit.com, where learners buy and watch structured courses and instructors run their own teaching workspace. Next.js 15 + TypeScript on Prisma/PostgreSQL: Clerk auth with learner/instructor/admin roles, Stripe Checkout for single courses and discounted bundles, Mux video with signed RS256 playback and self-serve instructor upload, and Resend transactional email. Lessons span video, audio, Markdown articles, and private reflection journals, with resume positions, practice-streak stats, and auto-issued shareable certificates; learners get one-click data export and account deletion, and money and content mutations land in an audit log.

  • Result: Deployed to production on Railway behind Cloudflare at learnwithspirit.com, with 13 Prisma migrations applied on deploy and CI on GitHub Actions
  • Result: The money path is covered end to end by an automated suite: checkout (course and bundle variants), signature-verified webhook fulfillment with idempotent redelivery, refund revocation, entitlements, and the admin authorization matrix
  • Result: Signature-verified Stripe webhook grants entitlements idempotently inside a DB transaction; bundle purchases allocate the discounted total pro-rata across member courses; refunds revoke access and email the buyer

Index · additional entries

04Entropy Encryption Detector

Autopsy Ingest Module

An Autopsy file-ingest module that implements the methods from my ITIS 5250 computer-forensics paper ('Evaluating Entropy-Based Encryption Detection Methods', UNC Charlotte, Dec 2023). Autopsy's built-in detector flags a file as encrypted when its Shannon entropy exceeds 6.0 bits/byte, a rule that produces false positives on structured-but-dense formats like PDFs and archives. This module instead computes four entropy measures per file from a byte histogram (Shannon, collision at Rényi α=2, Rényi α=3, and min-entropy) and raises Autopsy's native TSK_ENCRYPTION_SUSPECTED artifact with all four values recorded in the comment. It defaults to min-entropy, the paper's lowest-false-positive measure: structured files always have a dominant byte, encrypted data has none. Analysts can switch measures, tune the threshold and α, or flag when any measure trips. The code is Jython- and CPython-safe, and ships with unit tests, a standalone CLI scanner, and a regenerable labeled corpus that reproduces the paper's result.

Python · Jython · Autopsy · Digital Forensics · Entropy · Security

  • Result: Reproduces the paper's headline on the bundled corpus: Shannon (Autopsy's default) wrongly flags a real PDF at 7.90 bits/byte; min-entropy clears it (4.82) while still catching random bytes (7.87)
  • Result: Drop-in python_modules folder with a settings UI (measure, threshold, α); unit tests and the CLI scanner run without Autopsy
05Retail Platforms

New Leaf & Locket Vintage

Two live e-commerce sites, a mobile plant shop (The New Leaf) and a vintage clothing store (Locket Vintage), each with a self-serve storefront and an owner admin dashboard. Built on a reusable Express + SQLite (node:sqlite) API and React/Vite storefront pattern, with self-serve Stripe Checkout, signature-verified Stripe webhooks driving order and inventory state, and JWT-secured admin portals for inventory, orders, schedule, expenses, bookings, and reporting. Hardened with express-rate-limit, Helmet, a strict CORS allowlist, and request sanitization; Locket onboards real inventory via a CSV import pipeline. Deployed on Vercel (storefronts) + Railway (APIs).

React · TypeScript · Express · SQLite · Stripe · Admin Portal

  • Result: Two stores live in production on Vercel + Railway; Locket onboards real inventory via a CSV import pipeline
  • Result: Stripe Checkout with signature-verified webhooks driving order and inventory state; JWT admin, rate limiting, Helmet
06Board Logs

Board Climbing Analytics Platform

A personal climbing-progress tracker that turns a messy session spreadsheet into an interactive analytics dashboard for spotting training trends. Full-stack TypeScript: an Express + SQLite (node:sqlite) backend seeds from an Excel/CSV log via an ExcelJS pipeline (with Excel date-serial handling), then computes analytics server-side: a cumulative timeline with 7-day rolling average, grade distribution, per-grade flash rate and average attempts, attempt histogram, sends-by-wall, style mix, and weekday training volume. A React + Recharts frontend renders these with date/wall/grade/style filters and token auth. Deployed frontend on Vercel, backend on Railway.

React · TypeScript · Express · SQLite · Recharts · Data Visualization

  • Result: 687-send dataset across 9 walls; 8 analytics views computed server-side
  • Result: Live at boardlogs.com
07Taskmaster Presentation Suite

A local-first web app I built to run homemade Taskmaster game nights: one operator drives the show, and a second screen displays it to the room. TypeScript end to end: a Fastify API over Node's built-in SQLite (node:sqlite, WAL mode) with routes for episodes, contestants, tasks, scoring, results, presenter state, and history, where every score change is written to a score_events table (old/new points, actor, timestamp) for a full audit trail with rollback. A React + React Router front end pairs a control surface with a dedicated /presenter window that stays in sync by polling shared state through TanStack Query; Zustand (persisted) handles local UI flow. Includes a media picker (video/image/gallery via multipart) plus JSON export/import of the whole show database.

React · TypeScript · Fastify · SQLite · TanStack Query · Zustand

  • Result: Every score change lands in a score_events audit table (old/new points, actor, timestamp) with rollback
  • Result: Operator and presenter UIs stay in sync via polled shared state; TypeScript end to end
08Automated Multicam Edit Pipeline

An automated assembly pipeline for long-form multicam footage, built for a homemade comedy panel show made with friends, shot one locked camera per person plus room sound. It transcribes with faster-whisper large-v3-turbo on CUDA with word timestamps, syncs cameras by cross-correlating audio-energy envelopes, synthesises a second angle by cropping 3024x1964 mains to native 1920x1080 with no upscaling, plans shots against a grammar calibrated on real broadcast episodes, and renders with ffmpeg under two-pass loudness normalisation. Camera sync, punch-in framing, within-chunk shot selection and rendering are deterministic Python over ffmpeg; the story layer above them is not, since each unit's chunk grouping was hand-fed from a chosen plan and transcript condensation runs through a local Qwen 2.5 14B. Nothing here learns an editing style and nothing here decides what is funny. The output is 50 first assemblies, dialogue only, with connective narration, opening and ending left to a human editor.

Python · ffmpeg · Whisper · Signal Processing · NumPy · Ollama

  • Result: Ran end to end at real scale rather than on a demo clip: a 21.6 hour inventory of 142 camera files across 18 tasks and five people, 106 of them (18.3 hours) identified down to task and person, producing 50 rendered assemblies totalling 88.5 minutes across 14 task units.
  • Result: Defined a jump cut structurally, same person plus same camera plus a skip in source time, and made that definition a hard precondition of the assembler rather than a review note: it exits before encoding a frame if a plan violates it. Re-running the check over every stored render plan gives 52 plans, 50 clean across 875 shots with zero jump-cut and zero chronology violations, and 2 that fail with 9 jump cuts each and were never rendered. The composer masks residual same-camera seams with a punch-in before the gate runs, so the zero is those two mechanisms together, and chronology is a warning at assemble time rather than a gate.
  • Result: Retracted a metric of my own after testing it. Cut visibility had been scored by colour-signature distance across each cut, and that proxy cannot answer the question, because every person sits in the same locked frame, so a cut between two people barely moves the picture statistics. The within-shot control (max 0.233) overlapped the cut distribution (median 0.226) outright. The perceptual proxy was replaced by the structural definition, and colour was kept only for the question colour genuinely answers, which room a shot is in.
  • Result: Documented a proxy-gaming failure from the inside. A synthetic language-model audience panel scored each cut and an automated loop hill-climbed that score. Across four rounds the panel held flat at 4 out of 10 while the loop deleted 49% of the runtime, cutting the plan from 7.9 minutes to 4.0, and the result read worse to me as the editor. A structured audit of the loop named the mechanism: critics were shown only a short head of each shot, so length was the only property they could perceive, and the actuator set was strictly subtractive, which left the panel's own lowest axis, payoff at 3.0 out of 10, unreachable. The audit is self-reported and the pre-fix code is not under version control, so the mechanism rests on that review rather than on an independent replay.
  • Result: Found and fixed a 0.450 s container timestamp offset between the audio and video streams that was clipping the tail off every cut. Transcript timestamps come from decoded audio, which starts at the first sample, while ffmpeg seeks the container, and in four of the five main cameras the audio stream starts 0.450 s after the video stream; the fifth, recorded separately, measures 0.000. Every cut derived from a transcript therefore began and ended 450 ms early, and two sampled lines still had real speech in the chopped tail. The conversion from transcript time to container seek time now adds the offset, probed per file rather than assumed.
  • Result: Delivery is 1920x1080 and every main camera records 3024x1964, so a native crop is a 1.575x horizontal punch-in with no upscaling, a second framing out of a camera already owned. A per-pixel temporal-variance map is used as a person mask rather than as an energy target, because maximising enclosed motion energy centres on the hands at the desk and cuts off heads; the window hangs from the top of the mask with headroom, and an integral image reports how much of the clip's motion it actually contains, 37 to 61 percent across the five people. The punch-in supplied 25 of 97 shots in the rebuilt task assembly, which passes the jump-cut check at zero, and it is the only mask available for the one person with no second camera.
09MultiModelRTC

P2P Video Chat with In-Browser ML Detection

A peer-to-peer WebRTC video chat where TensorFlow.js models run in the browser to detect faces, hands, and objects, overlaying live annotations on each stream. A Vite client establishes RTCPeerConnection sessions over Google STUN with Firebase Firestore for room signaling (offer/answer + ICE), so media never leaves the peer-to-peer path. Four TensorFlow.js models (coco-ssd, facemesh, handpose, body-pix) run client-side and render predictions as positioned DOM overlays (boxes, face mesh, gesture labels). No video pixels are modified, and there is no server-side CV. Built as a 4-person UNC Charlotte ITCS 6166 course project (2023), where I was the repo integrator and authored the final IEEE report; per-member feature ownership isn't separable from the source.

WebRTC · TensorFlow.js · Computer Vision · Firebase · JavaScript · Real-time

  • Result: Four TensorFlow.js models run fully in-browser; media stays on the peer-to-peer path with no server-side computer vision
  • Result: Four-person course project (UNC Charlotte, ITCS 6166): I was the repo integrator and authored the final IEEE report
10CYouTube Desktop

A desktop GUI for grabbing YouTube videos and playlists without touching the terminal. A C++17 app with a wxWidgets window that takes a pasted URL and an output folder, then shells out to yt-dlp (with ffmpeg for format support) and streams its log output live in-app. Intentionally wraps the yt-dlp CLI rather than re-implementing YouTube extraction, with an optional full-playlist mode for playlist links.

C++ · wxWidgets · GUI · Desktop

11MDViewerMac

Native Markdown Viewer

A native macOS app for people who want a fast, clean local preview of Markdown files without opening a browser or editor. A SwiftUI shell renders Markdown to HTML in a WebKit view via the Ink parser, with Cmd+O file-open and drag-and-drop intake, a GitHub-like reading style, and shell scripts that build a .app bundle and package it as DMG/PKG.

Swift · SwiftUI · macOS · WebKit · Desktop

  • Result: Downloadable DMG build, ad-hoc signed for local use and not notarized

Education

9 graduate courses

2022 – 2023

M.S. Computer Science · University of North Carolina at Charlotte

  • 4.0 GPA · Phi Kappa Phi Honor Society
  • Graduate Certificate in Applied Artificial Intelligence
  • Graduate coursework in machine learning, digital forensics, databases, and algorithms
  • Graduate research assistant in optimization and reinforcement learning

Graduate coursework

Full course dossier

Contact

Interested in collaborating, hiring, or discussing ML, systems, or product work. Open to contract & full-time · Remote (US).