--- name: serve description: Write and create issues or guarded pull requests for approved nutrients with trace and deduplication markers, using crab serve. Use when serving a menu, writing the why and how of a nutrient, explaining the trace footer, or checking why a nutrient was skipped. Do not use for ordinary issue or pull-request authoring unrelated to a Hungry Crab nutrient, menu, or implementation receipt. --- # Serve nutrients `crab serve` creates issues or, for nutrients you have implemented in the maw — REIMPLEMENT through the clean room, COPY and COPY_FILE by carrying the files with their attribution — guarded pull requests. You write the two sentences that make them worth reading. ## Notes file Before serving, write `notes.json` with one entry per nutrient you keep: ```json [ { "id": "crab:ci:ci.cache", "why": "Every CI run reinstalls the dependencies; the prey's cache step cuts its lint job to 12 seconds.", "how": "Add `enable-cache: true` to astral-sh/setup-uv in ci.yml, keyed on uv.lock; run it on one PR before enabling it for the matrix." } ] ``` Rules for `why` and `how`: - Name a concrete effect in this repository (time, risk, a class of bugs), not a generic benefit. - `how` names files and tools of the maw, adapted to its toolchain; it is the first step, not a full plan. - Never paste prey text unless the license mode is `COPY`; even then cite the path. - Optional fields: `title` (if the generated one is off), `serve_as`, `effort`, `risk`. A note may narrow `serve_as` (`pr` → `issue` → `idea`) but never widen it; values outside the vocabulary are ignored with a warning. ## Commands ```bash crab serve --maw . --ids id1,id2 --notes notes.json --as dry-run # previews crab serve --maw . --ids id1,id2 --notes notes.json --as issue # creates issues crab serve --maw . --top 5 --as dry-run # top of the menu cat receipt.json | crab serve --maw . --ids id1 --notes notes.json --as pr-branch crab spec crab:ci:ci.cache # the maw-relative path of a nutrient's clean-room specification ``` - Dry-run is the default; show the previews and get a confirmation before `--as issue`. - `serve.issues: off` in `.crab.yml` blocks issue creation; `auto` allows it in CI without asking. - `serve.prs: off` blocks pull-request publication; `ask` requires explicit `--ids`, while `auto` permits ranked selection such as `--top`. - Pull-request mode accepts the strict JSON implementation receipt returned by `crab-cleanroom-impl` on **stdin**. For a batch, concatenate the complete JSON documents with whitespace between them. The receipt stream is transport only: Hungry Crab does not persist it as a second PR state store. - Pull-request mode publishes a card only when it is REIMPLEMENT, marked `serve_as: pr`, and has a receipt; every other selected card is skipped with its reason, so `--top` serves what it can. Each published nutrient has exactly one receipt, whose own `nutrient_id` binds it to the menu card, and its specification at the path `crab spec ` prints, which is read, scanned and carried in the pull request. Duplicate, malformed, stale, non-UTF-8, missing or maw-escaping declared files, and a missing specification, fail closed before provider effects. - `serve_as` is honoured in every mode: an `idea` card (`hunger: : ideas-only`, or an issue lesson) is skipped unless its id is passed explicitly, and then only as an issue; an `issue` card is never published as a pull request. `serve` reads the `hunger` block of `.crab.yml` again, so a category switched off or narrowed after compare holds. - A COPY or COPY_FILE nutrient takes a **materialization receipt** instead of the clean-room one (below); the two kinds may share one stdin stream. A receipt of the wrong kind for the card's licence mode fails the batch before any effect. - The maw must have a GitHub `origin` remote; `gh` must be authenticated. Pull-request publication also requires the maw to be the root of a git repository with its `origin` fetchable: files are staged relative to that root, so `--maw packages/app` is refused rather than published wrong. - The receipt on stdin is read as UTF-8 bytes, whatever the console's own code page is. ## Pull-request transaction `--as pr-branch` does not publish the current dirty diff. It consumes only the exact paths in the clean-room receipt, freezes their current UTF-8 bytes, and then follows the guarded transaction: 1. prepare **all** selected nutrients into immutable payloads — the receipt's files plus the specification at the path `crab spec ` prints, without which the nutrient is refused; 2. scan every file plus PR title/body for possible secrets; 3. reconcile marker-bearing PRs already present on GitHub; 4. only then create/reuse the deterministic nutrient branch and create the PR; 5. write the provider URL to the ledger after GitHub returns it. For a reviewed false positive, use `serve.publication_allow` in the maw's committed `.crab.yml`: each entry has an exact `finding` SHA-256 identity printed by the refusal and a non-secret `reason`. Commit the policy before retrying. The CLI rechecks it against HEAD before effects; a different file, content, line or rule invalidates the exception. Never put the matched value in policy, use a wildcard, or disable scanning. Secret-shaped paths are reported by opaque identity. The branch effect uses a detached temporary worktree and stages the frozen bytes without repository clean filters. A rerun after a successful PR but failed ledger save reconciles the existing PR by its opening `` marker instead of creating a duplicate. `max_prs_per_run` counts new PRs only; reconciliation does not spend creation budget. ## COPY pull requests: the materialization receipt A COPY verdict lets prey files travel; COPY_FILE lets whole files travel with their own licence header. Start with a separate, temporary worktree of the maw's current default branch (`git worktree add --detach origin/`). Carry there and pass `--maw ` to compare and serve, so the maintainer's checkout stays free of copied inputs. You do the carrying in that worktree before serving: copy or adapt the prey file, keep the copyright and permission notice it came with, and never mix in text the verdict does not cover. The crab reads the source's licence and NOTICE files at that commit and the notice reproduces them; a licence that asks for its notice to travel, from a prey with no licence file to carry, is refused. Then write one receipt per nutrient and pipe it to `--as pr-branch`: ```json { "version": 1, "kind": "materialization", "nutrient_id": "crab:ci:ci.cache", "source": {"label": "pypa/pipx", "url": "https://github.com/pypa/pipx", "sha": "", "license": "MIT"}, "taken": [ {"maw_path": ".github/workflows/ci.yml", "prey_path": ".github/workflows/test.yml", "verbatim": false} ], "summary": "carried the cache step over, adapted to uv", "checks": ["uv run pytest -q"] } ``` `source` must be the meal's prey exactly as `menu.json` records it — label, URL, commit and licence — because the notice will cite it. Every `prey_path` is checked against the prey at that commit through read-only git plumbing in the cache; a `verbatim` file is compared byte for byte, and under COPY_FILE every file must be verbatim. A file in vendored code, under a directory with a licence file of its own, or whose header names another licence than the source's is refused: the verdict is the repository's and does not answer for it. The crab then appends the receipt to `.crab/attributions.json` (once: a rerun or a reconciliation adds nothing, and the same nutrient from another commit is a second receipt that never rewrites the first), renders the notice file named by `attribution_file` in `.crab.yml` from all receipts, and carries the taken files, the receipts and the notice in one pull request whose body says what was taken from where and what the licence asks for. `crab attribution --maw .` rewrites the notice file from the receipts; `--check` fails when it is missing or stale, a stored receipt is malformed, a carried text's SHA-256 differs, or a recorded maw file is missing or unsafe. Stored array members are never silently discarded. Legacy carried texts without a digest must be verified against the recorded prey commit before migrating them; hashing unverified stored prose would bless the same corruption. Legacy missing checks remain explicitly unrecorded. New COPY records retain checks, and both PR paths quote producer summaries/checks as fenced data, neutralizing raw reconciliation comments separately. Blockquotes and HTML entities still permit GitHub closing references, including full issue URLs; receipt and obligation prose therefore stays in code fences that its contents cannot close. Metadata that carries a closing directive is redacted instead of becoming provider syntax. GitHub also recognizes entity-encoded mentions: metadata displays `@` as `(at)` while JSON retains exact names. Publication scanning checks that display form as well, so the substitution cannot conceal a credential-bearing URL. The notice names only what the crab actually carried in: a COPY nutrient filed as an issue took nothing and appears nowhere. Serve from a checkout that is up to date with the default branch: a pull request that would drop a receipt the default branch already holds is refused. Rewriting refuses missing receipts evidenced by the old notice or this maw's Git HEAD; restore them, or review an intentional removal and use `crab attribution --maw . --allow-remove`, then commit the reviewed change. That flag cannot weaken `--check`. Keep receipts and notices in version control: outside Git, deleting both leaves no evidence from which the crab can discover previous copies. Stored hashes detect text corruption; they are not a signature proving an independently edited record's origin. `attribution_file` must be a plain relative path in the maw, and the crab never overwrites a file it did not write. A COPY card whose licence verdict asks for human review is published only when you name it with `--ids`. The receipts and the notice are written at publication and were never in the working tree, so the `checks` of your receipt did not see them. After serving, let the maw's own gate run on the branch — its CI, or the gate by hand in a checkout of the branch where there is no CI — and fix the branch before anyone merges it. The notice is headings and paragraphs only, which a Markdown formatter's defaults leave alone; where the maw's formatter rewrites it anyway, exclude the file from the formatter, never from `crab attribution --check`. Publication leaves the receipt's input paths in the worktree it read. The text and JSON reports name those paths and the nutrient branch, including when an existing pull request is reconciled. After verifying the branch contains the files, receipts and notice, clean up the disposable worktree. If you carried in the maintainer's checkout instead, use `git stash push --include-untracked` to preserve all local changes and clean that checkout; this also stashes unrelated edits, so inspect the stash before restoring anything. Do not sweep the copied inputs into an unrelated commit with `git add -A`. Keep `.crab.yml` and `.crab/ledger.json` (`ledger: repo`) on the default branch in their own commit. Before removing the carrying worktree, preserve its configuration and ledger updates separately; the nutrient pull request does not carry them. Its copied files belong only with their receipts and notice on the nutrient branch. ## Whose name the artifacts carry Issues and pull requests go into **the maw**, so serving into another repository means digesting that repository as the maw: `crab serve --maw ../their-repo`. It needs a working tree, because the comparison and guarded PR publication operate against real maw files. By default artifacts are filed as whoever `gh` is logged in as. `serve.token_env` in that repository's `.crab.yml` names an environment variable holding a token to use instead — a GitHub App installation token, or a machine account's — and then artifacts carry the crab's name rather than a person's. The first log line of an effectful serve says which identity is in use; read it back to the user before confirming. Opening an issue needs no special permission on a public repository, but **creating a label needs write access**. Where the crab cannot create its label it says so once and serves without labels; deduplication is unaffected, because it reads the `crab:` marker in the body, not the label. ## What an issue contains See `references/issue-template.md`. Every issue carries a hidden `` marker (deduplication across runs and machines), the `hungry-crab` label, and a trace footer with prey, commit, license and mode. The ledger records `served` with the issue URL. Pull requests carry the same opening marker and trace, plus the clean-room implementation summary and a link to the specification. The files are exactly the receipt-declared, hash-verified publication payload plus the specification itself; unrelated dirty maw files are not discovered or added. ## Skips and their meaning | Reason | Meaning | |---|---| | `ledger: rejected (...)` | decided earlier; do not re-propose unless the user asks | | `ledger: served ` | already served; link to it | | `issue #N exists (open)` | found by marker on GitHub; the ledger is updated | | `pull request exists ; ledger reconciled` | provider already has the PR; local truth was repaired without creating another | | `serve.max_prs_per_run reached (N)` | creation budget is exhausted; an already-existing PR could still have reconciled | | `not in the menu` | run `crab compare` again or check the id | | `serve_as: idea` | the hunger block or the card itself keeps it an idea; pass the id explicitly to file it as an issue anyway, never as a pull request | | `serve_as: issue` | (pull-request mode) the card is an issue, not a pull request; serve it with `--as issue` | | `hunger: is off` | the maw switched the category off in `.crab.yml`; nothing of it is served | | `license mode IDEAS_ONLY has no pull-request path` | only REIMPLEMENT, COPY and COPY_FILE become pull requests; serve the card as an issue | | `no clean-room receipt` | pull-request mode found no receipt for a REIMPLEMENT card on stdin; run the clean-room protocol first | | `no materialization receipt` | pull-request mode found no receipt for a COPY card on stdin; carry the files and write the receipt first | | `the licence verdict asks for human review; name it with --ids` | the meal's verdict wants a person to look; decide, then serve the card by id | | `notes quote commenter text; rewrite why/how in your own words` | the card is not of licensed origin and your `why` or `how` repeats one of the prey's issue titles, or eight words in a row of one; state the need in your own words and link the issue | | `the prey's issue titles cannot be read, so these notes cannot be checked; run crab compare again` | the prey digest the meal names is gone from the cache; recompare, then serve |