Chris Hopman
cjhopman@meta.com
90d · built 2026-05-28
90-day totals
- Commits
- 57
- Grow
- 9.9
- Maintenance
- 2.6
- Fixes
- 0.9
- Total ETV
- 13.4
Where this dev ranks
Percentile against the global top-100 leaderboard (all-time totals).
- By commits
- Top 84 %
- By Growth share
- Top 3 %
30-day trajectory
Last 30 days vs. the 30 days before. Up arrows on Growth and ETV mean improvement; up arrow on Fixes share means more time on fixes (worse).
Daily performance
Daily ETV, stacked by Growth, Maintenance and Fixes.
Work-mix over time
Share of Growth / Maintenance / Fixes over a rolling 7-day window. Reads as 'where is effort flowing right now'.
Bug flow over time
Monthly bug flow attributed to this developer. The left bar (red) is bug impact this dev authored that was addressed in the given month — combining bugs others fixed for them and bugs they fixed themselves. The right bar is fixes they personally shipped that month, split between self-fixes (overlap with the red bar) and fixes done for someone else. X-axis is fix-time, not introduction-time — the Navigara API attributes bugs backward to the author at the moment the fix lands.
- Self-fix share
- 42%
- Bugs you introduced
- 2.5
- Bugs you fixed
- 2.0
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 20 by ETV in the 90-day window.
- 1.1ETVAdd common infrastructure and build scaffolding Summary: Adds the games/ crate with shared infrastructure but no actual games yet: - console.rs: terminal input handling, re-exports superconsole::Stdin - framebuffer.rs: pixel-level framebuffer for sprite rendering - games/mod.rs: Game trait, run_game() loop, HighScores, overlay system - lib.rs: crate root - sprites.png: shared sprite sheet - BUCK: rust_library target only - Cargo.toml: all dependencies, no binary entries - PLAN.md: design notes Reviewed By: JakobDegen Differential Revision: D98707333 fbshipit-source-id: 51e5797d27915a3a4267c0ef0624c7e3442b5189github.com-facebook-buck2 · 4843314f · 2026-04-01
- 0.8ETVAdd blocks (tetromino color-matching) game Summary: Tetromino color-matching game: pieces fall and lock, runs of 4+ same-color cells in a row/column clear and score (n-3)² points. Unsupported blocks fall as rigid groups with chain-matching. Notable implementation details: - Color-matching mechanic: each cell is randomly colored, matches are 4+ adjacent same-color in a line, scored (n-3)² to reward longer runs - Floating group gravity via disjoint-set (union-find) to identify connected components, with chain re-matching after settling - Seven-bag randomizer for even piece distribution - Lock delay with reset on move/rotate (~0.5s) - Match animation (random color flash for 30 ticks) Reviewed By: JakobDegen Differential Revision: D98824937 fbshipit-source-id: bd2717ccb472a43938854488f7e8bbf2127f1841github.com-facebook-buck2 · cab28a1c · 2026-04-01
- 0.8ETVAdd minesweeper game Summary: Minesweeper with constraint-based solver, no-guess guarantee, and multiple board sizes up to 50×34. Notable implementation details: - Two-phase constraint solver: single-cell propagation + subset elimination between constraint pairs for logical deduction - No-guess guarantee: when no safe moves are deducible, the board is rearranged to make the clicked cell safe while preserving all existing constraints (exact backtracking for small components, randomized for large) - Safe mode (toggle 'g'): blocks provably risky clicks when safe alternatives exist - Auto-chord (toggle 'c'): automatically reveals neighbors of satisfied numbers when flagging, with cascading - Guaranteed large opening on first click via layout regeneration - 4 board sizes: Beginner, Intermediate, Expert, Huge (50×34) - Timer tracking Reviewed By: JakobDegen Differential Revision: D98824934 fbshipit-source-id: c93675bd80ecaf9c34f1867b084ad3dbbf83ac46github.com-facebook-buck2 · ffdb8b43 · 2026-04-01
- 0.8ETVAdd sokoban puzzle game with level generator Summary: Sokoban box-pushing puzzle with A* solver, procedural level generator, and 600+ pre-generated levels organized by box count. Notable implementation details: - A* solver with state normalization (player position canonicalized to top-left of reachable region), dead-cell pruning via reverse BFS, and greedy Manhattan-distance heuristic - Procedural level generator (sokoban_gen) based on a simplified version of Taylor & Parberry: https://ianparberry.com/pubs/GAMEON-NA_METH_03.pdf Generates rooms via 2x2 templates, searches backward from solved state to find the farthest reachable start position - 600+ levels in separate files by box count (2-6), all guaranteed solvable by construction - Per-level move tracking with lower_is_better scoring - Numbered box mode for following solver solutions - Box-on-target and player-on-target color feedback Reviewed By: JakobDegen Differential Revision: D98828179 fbshipit-source-id: 5f9f1667f9c0afe075e7282773fbfaf1d005993bgithub.com-facebook-buck2 · e8a1a0b3 · 2026-04-01
- 0.7ETVIntroduce ResultMaybeCompatible Summary: Add `ResultMaybeCompatible<T>`, a three-variant enum that flattens `Result<MaybeCompatible<T>>` into `Compatible(T) | Incompatible(...) | Err(...)`. It implements the `Try` trait so that `?` propagates both errors and incompatibilities, eliminating the common `.await?.require_compatible()?` and `match` boilerplate at callsites. Migrate `get_configured_target_node` and related functions (`gather_deps`, `compute_configured_target_node`, `configure_exec_dep_with_modifiers`, `get_graph_properties`, `ErrorsAndIncompatibilities::finalize`) to return `ResultMaybeCompatible` instead of `Result<MaybeCompatible<...>>`. Also makes `buck2_error::Error::compute_context` public and re-exports `buck2_error::ContextValue` so that `ResultMaybeCompatible` can provide its own `compute_context` method. Reviewed By: JakobDegen Differential Revision: D95148447 fbshipit-source-id: e756146a887e0898b5e6c7c24dd4d4d823f37288github.com-facebook-buck2 · 2a6d9e16 · 2026-03-10
- 0.7ETVIntegrate games into superconsole build overlay Summary: Embeds the games crate into buck2's superconsole as a hidden overlay activated by pressing 'g' three times during any build. Notable implementation details: - Secret 'ggg' activation: three consecutive 'g' presses activate the games overlay during a build, any other key resets the counter - Overlay rendering: games draw above the normal build status output, not replacing it — the build continues underneath - Tick-based escape detection: custom EscapeState machine with a tick-counting timeout (~66ms at 30fps) since buck2's input path delivers characters one at a time without async timeouts - 30fps game tick rate: desired_ticks_per_second switches from the normal build rate to 30fps when games are active - Full state machine: Menu → Playing → GameOver → HighScores with save/load on escape to ~/.buck2_games/ - High scores display matching the standalone menu binary's layout - Escape from menu deactivates the overlay, returning to build view Reviewed By: JakobDegen Differential Revision: D98707326 fbshipit-source-id: 8d770cee9c4aeb26fc8a0445e41869ba04320c2cgithub.com-facebook-buck2 · b7c01db5 · 2026-04-01
- 0.6ETVAdd select_incompatible() Summary: Implement `select_incompatible()`, a new select branch marker that causes the target to be treated as incompatible with the current configuration when that branch is selected. Unlike `select_fail()` which is always an error, `select_incompatible()` causes the target to be skipped gracefully, the same way `target_compatible_with` incompatibility works. This allows expressing conditional incompatibility inline on any attribute: ``` deps = select({ "ovr_config//os:linux": [":linux_dep"], "DEFAULT": select_incompatible("only supports linux"), }) ``` Key changes: - Add `select_incompatible()` starlark function and `StarlarkSelectIncompatible` type, mirroring `select_fail()` / `StarlarkSelectFail`. - Add `CoercedAttr::SelectIncompatible` variant and `IncompatiblePlatformReasonCause::SelectIncompatible` cause. - Implement the `SelectIncompatible` arm in `CoercedAttr::configure_inner` to return `ResultMaybeCompatible::Incompatible`. - Add `incompatible_platform_reason()` method to `AttrConfigurationContext` trait, which constructs an `IncompatiblePlatformReason` from a cause without exposing the `ConfiguredTargetLabel` directly. - `select_map()` preserves `select_incompatible()` values without applying the mapping function, and `select_test()` skips them (same as `select_fail()`). - Incompatibility propagates through the dependency graph: dependents of an incompatible target are also considered incompatible. Reviewed By: JakobDegen Differential Revision: D95148448 fbshipit-source-id: d0330d135ce5db305df35d1cccc8429300ef1c95github.com-facebook-buck2 · 5deef08b · 2026-03-10
- 0.6ETVWire up Dice::page_out and worker page-in step Summary: Adds the user-facing `Dice::page_out()` API and the worker's lazy page-in flow: - `Dice::page_out()` sends a new `StateRequest::PageOut` to the core thread, which iterates all `OccupiedGraphNode`s with hydrated values and serializes them to `DiceStorage`. Caller must ensure DICE is idle first (`wait_for_idle().await`). No-op when no `DiceStorage` is configured. Keys whose `value_serialize()` is `NoValueSerialize` (or returns `None`) are silently skipped and stay hydrated. - `DiceTaskWorker` handles the new `MatchPagedOut` / `CheckDepsPagedOut` lookup variants by deserializing the value via `DiceStorage::hydrate` (off the core thread) and sending a `Rehydrate` request to the core thread before continuing. Subsequent lookups for the same key see the rehydrated `NodeValue::Hydrated` and proceed normally — no further hydration needed until the next `page_out`. - Adds 4 e2e tests in `impls/tests/page_out.rs` covering: hydration on next lookup, rehydration persistence across multiple lookups, skip of `NoValueSerialize` keys, and `page_out()` no-op without storage. This completes the page-out / page-in feature stack. Reviewed By: jtbraun Differential Revision: D101759759 fbshipit-source-id: aa2434661fa336ac1b545b3f8b05f704451deb57github.com-facebook-buck2 · d42b5885 · 2026-04-29
- 0.5ETVAdd jump (side-scrolling runner) game Summary: Endless side-scrolling runner with sprite-based parallax rendering, 5 obstacle types, and adaptive difficulty. Notable implementation details: - Multi-layer parallax: 4 layers at different scroll speeds (sky, mountains, trees, ground) with seamless tree wrapping - PNG sprite sheets rendered through FrameBuffer with alpha compositing; 6-frame deer run cycle, 2-frame bird wing flaps - 5 obstacle types: small/large boulders (ground), slow/medium/fast birds (airborne with vertical bobbing and randomized heights) - Adaptive difficulty: gap shrinks 65→45px over 60s, bird probability ramps 30%→75% over 2 minutes - Speed-aware spawn timing: accounts for different obstacle speeds so gaps are correct as they pass the deer - Inset hitbox collision (2px deer, 1px obstacles) for fair near-misses - Airborne animation freeze (legs-together frame while jumping) Reviewed By: JakobDegen Differential Revision: D98824939 fbshipit-source-id: 466108dd25f4e31e709426817f6be5f22c4bf1b2github.com-facebook-buck2 · 655ce754 · 2026-04-01
- 0.5ETVCheck installer child process exit status while waiting on socket Summary: When `buck2 install` spawns an installer process that exits immediately (e.g., due to invalid arguments like passing `--emulator` to an iOS installer), buck2 previously had no mechanism to detect the early exit. It would wait the full 120-second timeout trying to connect to the installer's TCP port, then fail with a confusing timeout error. This change monitors the installer child process while waiting for the TCP connection. If the installer exits before establishing a connection, buck2 immediately reports the failure with the process exit status instead of waiting for the timeout. The fix: - Changed `build_launch_installer` to use `async_background_command` (tokio) and return the `Child` handle instead of discarding it. - Added the child process handle as a parameter to `ConnectedInstaller::connect`. - Used `tokio::select!` to race the connection retry loop against the child process exiting, providing an immediate error when the installer dies early. Reviewed By: JakobDegen Differential Revision: D103760516 fbshipit-source-id: 3ccf85a7ca3af3ef78cd033c3c99da64094881fbgithub.com-facebook-buck2 · a11ac771 · 2026-05-19
- 0.4ETVAdd breakout game Summary: Breakout with multi-hit bricks, powerups, combos, and level progression. Notable implementation details: - Multi-hit bricks (1-3 health) with color fading as health decreases - 4 powerup types (WidePaddle, MultiBall, FireBall, SplitBall) hidden in ~30% of bricks, visually marked with bold double-line borders - Combo scoring: consecutive breaks without paddle hit multiply points - Border flash feedback in the brick's row color on every break - Paddle hit angle control for directional ball steering - Fine movement (Shift+Arrow for 1-cell precision) - Respawn delay with flashing lives indicator - Level progression with increasing brick health Reviewed By: JakobDegen Differential Revision: D98824945 fbshipit-source-id: 9c0fc757189695368fdd2b6f8ee3e8bce19cb9bdgithub.com-facebook-buck2 · fedc9819 · 2026-04-01
- 0.4ETVAdd snake game Summary: Classic snake on an 80×40 board with emoji rendering (🦌 and 🍎). Notable implementation details beyond a naive snake: - Distance-biased apple placement using weighted random selection (inverse distance^2.7) so apples spawn near the head - "Last chance" forgiveness tick when about to collide, preventing deaths from input arriving one frame late - Input buffering via VecDeque for rapid cornering sequences - Opposite-direction rejection (silently ignored, not instant death) - Tail-chase allowed since the tail vacates before the head arrives - Full save/load state support via serde Reviewed By: JakobDegen Differential Revision: D98824938 fbshipit-source-id: fe065613f447bf7d9742d444b15e087d76ca208fgithub.com-facebook-buck2 · 4f49b8d0 · 2026-04-01
- 0.4ETVAdd life (Conway's Game of Life) game Summary: Interactive Game of Life on a 160×80 toroidal grid with framebuffer rendering. Notable features beyond a basic Life implementation: - 8 color visualization schemes (Age, Density, Velocity, Heat, Gradient, Energy, etc.) cycled with 'm', each driven by per-cell auxiliary state - Per-cell age, previous-generation, and heat tracking to power the color schemes without affecting the simulation rules - Interactive editing: cursor to toggle cells, pause, single-step, clear, and re-randomize - Toroidal topology with rem_euclid wrapping - Framebuffer rendering for per-pixel RGB color via half-block characters - Sparse save format (only live cell coordinates) Reviewed By: JakobDegen Differential Revision: D98824935 fbshipit-source-id: a2ccdb60e662ab5e65fc46943ed3aee0e87c459egithub.com-facebook-buck2 · b4f01cac · 2026-04-01
- 0.4ETVAdd DiceStorage type backed by sled Summary: Introduces `DiceStorage`, a wrapper around any `pagable::storage::traits::PagableStorage` backend, that exposes (crate-internal) `page_out` and `hydrate` APIs for serializing / deserializing a `DiceValidValue` via its key's `ValueSerialize`. `page_out` accepts a `&mut HashMap<usize, DataKey>` cache so callers in a batch can dedup serialization of arcs reachable from multiple values. Use `DiceStorage::new_page_out_cache()` to construct a fresh cache. Constructors: `DiceStorage::new(Arc<dyn PagableStorage>)` for arbitrary backends, plus `DiceStorage::open(path)` and `DiceStorage::from_sled_db(db)` as sled convenience constructors. `Dice` and `DiceDataBuilder` gain an optional `pagable_storage` field. When `set_pagable_storage` is not called, paging is disabled (the upcoming `Dice::page_out` is a no-op). The storage is wired up but unused — the page-out flow that consumes it lands in a follow-up commit. The new code is annotated `#[allow(dead_code)]` to keep lint clean in the meantime. Reviewed By: jtbraun Differential Revision: D101759760 fbshipit-source-id: f65adc6e43cb5a03f95a858d1435980a71dede51github.com-facebook-buck2 · 10758fa6 · 2026-04-29
- 0.3ETVAdd 2048 game Summary: Tile-sliding puzzle on a 4×4 grid with box-drawn tile rendering. Notable implementation details: - Direction-agnostic slide via cell_pos mapping — one function handles all 4 directions without duplicating merge logic - Box-drawn tiles (╭──╮ │val│ ╰──╯) built span-by-span, not just numbers in a grid - Per-value color coding (distinct color for each power of 2) - Bold highlighting on the most recently spawned tile - 90/10 spawn distribution matching the original game Reviewed By: JakobDegen Differential Revision: D98824936 fbshipit-source-id: 1649475ce7be971d1eeaf1197ff9b091adba2ec8github.com-facebook-buck2 · 21b3e265 · 2026-04-01
- 0.3ETVAdd 'buck2 debug hydration page-out' command Summary: Hooks the dice paging feature into buck2 with a new debug subcommand: - `configure_dice_for_buck` opens a sled-backed `DiceStorage` when the `BUCK2_DICE_DB_PATH` env var is set, calling `set_pagable_storage` on the builder. Without the env var the storage is unconfigured and `page_out` is a no-op. - New proto `HydrationPageOutRequest` + `DaemonApi::HydrationPageOut` RPC. - Server-side handler waits for DICE to go idle, then calls `dice.page_out()`. - Client-side `oneshot_method!` plus a new `Hydration` debug subcommand with a `PageOut` variant. CLI invocation: `buck2 debug hydration page-out`. To use: start the daemon with `BUCK2_DICE_DB_PATH=/some/path` set in the environment, then run `buck2 debug hydration page-out` to evict every hydrated `OccupiedGraphNode` value to disk. Subsequent lookups hydrate values back on demand off the DICE core thread. Reviewed By: jtbraun Differential Revision: D101759761 fbshipit-source-id: f1e821a5f92df81be59cdff989eea7774f028e81github.com-facebook-buck2 · 33b119d8 · 2026-04-29
- 0.3ETVExtract DiceTaskInternalCritical into its own module Summary: Refactor to encapsulate the mutex-protected critical section of DiceTaskInternal into a separate `mod critical` block. This moves the lock acquisition into methods on the new `DiceTaskInternalCritical` wrapper type, hiding the internal `Data` struct and exposing a cleaner API. No behavioral changes. Reviewed By: JakobDegen Differential Revision: D99415248 fbshipit-source-id: 20ddd675b5c5b2ee5cee4e03968e15132a64776fgithub.com-facebook-buck2 · 5595620f · 2026-05-01
- 0.3ETVMake OccupiedGraphNode value page-able via PagableNodeValue Summary: Introduces a `PagableNodeValue` enum (`Hydrated(DiceValidValue)` / `PagedOut(DataKey)`) and stores it as `OccupiedGraphNode.res`. Adds two new `VersionedGraphResult` variants — `MatchPagedOut(PagedOutMatch)` and `CheckDepsPagedOut(PagedOutMismatch)` — that the graph emits when the node's value is paged out, carrying the `DataKey` the worker needs to hydrate. `OccupiedGraphNode::val()` now returns `&PagableNodeValue`; callers needing the hydrated value call `.expect_hydrated(msg)` with a message explaining why they know the value is hydrated (analogous to `Option::expect`). No code path actually constructs `PagableNodeValue::PagedOut` yet, so the new lookup variants are unreachable in practice. The follow-up commit wires up `Dice::page_out` (which transitions nodes to `PagedOut`) and the worker hydration step (which consumes the new variants). Tests pass unchanged. Reviewed By: jtbraun Differential Revision: D101759758 fbshipit-source-id: 2efd54a402bc01aecc3f3c62d8de7d622c70fc67github.com-facebook-buck2 · 6d219209 · 2026-04-29
- 0.2ETVAdd menu system for game selection Summary: Game selection menu with save/continue support, high score viewing, and animated title. Notable implementation details: - Two-level selection: game list → New Game / Continue sub-menu with greyed-out Continue when no save exists - Animated "BUCK2 GAMES" title with cycling dot animation - High Scores entry in the game list for viewing per-game records - Reusable bordered_line() and hline() helpers shared with the high scores display for consistent box-drawing formatting - Fixed-height rendering via FixedHeight to prevent display jumps when switching between menu and gameplay Reviewed By: JakobDegen Differential Revision: D98828180 fbshipit-source-id: 10b193f6191c319042c4219456dc9c9b8e057fb5github.com-facebook-buck2 · 259f1ce7 · 2026-04-01
- 0.2ETVMove cancellation state into the critical section mutex Summary: Move cancellation state from a separate `Cancellations` type on `DiceTask` into the mutex-guarded `Data` in `DiceTaskInternalCritical`. Since it was always accessed under the critical section mutex anyway, the `Arc<UnsafeCell<...>>` wrapper and `MutexGuard` parameter ceremony were redundant. - Replace `Cancellations` with `CancellationState` enum stored directly in `Data`. The Mutex provides all the safety — no more UnsafeCell. - `DiceTask` simplifies to just `{ internal: Arc<DiceTaskInternal> }`. - `DicePromise::Pending` drops its `cancellations` field — `drop_waiter` accesses cancellation state through `task_internal.critical`. - `cancel()` uses `std::mem::replace` on owned state instead of `take_mut` + UnsafeCell. - `CancellationState::Pending` variant bridges the gap between `DiceTaskInternal` creation and `CancellationHandle` availability. Reviewed By: JakobDegen Differential Revision: D99415245 fbshipit-source-id: d94cb875ba1598ed52ff49ad95c8f29d2ac76823github.com-facebook-buck2 · ec58e3ae · 2026-05-01