# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is A Ruby gem (`picopackage`, CLI `ppkg`) for installing and updating single-file packages: ordinary source files that carry their own provenance in a commented YAML block at the end (`# @PICOPACKAGE_START` … `# @PICOPACKAGE_END`). See README.md for the format and notes.md for the resolution rules and open design questions. ## Commands ```bash bin/setup # install dependencies rake # tests + StandardRB (the default task) rake test # tests only rake test N=/pattern/ # run tests whose names match a pattern ruby -Itest test/test_merge.rb # run a single test file bundle exec standardrb # lint (config in .standard.yml, not .rubocop.yml) bundle exec standardrb --fix ``` Releasing: bump `lib/picopackage/version.rb`, then `bundle exec rake release`. ## Architecture Everything lives under `lib/picopackage/`; `lib/picopackage.rb` just requires the pieces and defines the shared error classes. The flow of an `install`/`update` is: **CLI → Fetch.fetch → Provider → Resolver → (Merge, Cache) → Package#save**. - **package.rb** — the data model. `Package` wraps a file's content and splits it into `Payload` (the code, normalized to `rstrip + "\n\n"`) and `Metadata` (a Struct parsed from the YAML block). `Metadata#extra` round-trips unknown keys so old tools can't corrupt packages written by newer ones. Key distinction: `modified?` (payload differs from its recorded `payload_checksum` — a human edited it) vs `diverged?` (payload differs from `base_checksum`, the upstream ancestor — true after a merge even when `modified?` is false); `diverged?` is what decides whether a fast-forward is safe. - **provider.rb** — resolves a URL to a fetch strategy. `Provider.for` walks `PROVIDERS` (FileProvider, GithubGistProvider, OpenGistProvider, DefaultProvider); `handles_url?` returns true/false/`:maybe`, and `:maybe` providers prove themselves by fetching (they fetch in their constructor). Upstream metadata *claims* (url, version, licence) are adopted with `||=`; *records* (checksums) are always recomputed from the payload actually received. - **fetch.rb** — two things: the HTTP fetcher (`Fetch`: 1 MB cap, 10s timeout, ≤5 redirects to http/https only, ETag support; deliberately `Net::HTTP`, never `URI.open`) and the `Resolver`, which contains all install/update decision logic and returns a `Result` with a state (`installed/adopted/current/updated/merged/conflict`) — deciding what to do about a conflict is the CLI's job. - **merge.rb** — three-way merge shelled out to `git merge-file` or `diff3`; no Ruby reimplementation. - **cache.rb** — content-addressed store of upstream payloads under `$XDG_CACHE_HOME/picopackage` (override: `PICOPACKAGE_CACHE`), keyed by checksum. This is where merge bases come from; a cache miss means the merge is refused, not guessed. - **cli.rb** — argument parsing and reporting only; commands are `install update package verify inspect scan`. ## Invariants (violating these is a bug, not a style choice) 1. **The checksum decides, never the timestamp.** mtime is not evidence about content; equal payload checksums settle the content question regardless of timestamps. 2. **Merges run on the payload alone; the metadata block is derived, never merged.** It is regenerated on every write — `Package#generate_package` is the single choke point where `payload_checksum` is computed, so a stale checksum can never survive into a written file. 3. **Conflicts are non-destructive.** The user's file is never touched; conflict markers go to a sibling `.picopackage-merge`. 4. A bare local file whose payload differs from upstream is an *unrelated file*, not a modified package — refuse, don't merge. ## Tests Minitest, in `test/test_*.rb`. `test/test_helper.rb` provides `PicopackageTest#with_dirs`, which gives every test its own upstream dir, project dir, and cache root (the cache is global state; never share one across tests). Fake an upstream by writing a real file and fetching it through the real `FileProvider` (`publish` + `upstream_package` helpers) rather than stubbing. Fixtures in `test/files/` are deliberately malformed or copied verbatim and are excluded from linting.