recipes · v0.1.1

A compliant personal-data legal set + cookie-consent banner for a Russian-jurisdiction site

verified 18.07.2026from ag-web@axon

A public site that processes any personal data of people in Russia needs a legal-document set and a cookie banner that satisfy 152-ФЗ — not a translated GDPR template that blocks the wrong things and is silent on what the law actually demands.

sporo recipe11 sections · 7 scars · sealed 0.1.1
download .md ↓
the 11 gated sections
01The problem
02Why the obvious approach fails
03The principles
04The ground it needs
05The contracts4 bound
06The build sequence
07The seams
08The scars7 scars
09Verification
10The trade-offs
11For the human
what you'd hand an agent
11
gated sections
7
earned scars
4
bound contracts
1
self-contained file

Readable before it runs — audit the prose, then let an agent build it on your stack.

What a reader receives is the exported form — this, with the adoption protocol appended. Every section below was distilled from one live implementation: its build record, its release history, its fix commits, and the scars only a real build leaves.

Works with any agent
Claude CodeOpenAI CodexCursorGitHub CopilotGoogle GeminiWindsurf
the recipe, section by sectioneach gated · scars open
01

The problem

You run a public site — a magazine, a shop, a service — and it processes personal data of people located in Russia. It almost certainly does: an analytics tag reads an IP and a device fingerprint; a contact form takes an email; comments and accounts take names; a newsletter takes an address. The moment any of that is true, Russian law (152-ФЗ «О персональных данных», 149-ФЗ, and for marketing 38-ФЗ «О рекламе») binds you as an operator of personal data, with duties that a Western privacy template does not cover and partly contradicts.

You do not have: the right set of legal documents (which documents, how many, why), the content each one must carry to be valid, the operator registration the law requires before you process anything, and a cookie banner that behaves the way the law — not a marketing plugin — requires.

The output is: a published set of separate legal documents carrying mandatory content and the operator's identifying details; an operator notification filed with the supervisory authority (Roskomnadzor / РКН); and a cookie-consent banner that holds every non-essential tracker back until the visitor affirmatively opts in, and can prove the choice later.

You have it when:

  • Every document the law obliges for your feature set exists, each is a separate document (consents are not folded into the terms of use), each carries the operator's full identifying details, and each cites the specific article it rests on.
  • The operator notification is filed before processing begins, or you can name the narrow statutory exemption that releases you (there are only a few, and most sites meet none of them).
  • Loading the site in a clean browser fires zero network requests to any non-essential third party and writes no persistent tracking identifier, until the visitor clicks accept. Rejecting leaves that same clean state. Strictly-necessary and functional storage works regardless.
  • You can produce, for any consent, a record of who agreed to what, when, and in which revision of the document.
02

Why the obvious approach fails

The next agent reaches for a cookie-consent plugin and one "Privacy Policy" page — the GDPR shape everyone has seen — translates it to Russian, and ships. It fails on five concrete points, each of which is a live legal exposure, not a stylistic quibble:

  1. One policy is not the document set. 152-ФЗ art. 9 requires the subject's consent to be a specific, informed, freely-given act — and in practice a separate document, not a clause buried in a terms-of-use page. Distributing personal data to an open audience (public author profiles, comments) requires its own consent under art. 10.1. A newsletter requires its own consent (152-ФЗ + 38-ФЗ art. 18). Fold these together and none of them is valid.

  2. "Accept cookies" is not consent to process personal data, and vice-versa. They are two different legal acts under two different bases. A single banner that conflates them satisfies neither.

  3. The GDPR banner blocks the wrong categories. GDPR-shaped tooling tends to gate functional preferences (theme, layout) behind consent and can be lax about analytics. Under the Russian model, strictly-necessary and functional storage needs no consent (blocking it is a self-inflicted UX wound), while analytics/marketing trackers must not fire before an opt-in.

  4. It ignores duties that have no GDPR analogue. Operator notification to the supervisory authority before processing (152-ФЗ art. 22); data localization — the primary databases holding Russian citizens' data must be physically in Russia (art. 18); a separate notification before any cross-border transfer (art. 12). A plugin does none of this.

  5. It uses the wrong lawful bases and misses the requisites. Every document must name the operator's full identifying details and cite the specific article each purpose rests on. A translated template names a foreign entity and the wrong statute.

The reader's instinct produces a site that looks compliant and is exposed on all five. This recipe exists to replace the instinct.

03

The principles

  1. Consent is a separate, specific, informed, and free act — never bundled, never a condition of access. (152-ФЗ art. 9.) One consent document per purpose family. It may not be pre-ticked, and refusing it may not deny access to content the consent is not actually needed for. A consent that is the price of reading the site is not "free" and is void.

  2. The document count follows your features, not a fixed number. A minimal read-only site needs fewer; each capability that touches personal data pulls in its obligatory document (see the feature→document map in the build sequence). Adding a feature is a legal event, not just a code change.

  3. The operator's identifying details appear in every document. (152-ФЗ art. 18.1.) Legal name / sole-proprietor name, tax and registration numbers, registered address, and a contact email. A document without them cannot be relied on.

  4. Categorize cookies by legal necessity, not by vendor. Strictly-necessary and functional storage need no consent and are always on; analytics and marketing need prior consent. The vendor is irrelevant to which bucket a key falls in — its purpose decides.

  5. The consent record itself needs no consent. Storing the visitor's own accept/reject choice is the operator discharging a legal obligation (proving the choice), not a tracking act — it is always-on, and the cookie policy must say so.

  6. Prior-blocking is the load-bearing banner behavior. No non-essential third-party request and no persistent identifier may occur before an affirmative decision — and this includes the paths that survive with JavaScript disabled (a no-JS tracking pixel) and identifiers created at page-init. "The script waits for consent" is not enough if a pixel or an ID beat it.

  7. Consent is versioned and auditable. Store who / what / when / which revision / from where. When the meaning of a document or the banner's category set changes, its revision changes, and stored consents to the old revision must be re-collected. A cosmetic edit is not a new revision; a change in what you told people is.

  8. Withdrawal is as easy as granting. A visible control re-opens the banner and lets the subject revoke. The subject's right to withdraw (art. 9) and to complain to the supervisory authority must be stated in the documents.

  9. The regime is keyed by the visitor's jurisdiction, never by a market they picked. If the site serves more than one legal regime, a display preference (which market's content to show) must not select the consent model — that is a legal fact about where the person is, and keying it off a dropdown puts a breach one click away. Resolve conflicts strictest-wins. (This is a portable instance of the same principle content localization needs: never key a legal fact off an assumption.)

  10. Fail secure on every unknown. An unresolved model, a missing category, a malformed law-layer entry — each must resolve to more blocking, not less. The harmless error is a banner someone didn't legally need; the unrecoverable one is a tracker that fired without consent.

04

The ground it needs

Four things must be standing before the sequence starts. Each is a ladder: probe for it, build the smallest version, or degrade with a label the reader of your output can see.

A definite operator identity (the requisites). Why load-bearing: the requisites are mandatory content in every document (art. 18.1) and the operator notification cannot be filed without them. Probe: is there a registered legal entity or sole proprietor behind the site, with tax/registration numbers and a registered address? Build the smallest: if the venture is not yet registered, that registration is itself a prerequisite — there is no lawful "operator" without it. Degrade: none. This rung has no fallback; a site processing personal data with no registered operator is unlawful, not merely under-documented.

A complete inventory of what personal data you collect, why, and where it flows. Why load-bearing: the privacy policy must list every category of data, every purpose with its legal basis, every recipient, and every cookie/storage key with its retention — and it must be complete and current, because the enforcement gate is "code collects X that no document discloses." Probe: can you enumerate, from the running system, every field a form or an account takes, every event an analytics tag sends, every storage key the client writes, and every third-party host the browser contacts? Build the smallest: a hand-written data map — a table of (data point → purpose → legal basis → retention) and a list of (cookie/key → category → lifetime) and (third-party host → what it receives → is it in Russia?). Twenty rows, not a system. Degrade: if the inventory is partial, publish only what you can actually stand behind and treat every undisclosed collection as a defect to close before launch, not a rounding error — an undisclosed cookie or host is exactly the finding that draws a fine.

A place to record consents (the audit trail). Why load-bearing: principle 7 — consent you cannot prove is consent you did not obtain. Probe: is there an append-only store you can write a consent row to, keyed by subject and revision? Build the smallest: a single append-only table (or the anonymous visitor's own browser store for the cookie choice) recording subject, document slug, revision, timestamp, choice. Degrade: at minimum the cookie banner must persist the visitor's choice locally with the revision it was made under; without even that, you cannot re-prompt correctly and cannot prove refusal.

Legal review — this is YMYL. Why load-bearing: the documents are binding legal instruments; wrong content is worse than none. Probe: do you have access to a lawyer competent in 152-ФЗ? Build the smallest: use this recipe's structure and the harvested skeletons as a draft, then have it reviewed before you present it as binding. Degrade: if you must publish before review, mark the documents as a draft pending legal review where users can see it — never present unreviewed prose as a finished legal instrument.

05

The contracts

Four shapes. None is read by a consumer outside your own system, so all are Binding: adapt — rename fields into your own language, keep what they mean.

The operator requisites block — appears verbatim in every document. Placeholders where a real value would identify a real person; fill them with your own.

Binding: adapt

operator:
  kind: "legal-entity | sole-proprietor"
  name: "<full registered name>"          # legal name, or sole-proprietor's full name
  tax_id: "<taxpayer number>"             # ИНН
  registration_id: "<registration number>" # ОГРН / ОГРНИП
  address: "<registered address>"
  email: "<contact email for data-subject requests>"

The cookie-category model — drives what the banner shows and what it blocks. The needs_consent and blocking flags are the legal core; getting essential or functional wrong in either direction is a scar below.

Binding: adapt

categories:
  - key: essential      # auth, security, the consent record itself
    needs_consent: false
    default_on: true
    blocking: false     # never held back — the site cannot run without it
  - key: functional     # theme, layout, "already subscribed" — no PII, no identification
    needs_consent: false
    default_on: true
    blocking: false
  - key: analytics      # traffic statistics, visitor identifiers
    needs_consent: true
    default_on: false   # MUST default OFF — opt-in, never pre-ticked
    blocking: true      # held back until an affirmative decision
  - key: marketing      # ad/retargeting tags, if any
    needs_consent: true
    default_on: false
    blocking: true

The banner's stored decision — persisted in the anonymous visitor's own browser. The revision field is the trap: it binds the choice to the document version it was made under, so a revision bump can invalidate it (principle 7).

Binding: adapt

{
  "revision": "<banner revision this choice was made under>",
  "decidedAt": "<ISO-8601 timestamp>",
  "choice": { "essential": true, "functional": true, "analytics": false, "marketing": false }
}

The consent audit record — what you must be able to reproduce for any consent, cookie or document. subject is either an account id or, for pre-account acts (newsletter, the cookie choice), the email or the anonymous visitor id.

Binding: adapt

{
  "subject": "<account id | email | anonymous visitor id>",
  "doc_slug": "<which document or 'cookie-categories'>",
  "revision": "<the document/banner revision agreed to>",
  "decided_at": "<ISO-8601 timestamp>",
  "source": "<where the act happened: registration | subscribe | cookie-banner>",
  "choice": "<accept | reject, or the per-category map for cookies>"
}
06

The build sequence

1. Inventory the personal data, cookies, and third-party hosts

Enumerate, from the running system: every field a form or account collects; every property an analytics event sends; every cookie / local-storage / session-storage key the client writes; every external host the browser is told to contact. For each host, record what it receives and whether it is inside Russia.

Done when: you have the three tables from the ground section filled, and nothing the code does is missing from them.

2. Assemble the operator requisites

Fill the requisites contract from the registered operator's real details. If there is no registered operator, stop — that registration is a hard prerequisite.

Done when: the requisites block is complete and matches the operator's registration documents exactly.

3. Decide the document set from your features

Map features to obligatory documents. The set is at minimum a privacy policy and a terms of use; each capability below pulls in one more:

A feature that… obliges the document resting on
processes any personal data at all Privacy policy (Политика обработки ПД) 152-ФЗ art. 18.1
lets people register / hold an account Consent to processing (Согласие на обработку ПД) 152-ФЗ art. 9
publishes user data to an open audience (public profiles, comments) Consent to distribution (Согласие на обработку ПД, разрешённых для распространения) 152-ФЗ art. 10.1
sends a newsletter / marketing email Consent to mailings (Согласие на получение рассылок) 152-ФЗ art. 9 + 38-ФЗ art. 18
sets any cookie / browser storage Cookie policy (Политика использования файлов cookie) 149-ФЗ + cookie practice
has any usage rules at all Terms of use (Пользовательское соглашение) civil law

Each consent is a separate document; the terms of use references the consents but never contains them.

Done when: you can name, for each of your live features, the document it obliges — and each consent is a standalone document, not a clause.

4. Write each document with its mandatory content

Every document opens with the requisites block and a revision date. Each consent document carries, at minimum (152-ФЗ art. 9 ч. 4): the operator's identity; the subject's identity and how they are identified; the exhaustive list of personal data; the purposes; the list of actions and the method of processing; the term/validity and the withdrawal method. The privacy policy additionally lists data-subject rights (152-ФЗ art. 14), names third-party recipients, states data localization (primary databases in Russia, art. 18), and — if any recipient is outside Russia — a cross-border-transfer block (art. 12). State that non-special, non-biometric data only is processed, if that is true.

Done when: each document carries the requisites, a revision date, and every field its governing article requires; a reviewer can check each field against the article and find nothing missing.

5. File the operator notification with the supervisory authority

Before processing begins, file the operator notification (уведомление об обработке персональных данных) with Roskomnadzor, unless you meet one of the narrow statutory exemptions (152-ФЗ art. 22 ч. 2 — essentially: paper-only processing, a handful of security/transport carve-outs). A public site with analytics, accounts, or a newsletter meets none of them.

Done when: the notification is filed (or the specific exemption clause that releases you is written down), before the site starts processing.

6. Classify every cookie/storage key into the category model

Put each key from step 1 into essential, functional, analytics, or marketing by its purpose. Auth tokens, security, and the consent record itself are essential. Theme, layout, "already subscribed" flags with no PII are functional. Anything that identifies or counts a visitor is analytics or marketing. The cookie policy documents each key, its purpose, and its retention.

Done when: every key from step 1 is in exactly one category, the essential/functional ones carry no identifying data, and the cookie policy lists them all.

7. Build the banner with prior-blocking

The banner shows the categories from the contract, with non-essential ones defaulting off and reject as prominent as accept. Until an affirmative decision is stored: every blocking category's scripts are held back, no persistent identifier is written, and — critically — the no-JS fallback paths (a tracking pixel in a <noscript>) are also suppressed. On accept, the blocked scripts initialize; on reject, the clean state persists. A visible control re-opens the banner for withdrawal.

Done when: in a clean browser, the network panel shows no request to any non-essential third-party host and storage holds no tracking identifier until accept is clicked; reject preserves that; essential/functional keys are present throughout.

8. Wire consent recording, versioning, and re-prompting

Persist the stored-decision shape with the banner revision it was made under; write account and newsletter consents to the audit record with their document revision. When a document or the banner category set changes meaning, bump its revision so stored consents to the old revision are re-collected. Asymmetric shelf life is good practice: keep an acceptance far longer than a refusal before re-asking, and never destroy a stored consent merely because the current revision is momentarily unknown (a lagging config load must not delete a legally-recorded choice).

Done when: a consent produces an auditable record bound to a revision; a revision bump re-prompts affected visitors; and a transient failure to read the current revision keeps existing consents rather than wiping them.

07

The seams

  • The operator requisites — every value in the requisites block. The recipe fixes the fields; the operator supplies the values.
  • The document set — which documents exist is feature-driven; a project with no newsletter has no newsletter consent. Never hardcode a fixed list.
  • Retention periods — how long each cookie/consent lives, and the accept-vs-reject asymmetry, are policy values, not constants baked into the banner.
  • The banner revision string — the token that re-prompts on change. Its format is yours; its meaning (a change here re-collects consent) is fixed.
  • The category list — the four categories here are the common set; a site with no marketing tags omits that category. What must not vary is the needs_consent/blocking logic per category.
  • The jurisdiction regime — for a multi-market site, which legal model applies is resolved per visitor-jurisdiction; the resolution seam (and strictest-wins default) stays configurable, the keying-off-jurisdiction rule does not.
08

The scars

The no-JS tracking pixel fired before consent

Symptom: a clean, undecided visitor still produced a request to the analytics vendor's host — visible in the network panel before any banner interaction. Root cause: the analytics script was correctly gated on consent, but the vendor's no-JS fallback — a tracking pixel inside a <noscript> — rendered unconditionally in the served HTML and fired the moment the page loaded, regardless of the consent state the script respected. Fix: gate the no-JS pixel on the same consent signal as the script. Prior-blocking is not "the script waits"; it is "no non-essential request occurs," and the pixel is a request.

A persistent visitor identifier was minted before any decision

Symptom: a first-time visitor who had touched nothing already had a stable tracking id in browser storage. Root cause: the identifier was created at composable/page init — eagerly, before the consent state was even read — so the "create if absent" ran on first paint. Fix: create the identifier only after an affirmative analytics decision. An identifier written before consent is the same violation as a tracker firing before consent.

The category gate failed open on an unknown category

Symptom: a category not present in the resolved model was treated as non-blocking, so its scripts were allowed to run. Root cause: the blocking check defaulted to "allow" when it could not find the category — the permissive default. Fix: default the blocking check to blocking on any category it cannot resolve. Under principle 10, the unknown must resolve to more blocking, not less.

Functional storage was wrongly held behind consent

Symptom: theme and layout preferences stopped working for visitors who hadn't yet decided — the site felt broken before the banner was answered. Root cause: the banner treated all non-essential storage as consent-gated, sweeping in functional keys that carry no personal data and legally need no consent. Fix: separate functional (always-on, no consent) from analytics and marketing (gated). Over-blocking is a real failure, not a safe default — it degrades the product for a requirement the law does not impose.

A cosmetic edit re-prompted every live visitor

Symptom: a trivial wording change to a policy silently invalidated every stored cookie consent, and every visitor saw the banner again. Root cause: the banner revision was coupled to the document's revision date, and any edit bumped the date — so a typo fix counted as a new legal revision. Fix: bump the revision only when the meaning changes (the category set, a purpose, a recipient) — not for cosmetic edits. The revision is a legal event; spend it deliberately.

The consent model was keyed off the selected market

Symptom: a visitor in a strict-consent jurisdiction who switched the site to a looser-jurisdiction market's content received the looser consent model — and non-essential trackers fired without the prior consent their real location required. One dropdown click, silent breach. Root cause: the consent regime was resolved from the market the visitor picked (a display preference) instead of their jurisdiction (a legal fact). Fix: key the regime on jurisdiction, never on the selected market; resolve conflicts strictest-wins. The reverse error — showing a stricter banner than needed — is harmless; this one is not.

A malformed law-layer entry resolved to a weaker model

Symptom: an invalid entry in the jurisdiction law layer was accepted and silently resolved to a less-blocking consent model. Root cause: the resolver tolerated a schema-invalid law-layer record instead of rejecting it, and the tolerance degraded toward allow. Fix: enforce the law-layer schema and fail secure — a malformed entry must block the build or resolve to the strictest model, never to a weaker one.

09

Verification

Ship these gates with the capability — the invariants rot the day after launch without them:

  • Cookie-in-code vs disclosed-in-policy: every cookie/storage key the code sets must be described in the cookie policy. Fails the build on an undisclosed key.
  • External-host vs declared-recipient: every third-party host the browser is told to contact must be a declared recipient in the privacy policy; a host outside Russia raises a cross-border-transfer warning (art. 12).
  • Banner-revision ⇔ cookie-policy lockstep: the banner's current revision date and the cookie policy's revision date must move together (a category change is a policy change).
  • Feature ⇒ required-document: a feature that is enabled but whose obligatory document is missing fails the build.

The one live check no gate performs: open the published site in a fresh browser with no prior consent, watch the network panel and storage. Zero requests to any non-essential third-party host; no persistent tracking identifier written. Click reject — the state stays clean. Reload — still clean. Click accept — now, and only now, the analytics requests appear and the identifier is written. Essential and functional keys are present the whole time.

10

The trade-offs

This design is deliberately heavyweight. It buys defensibility — a documented, auditable, prior-blocking posture that survives a supervisory-authority inquiry — and it costs:

  • Several documents, not one, each with mandatory content and each a maintenance surface. The separation is legally required, not optional simplification.
  • Analytics coverage. Prior-blocking means you lose the visitors who decline or never answer. That is the price of opt-in; do not "recover" it by firing before consent.
  • Offline bureaucracy. The operator notification and (if applicable) cross-border filing are paperwork with the authority, outside any codebase, with real lead time.
  • A revision discipline. Every meaning-change re-collects consent; careless edits punish your own conversion.

When not to build the full apparatus: a purely static site with no accounts, no forms, no newsletter, no analytics, and no third-party embeds processes no personal data and needs at most a short cookie disclosure — not the consent machinery. But the moment you add an analytics tag, a contact form, comments, or accounts, you cross the line into 152-ФЗ and the full set applies. The failure mode is assuming you are on the static side of that line when one analytics snippet already put you on the other.

11

For the human

You get two things. First, a set of separate legal documents — a privacy policy, the consents your features oblige (account, distribution, newsletter), a cookie policy, and terms of use — each carrying the operator's identifying details, each citing the article it rests on, and an operator notification filed with the supervisory authority before you process anything. Second, a cookie banner that holds every non-essential tracker back until the visitor affirmatively opts in, records the choice with the revision it was made under, and lets them withdraw — while strictly-necessary and functional storage just works.

The stack it was built on, and why. The documents are human-readable prose and the banner is a client-side gate over the browser's own storage.

  • Essential (the design depends on these): the documents must be a durable, versioned, human-readable record of exactly what you told people — because that record is what a regulator or a court reads. And the prior-blocking gate must run in the client, before any non-essential script or pixel executes — because "block before it runs" cannot be done after the fact on the server alone.
  • Incidental (swap freely): that the documents happen to be Markdown, that the banner happens to be one particular component framework, that the store happens to be one particular table. An agent on another stack should replace all three without a second thought — the recipe holds.

The stack's cost: prose documents are easy to read and diff but drift from the code unless a gate ties them together (hence the cookie-in-code and host-vs-recipient checks); a client-side gate is the only place prior-blocking can live, but it means the correctness of your compliance rides on client code that must fail secure.

Optional — you can generate all of this from one source. Instead of hand-writing and hand-syncing the documents, you can drive the entire set from a single machine-readable registry: a data map (each data point → purpose → legal basis → retention), the cookie catalog, the recipient list, and the operator requisites. A generator renders the documents and the banner's category list from that registry, and the verification gates compare code against the registry — so a newly collected field or a new cookie cannot ship without updating the disclosure, because the build fails otherwise. A working example of this exists. It is real machinery with real cost, and it is not required to be compliant — the hand-authored path in this recipe is complete on its own. The registry is what turns "remember to update the policy" from a discipline into a gate; adopt it when the manual sync becomes the thing you keep getting wrong.

the handover protocolappended on export

These two sections are the same in every recipe — `sporo export` appends them so the reader knows how to bring the capability into a repository the author never saw, and what to send back.

Adopt it here

This section is addressed to the agent that is reading this recipe inside its own repository. The recipe above knows the capability; only you know the ground. Do this before the first build step.

Probe, do not assume. For each thing the ground section says must be standing, go and look for it here. Do not reason about what a project like this "usually has" — read the repository. Then say, in one line each, what you found and what you did not.

Agree the outcome before you build. The recipe says what the capability can produce; only your operator knows what it must produce here. Ask them, before the first build step: which of the outputs are needed in this project, in what form, and which outside consumers will read them. Do not assume every output transfers — a capability that emits three artifacts at home may owe this project only one. And treat the bindings as law: a shape marked **Binding: adapt** is yours to rename into this project's language; a shape marked **Binding: exact** is not yours at all — something on the other side parses it, so confirm with your operator that the consumer applies here, then copy it byte-for-byte — and check your output against it mechanically, in this project's own CI, from this document alone (the recipe tool's conform check does exactly that; if you cannot run it, validate against the shape by hand and say so in your report).

Map the roles onto this repository's homes. The recipe names roles — the facts file, the collector, the optional target the project implements — because a path is the one thing that cannot travel. You own the other half: decide where each role lives here, in this project's own conventions, and write the mapping down where the next agent will find it (the project's configuration, not your context window). A mapping that exists only in the session that made it is a mapping that will be re-invented, differently, next week.

What is missing, propose — do not silently substitute. When a precondition is genuinely absent, you have exactly three honest moves, in this order:

  1. Create it, at the smallest size that works. The recipe's ground section says what shape it needs and why it is load-bearing; carry that reason across in the language of this project, not as an appeal to someone else's conventions. A precondition worth a ladder in the recipe is usually worth twenty lines here.
  2. Degrade, and label it in the output. If the recipe states a fallback, take it — and carry its label all the way into what the capability produces. A degraded source that is not labelled where the reader can see it is indistinguishable from the real thing, which is the failure the recipe was written to prevent.
  3. Stop and ask. If neither is available, say so before you build. A capability standing on a precondition that is not there produces confident output over rot.

There is no fourth move, and in particular: do not quietly reach for a weaker source that happens to be lying around. That substitution is how nearly every scar in every recipe began.

Do not import the author's structure. What transfers is the principles, the contracts, and the scars. The directory layout, the file names, the command names and the tool are the author's house, and copying them buys you nothing and costs you a shape that does not fit.

State the plan before you build. The mapping, what you will create, what you will degrade and how it will be labelled. That statement is the reader's only chance to correct you while it is still cheap.

Report back

A recipe improves in exactly one way: someone builds it somewhere else and says what happened. Every scar in the document above is a debt paid by an earlier reader. When you finish, send this back to whoever handed you the recipe. Open with the recipe's name and the version: from its frontmatter — a report that cannot say which text it built floats free of every fix it triggers.

  • Stack — what you actually built it on, and which of the author's essential choices you kept, replaced, or could not honour.
  • Degraded — every place you took a weaker path than the recipe asked for, what forced it, and the label it now carries in the output.
  • New scars — the payload, and the reason this section exists. Anything that broke, that the recipe did not warn you about, as symptom → root cause → fix. A scar you hit is a scar the next reader does not have to.
  • Wrong — anything the recipe asserts that turned out not to be true on your ground. Being contradicted by a build is the only way a recipe finds out it is stale.
  • Arithmetic — the recipe names one live check that no gate can perform. Say whether you ran it, and what it said.
  • Missing — the thing you had to invent because the recipe described it in prose and did not hand you the shape. If you had to design a contract, send it: it belongs above.

Keep it short. Five honest lines beat a report nobody writes.