Added
Added in fragcap 0.5.0.
cargo xtask spec checks that the specification's Applies-To field matches the workspace version and that every changelog fragment declares its spec-impact, and the changelog release step now refuses to assemble a release whose fragment claims a specification change the release diff does not contain.
A capture target can now be a stored entry rather than a profile file. fragcap targets add <name> registers a target in local.db, deriving a unique, human-readable handle from the name (Unicode-normalized, never purely numeric, collisions suffixed _2), and assigning a durable 63-bit identifier: the low 63 bits of BLAKE3 over a canonical platform anchor such as steam:620 when one is given, so two independent registrations of a title merge on identity, or a random 63-bit value otherwise. fragcap targets list and fragcap targets show select a target by handle, case-insensitive name, row index, or --id; a name that matches more than one target lists the matches and exits 2 rather than guessing.
Resolution over the two stores is now fidelity-ordered: a locally authored or verified target entry outranks the shipped catalog's heuristic hint for the same title, with the order authored > verified > heuristic-unverified > observed. The four declines that keep the resolver from naming a launcher as the game or guessing among clients (a sparse row, an engine-only row, a launcher-mediated row, and a row naming more than one distinct client) are preserved for entries as well.
This slice ships the entry model, handles, the identifier scheme, the selector, and the fidelity-ordered store read. The remaining v0.5.0 target work is staged across later slices: the engine and platform-walker providers become target sources in S052; the JSON export and import of entry documents and their unification with the master schema land in S055 and S057; and the profile-file surface is retired by S054's command rework. Until then the profile command and the --profile capture selector are unchanged.
Installed games can now be discovered rather than described by hand. Every origin of a capture target, a Steam walk, a scan of the known game-install roots, or a directory the user points at, is a discovery source behind one seam, so single authoring and bulk platform walking are the same operation at different batch sizes: adding Epic, GOG, Xbox, Battle.net, or an emulator ROM directory later is a new implementor with no downstream change.
fragcap targets discover walks Steam (each installed title joined to the shipped catalog for its classification) and, on Windows, the known game-install roots across every eligible fixed volume, and lists what it finds. A machine with no Steam still lists games whenever a known root exists, on any drive, not only the system drive. fragcap targets scan <dir> points discovery straight at one folder. Each listing surfaces a conserved account, so an excluded volume or an unparsable title is counted and shown rather than dropped silently.
The cross-volume walk is kept safe by a persistent volume eligibility allowlist in local.db, seeded permissively with the fixed volumes present at first run and requiring an explicit opt-in for any volume that appears later or misreports itself as fixed (a userspace or FUSE mount). Volumes are keyed on their stable GUID identity, not a reassignable drive letter. The known-roots walk classifies a directory by its shape and stops descending on a hit rather than enumerating every executable on the machine; the signature matcher that generalizes this beyond curated roots lands in a later slice, along with deep filesystem scanning.
Two operator decisions shaped the slice and are recorded in the spec's clarifications: the eligibility table is permissive-seeded then behaves as an allowlist, and discovery surfaces candidates live and persists one only when the user acts on it, so a scan result becomes a stored target through the same entry model every source shares. The pure discovery model (the seam, the tiers, the account, the classifier seam, and the eligibility store) lives in fragcap-targets and adds no new dependency and no new inter-crate edge; the two platform adapters (the Steam walk and the Win32 volume inventory) live in the fragcap facade, the one crate that already depends on both leaf crates. Wiring the interactive, one-step scan-confirm-author flow into the command line is deferred to the S055 targets hero command; the InteractiveSource seam it uses ships here.
Technology detection is now data, not code. The engine, anti-cheat, and DRM signatures that identify a game from its install directory live in a signature table in the shipped catalog database, and one generic matcher evaluates them. Adding a signature of an implemented kind is honored on the next scan with no code change and no release: fragcap targets seed-signatures refreshes detection capability through the same catalog-seed path that refreshes the title catalog, seeded from a bundled set that covers the engines Unity, Unreal, Source, Godot, CryEngine, and RE Engine; the anti-cheat products Easy Anti-Cheat, BattlEye, Vanguard, mhyprot, nProtect GameGuard, and Xigncode3; and the DRM products Denuvo, Steam DRM, Arxan, and VMProtect.
Detection now runs automatically in the scan phase of every discovery source: a directory whose shape matches an engine signature is a game, the walk stops descending on the hit, and the candidate carries the detected engine and any anti-cheat or DRM found alongside it as evidence. A locally detected engine is stamped verified, which outranks the heuristic-unverified attribution a remote catalog carries, because evidence on disk outranks a remote claim. The standalone fragcap technologies --path <dir> --catalog-db <catalog.db> command a researcher uses to inventory an unknown binary reads the same table, and fragcap steam profile labels a scaffolded profile's technologies from it when a catalog is given.
A detected anti-cheat or DRM product is neutral evidence, never a gate. Nothing in any output frames a title as off limits, risky, or discouraged, and a title with no recorded online mode is still fully capturable.
Filename, directory-shape, and PE-version-string matching are evaluated this slice; the binary-marker kind is carried in the schema for the deep-protection DRM products (Denuvo, Arxan, VMProtect) and left inert, counted and surfaced as not-yet-matchable rather than dropped. The signature table is an additive migration advancing the catalog schema to version 5. The change adds no dependency and removes the vendored SteamDB ruleset that was compiled into the binary, authored for depot manifests rather than on-disk installs, and never validated against a real game.
Command line surface rework (slice S054)
The three capture verbs run, tap, and watch collapse into a single capture verb, with no aliases and no deprecation shims. A target is identified by exactly one of two mutually exclusive, required inputs: --target <selector> (a stored target resolved against local.db by an S051 selector) or --process <image> (a raw process image name). Every other flag is orthogonal to the target input, so all five previously-inexpressible captures become expressible: a named process into a ring buffer, a named process waited for, a registered title launched under capture, a profile-equivalent capture with a give-up timeout, and a named process with a ring buffer. --launch requires the resolved --target to carry a Steam anchor; a --process capture, or an anchorless target, is refused (exit 2) rather than silently ignored. The optional path anchors (--path, --path-regex) the retired watch carried survive on capture.
The profile-file capture surface is retired (completing the S051 US5 deferral): the profile command and its subcommands, the AppData profile directory, the --profile-dir global, and the run/tap/watch --profile capture selector are removed. schema validate <file> remains as the general JSON-artifact validator. The file-backed profile provider itself is retained for now, because the Wireshark extcap integration (section 14.5) still advertises and resolves a --profile analyzer-config option; migrating extcap to the stored-target/raw-process model is follow-up work in its own slice, since it changes the analyzer dialog contract rather than the capture verb this slice reworks.
The command namespaces now follow the two stores. A new catalog namespace owns every operation that writes the shipped, disposable catalog.db: import, export, seed, seed-engine, seed-signatures (moved from targets), and a new catalog update that fetches the published catalog behind the net feature. The targets namespace keeps only user-store operations, and registering an installed Steam title moved from steam profile <app_id> to targets add --steam <app_id>; the steam namespace keeps steam list.
--help groups the surface under four presentational headings (Capture, Targets, Environment, Data) that hide nothing, rendered through a custom help template because the pinned clap 4.5.32 cannot group subcommands natively. A bare fragcap lists the registered targets and prints a footer pointing at --help; an explicit fragcap targets prints the same listing without the footer.
No capture, attribution, pipeline, sink, or core code changed, and no dependency was added or removed; the change is confined to the fragcap-cli argument grammar, dispatch, and assembly seam, plus documentation and the master-specification command surface (section 17).
The targets hero command and interactive authoring (slice S055)
fragcap targets (and a bare fragcap) becomes the hero command: it runs discovery across its tiers, registers any newly found titles into local.db idempotently, and presents the registered targets as a numbered table with a CAPTURE column (ready or needs a target, derived from whether the entry names a Windows client or carries a resolvable anchor) and a neutral KNOWN column (the detected engine, anti-cheat, and DRM products, else "no online mode recorded", else "no launch data known"). Rows are ordered by handle, the listing ends by naming the next command (fragcap capture <n>), and an empty result prints the concrete commands that populate the store rather than an empty table. Registration is additive and idempotent: a repeat listing over an unchanged environment registers nothing new and never modifies or removes an existing entry.
The listing now writes a durable snapshot of what it displayed to local.db (a new listing_snapshot table, schema version 5 to 6), and a bare-integer selector resolves against that snapshot rather than the live store order. So fragcap capture 3 names the row the user just saw, even after an intervening add or remove shifts the live order; a position past the snapshot, or before any listing has run, is an out-of-range usage error (exit 2), distinct from a clean handle or name miss. This changes the S054 behavior where capture <n> resolved over the live order.
targets add gains interactive authoring. Pointed at an executable, it runs detection on the executable's directory and shows the engine, anti-cheat, and DRM evidence inline, then asks whether that executable is the process that holds the sockets: [Y/n/unsure]. The unsure branch is a first-class outcome and the reason the prompt exists: it registers the entry with its launch chain unresolved and records no socket holder the tool did not observe (P-9). yes records the executable as the resolved client; no records it as a launcher with the holder unresolved. When standard input is not a terminal, the same decision is supplied by --socket-holder yes|no|unsure (which requires --exe), so every branch is reachable without a terminal and a required-but-missing value is a usage error rather than a blocking prompt.
The target lifecycle is rounded out: targets scan <dir> --db <local.db> registers the titles it discovers (through the same idempotent registration the listing uses); targets remove <selector> deletes exactly the resolved target and refuses an ambiguous name (exit 2); and targets export [selector] / targets import <file> move targets between stores as a dedicated JSON array of target-entry objects (carrying each entry's identity), merging on the stable identifier so an export round-trips through an import with identical identifiers and no duplicate rows. This representation is deliberately not the published capture schema, whose export records are catalog games and omit the entry identity that merge-on-id requires (operator decision, 2026-08-18); the published schema is neither used for targets nor changed.
The capture-time promotion of an unsure-authored row to verified is partially delivered and partially deferred, stated here rather than reported as complete. The promotion mechanism ships and is tested: Store::promote_target_launch rewrites an entry's launch chain and raises its fidelity, and the launch-chain resolution logic is unit-tested end to end. The capture-time trigger is deferred: capturing an unresolved target requires a capture-by-observation mode this architecture does not have, since capture --target refuses a target that names no single Windows client. Wiring that trigger is follow-up work in its own slice (issue #152); no dead or fabricated promotion path was added to stand in for it.
doctor gains an action layer (doctor --fix, slice S056)
fragcap doctor is unchanged: it stays a read-only classifier that probes the environment, reports each check, names a remediation for every blocking failure, and exits 1 when anything blocks. A new fragcap doctor --fix adds an action layer above that same classifier. It prints the same report, then offers to perform the remediations the report named, one at a time, under the operator's confirmation. It acts only on remediations doctor already printed: each actionable check now carries a structured action alongside its human-readable remediation, constructed together so the two cannot drift, and --fix offers only the actions carried by a check present in the current report.
--fix is interactive and confirmation-driven, so it is refused (usage error, exit 2) when combined with --json, when stdout is not an interactive terminal, and, without --yes, when stdin is not a terminal. --yes pre-confirms every offered action for unattended interactive use but still requires a terminal stdout; --yes without --fix is a usage error. After the action phase, the classifier is re-run and the updated verdict is printed. Every action reports its honest outcome (performed, skipped, degraded, or failed); a failed action is never reported as success.
The actions offered map to the findings doctor already reports: obtain npcap (fetch and launch the vendor's own signed installer in a net-capable build, or open the official download page otherwise), relaunch the npcap installer for the WinPcap API mode, relaunch elevated (offered first so escalation precedes privilege-gated work; the elevated child re-checks and the parent stops), register the analyzer extcap integration, fetch the published catalog, and run discovery (tiers 1 and 2). The catalog and npcap fetch actions are gated on the net capability and degrade in a default build (the catalog finding becomes guidance naming catalog update; the npcap finding opens or names the download page). The extcap, catalog, and discovery actions reuse the existing extcap install, catalog update, and discovery-composition paths, so there is one path to each effect.
Two findings the classifier did not previously name are added as new, additive pure checks so --fix can act on them: a missing catalog store (warns, carries the fetch action) and no registered target entries (warns, carries the discovery action). Both are warnings, never blocking failures, so a ready machine, one with a catalog and at least one target entry, still passes and exits 0 with unchanged output. The classifier remains a pure function from an injected Inputs to a Report; the new target-entry count enters through the thin probe, and the action layer lives entirely in fragcap-cli above the classifier.
This change amends constitution Licensing rule 2 to permit the user-confirmed vendor-installer fetch; see the decisions fragment for the npcap license determination and the amendment.
Landing page and getting-started rewrite, and site docs convergence (slice S057)
The public site and the first-run documentation now describe the tool that shipped in S054/S055 rather than the profile-file tool that preceded it. The landing page opens with the problem fragcap solves that standard capture does not, stated for a visitor who has never thought about attribution, shows the real fragcap targets hero listing as its worked example, carries the dependency-model diagram (npcap required, Wireshark recommended, extcap optional), and directs the visitor to getting started with a single call to action. The getting-started guide is rewritten to end at a capture file on disk using fragcap targets then fragcap capture <n>: prerequisites are acquired in "Before you begin" (framed conditionally), the install step links the releases page and names the .msi / .zip / .sha256 assets, the verify step tells the reader to run the terminal as Administrator and is the single home of the optional fragcap extcap install, and the target step presents automatic discovery as the happy path and defines a Steam App ID inline. The npcap narrative across the guide, the installer exit-dialog prompt, and the S056 doctor --fix action now tell one coherent story (detection-only, with the user-confirmed vendor-installer fetch permitted).
This resolves the getting-started QA batch: issues #130 (extcap command surfaced before fragcap is installed), #131 (no download link), #132 (prerequisites installed as a numbered step), #134 (run as Administrator; extcap home), #135 ("Get a profile" unclear), and the documentation half of #133 (installer npcap exit-dialog contradicting the docs narrative).
The reference set is converged onto the shipped command surface: the CLI reference is rewritten to capture, replay, targets, technologies, steam, doctor, extcap, catalog, and schema (no run/tap/watch/profile/steam profile); the capture-modes guide uses fragcap capture; and the two pages that taught authoring profile files (guides/writing-a-profile, reference/profile-schema) are removed, with their navigation and inbound links rerouted. No documentation page references the retired verbs, the retired profile directory, the --profile selector, or a profile slug that no longer exists.
fragcap capture now accepts a stored-target selector positionally, so the fragcap capture <n> form the targets listing hints (and the README and site docs show) works as advertised: the positional is equivalent to --target and mutually exclusive with it, resolving a handle, name, or row index the same way. Before this, only the --target flag form parsed, so the listing's own hint was rejected as an unexpected argument.
A small companion change removes the leftover profile dir identity row and the Profiles section from fragcap doctor (in both the human report and --json), so the getting-started sample is faithful to the binary and free of the retired directory. The bundled profile set was already permanently empty and the user profile directory unwritable after S054, so the rows reported dead surface; the classifier keeps its exit status and every other row unchanged, and the internal Profile capture-config type is untouched. Specification sections 15.5 and 26.3 are reconciled to match.
IGDB enrichment and its credential walkthrough (issue #144 stretch goal) are deferred to a dedicated slice: the codebase has no IGDB or credential-storage plumbing (the S050 local.db columns the handoff plan assumed were never built), so documenting a credential-registration flow with no consumer would describe unbuilt functionality (P-11). A dedicated slice should carry the storage, the fetch, and the walkthrough together.
CLI targets convergence: default --db and extcap by target selection (slice S058)
Two parts of the fragcap-cli surface now speak the targets model the rest of the tool adopted in S054/S055.
The explicit targets subcommands (add, show, remove, export, import, list) no longer require an explicit --db. When it is omitted they resolve the same default local store the bare fragcap targets hero command uses (the FRAGCAP_LOCAL_DB override, else the per-user default), so fragcap targets add --steam <app_id> and its siblings run with no store path, exactly as the listing does. An explicit --db still overrides, and a subcommand that must open a store with no resolvable location fails with a named error rather than a panic. This resolves the manual-registration path the S057 getting-started guide had to defer to the reference (issue #157).
The Wireshark extcap capture path now selects a stored target instead of resolving a retired profile file. The stored-target resolution that capture uses is extracted into one shared implementation (commands/target_resolve.rs), and extcap calls it: its configuration dialog presents a target selector (a handle, a name, or a row index), resolved against the local store exactly as capture --target resolves it, so the analyzer dialog and the command line select capture identically. The extcap control grammar is otherwise unchanged (interfaces, link types, the four-option config block, and FIFO streaming), so unmodified Wireshark still drives fragcap; only the meaning of the number=0 selection option changes from a profile reference to a target selector. No code path in the extcap capture handler resolves a profile file through the search-path or bundled-set cascade any longer. The S057 CLI-reference "legacy" callout is removed and the converged options are documented (issue #156).
The extracted target_resolve seam takes its inputs explicitly rather than from the capture argument struct, so both commands share one resolution body; it is also the single place the follow-up launch-and-observe slice (S059, issue #152) extends.
fragcap-cli only: no change to fragcap-core, the pipeline, attribution, or the capture orchestrator's behavior, and no new dependency or Cargo.lock delta. The extcap capture golden is regenerated to reflect the synthesized stored-target profile (a single target-role stage, as capture synthesizes), with the conservation identity and packet count unchanged.
Launch-and-observe capture and capture-time promotion (slice S059)
A stored target whose launch chain is unresolved, the record an interactive targets add writes when the user answers no or unsure to whether the executable they pointed at holds the sockets, can now be captured. Before this it named no client and capture refused it; S055 shipped the promotion mechanism but deferred the capture-time trigger (issue #152).
Capturing such a target runs in launch-and-observe mode: fragcap builds a profile from the executable the user did record (a two-stage profile whose observed executable is a launcher stage and whose terminal client stage matches the process that descends from it and holds the sockets), captures normally, and aggregates which process image the run attributed the most packets to. When the run observes a dominant socket-holding image, the stored target is promoted to that resolved client at verified fidelity (capture-time promotion), so a second capture addresses the client directly and the target reads ready. A run that observes nothing leaves the target exactly as it was: promoting on no observation would record a socket holder the tool never saw, which it does not do.
The observe-mode resolution is added to the shared commands/target_resolve.rs seam S058 extracted, so both capture and the Wireshark extcap path resolve an unresolved target identically; only capture writes the promotion back (extcap is a streaming bridge, not the store owner). The run's dominant socket-holder rides on an additive per-image tally on CaptureStats, folded across capture threads and kept out of every counter total, completion summary, and written file, so every committed golden is reproduced byte for byte.
This slice adds no direct-executable launcher: live launch stays restricted to the existing Steam-anchored path, so an unresolved target with no platform anchor is started by the operator (by any means) and observe-mode captures it. The whole resolve, observe, and promote-or-leave decision is verified offline over the scripted fixture pipeline; only the literal steam://run launch is Tier 2 and is not exercised in continuous integration.
No new dependency and no Cargo.lock delta. New glossary terms: launch-and-observe, observed socket-holder, capture-time promotion.