Skip to content

Leak harvesting — mechanic spec

Leak harvesting

This page is the canonical math layer for the harvest action in WalkRPG. It freezes the equations, region-pool caps, leak-tier consequences, and anti-exploit fingerprint that phase 12d (crafting UI) and the post-beta backend reconciliation worker must implement to the byte. If a downstream phase has to invent a number or a rule, this page failed.

It composes with — but does not redefine — the math layer in combat/formulas.md. New stats land in this page’s §4 as additions to that page’s §3 dictionary; energy cost shape matches combat/formulas.md §6 (per-action default = 1, parameterised). Where this page and combat/formulas.md overlap, combat/formulas.md wins for combat-side resolution and this page wins for harvest-side resolution.

1. Scope

This page covers the harvest action: a player-initiated, region-bound, step-paid event that takes a Material from the world into the Walker’s pack. Harvest is one of two Recipe.kind values (harvest vs craft); the other — combining inputs into an output — is covered separately.

In scope:

  • The five canonical harvest methods (scavenge | extract | distill | field-find | trade-only) — what each looks like in play, what it costs, what it returns.
  • A new stat (crafting) that scales harvest success rate, anchored in the §3 dictionary of combat/formulas.md.
  • Region-pool semantics — finite per-walker-per-region-per-material caps with a regen window, sized so harvest is a flavour activity, not a primary economy loop.
  • Leak-tier consequences — what changes at leakTier ≥ 2, including the keystone gate at leakTier = 3.
  • Backfire semantics — what a failed harvest costs, framed against the D-010 non-punitive philosophy.
  • Anti-exploit fingerprint — the minimum data the reconciliation worker needs (per D-007 / D-009).
  • A worked example end-to-end on material.silver-veined-frost in the Frostlands.

Out of scope, by canon:

  • Combat rebalance — combat/formulas.md is the source of truth for combat math; this page never writes to a combat stat during a turn.
  • Recipe kind: "craft" resolution — combine math (input consumption, output rolling, partial-output policy) is a separate phase. This page only covers the harvest side.
  • Crafting UI — phase 12d.
  • Backend implementation — backend-engineer codes against this spec post-beta.

The harvest action anchors in Pillar 1 (Steps = Energy) — every harvest is paid in steps, never in pure time — and in Pillar 3 (sick / leaked magic) — the leak intensity carried by the material is the thing that makes harvesting meaningful rather than ambient. A walker who never engages with the leak still walks; a walker who harvests is choosing to interact with the sick half of the world.

2. The harvest surface

A harvest action in WalkRPG is a Recipe.kind: "harvest" invocation, gated by region, paid in steps, resolved with a seeded roll, producing zero or more units of a Material.

The smallest model that is testable:

  1. Walker enters a region that hosts the target material (Material.originRegionId). Crossing the regional boundary does not by itself trigger anything — harvest is a player-initiated action, not an ambient pull.
  2. Walker invokes the recipe (UI affordance). The recipe declares a harvestMethod (read from the material) and the consumer (this layer) reads:
    • the Walker’s current crafting stat (§4),
    • the Walker’s step delta since the recipe’s last invocation in this region (§5),
    • the region pool state for (walker, region, material) (§6).
  3. A seed is drawn from the harvest trigger (deterministic; see §11). The system rolls success or backfire (§5). On success, it rolls yield (§5).
  4. Inventory updates; the region pool counter increments; the harvest event ships to the server (or queues, per D-009 heavy-offline play).

The intent is to keep harvest legible enough to live inside a walking-RPG (no minigame, no minute-by-minute pressure) and rich enough to make passive-tree allocation meaningful (the crafting stat earns nodes in a later phase, and leakTier-gated keystones unlock the corrupted top of the catalogue).

Cech porter na pierwszej trasie mrozłowskiej, wyjmuje skrobak z bocznej kieszeni: „Szron się nie podrywa sam. Krok zarobiony, krok zapisany, krok zeskrobany. Trzecia kropka się liczy."

3. The five harvest methods

Each HarvestMethod value maps to a player action with its own shape. Method does not change the math directly — yield and success are governed by §5 — but it shapes the step-cost band and the UI affordance in 12d.

MethodWhat the player doesStep cost band (per attempt)Time floorYield range (on success)
scavengePick a material off mutated terrain or a defeated leak-creature. Triggered on path-step inside the region; the affordance appears at a point of interest.150-3005 min1-3 units
extractScrape from a substrate with a tool (Cech-issued scraping knife for Frostlands frost; folded map-margin paper for Plenny ink-creep; a folded paper square for vigil-soot).100-2003 min1-2 units
distillRefine vapour or fluid in a small vessel along the route — Mglica twelve-drop measures, in the canonical case.200-4008 min1-2 units
field-findPluck along a walk — the universal walker-find. No tool, no setup, no leak handling. Pillar 1 in its purest form.50-1502 min1-4 units
trade-onlyNot self-harvestable. Sourced from Free Merchantry or faction quartermasters. No harvest roll — this page treats trade-only as a no-op for harvest and defers to a separate trade layer. The material’s harvestMethod: "trade-only" is a content-layer marker, not a harvest-action invocation.

Step-cost bands are deliberately wide so 12d can pick a single value per recipe (a recipe authored against material.silver-veined-frost might cost exactly 200 steps; a recipe against material.thaw-mint might cost exactly 80). The band is the design envelope; the recipe is the instance.

The time floor is a minimum real-time elapsed, not a step rate — a walker pacing slowly may walk 200 steps in 10 minutes, but they cannot harvest the same material twice inside the time floor regardless of step count. This protects against treadmill-style high-cadence step injection that satisfies step count without satisfying “walking” in the intended sense.

4. The crafting stat

Introduced here as an addition to the stat dictionary in combat/formulas.md §3. The combat layer never reads it; the harvest layer reads it on every invocation.

StatTypeDefault (level 1)RangeNotes
craftingint00..∞Scales harvest success rate (§5). Scales nothing else at phase 12. Future phases may extend to craft-side recipes (yield variance, time floor reduction); those extensions land as additive rules on top of this default.

Convention matches the combat dictionary: snake_case, integer where possible, default of 0 so a walker who never invests in the tree still functions (harvest is gated only by region and method, not by stat investment).

The stat lands on the passive tree in a later phase as small-node investments inside the as-yet-unauthored crafting clusters. Until those nodes exist, every walker harvests at crafting = 0 — which is the calibration point §5 uses to set baselines.

5. Success, yield, backfire

The harvest invocation rolls two dice in order: an outcome die (success vs backfire) and, on success, a yield die. Both draw from a seeded PRNG stream (§11) — the same xorshift/PCG family already frozen in combat/formulas.md §11 for combat. Determinism is non-negotiable: D-007 anti-cheat reconciles harvest server-side, and reconciliation requires replay.

5.1 Success roll

success_rate = clamp(base[method] + crafting * 0.02, 0.05, 0.95)
landed = (roll_s < success_rate)

Where:

  • base[method] is the method’s base success rate (table below). It already folds in the assumption that the walker has the right tool / setup for the method; missing tooling is a 12d UI concern (the affordance is hidden), not a math layer concern.
  • 0.02 is the per-point scaling. A walker at crafting = 25 adds +0.50 to success rate — meaningful but not trivializing. Larger investments saturate against the 0.95 ceiling, matching the saturation behaviour of hit_chance in combat.
  • clamp(x, 0.05, 0.95) mirrors the combat hit-chance clamp: even a crafting = 0 walker lands 5% of harvests on the worst method; even a perfectly-statted walker fails 5% of the time.
  • roll_s ∈ [0, 1) is the success roll (one per attempt).
Methodbase[method]Notes
scavenge0.55Mid-tier — depends on what the path coughs up.
extract0.70Highest — tool-and-substrate is the most reliable shape.
distill0.50Lowest of the active methods — vapour drifts.
field-find0.85Near-certain when the affordance fires; the world has decided the walker found something.
trade-onlyn/aNo success roll; the trade either resolves at the merchant or it does not, and trade-layer rules govern that.

5.2 Yield roll (on success)

yield = round(yield_min[method] + (yield_max[method] - yield_min[method]) * roll_y)

Where:

  • yield_min[method] and yield_max[method] are the band ends from §3.
  • roll_y ∈ [0, 1) is the yield roll (one per landed harvest).
  • round is half-up rounding, same convention as combat/formulas.md §4.

5.3 Backfire (on a missed success roll)

A missed success roll triggers backfire, which is not a hard penalty — D-010 is canonical on the non-punitive frame. The Walker receives:

  • No material yielded.
  • Energy refund denied — the action consumed its energy cost (per §7) and the energy is not returned.
  • A lore note added to the walk log, themed to the method and the region. Mechanics-only: the lore note is a 1-step content slot for narrative-designer to populate in a later phase.
  • No HP loss. No inventory loss. No streak loss. No reputation loss. All four are out of scope for backfire by the D-010 non-punitive frame and the D-007 streak invariant (streak decays only on missed attested walks, not on action outcomes).

The leak-touched case is harsher in flavour but identical in mechanics — see §6.

5.4 Leak-tier modifier to the success roll

leakTier adjusts success_rate after the crafting scaling but before the clamp:

success_rate = clamp(base[method] + crafting * 0.02 + leak_mod[leakTier], 0.05, 0.95)
leakTierleak_modAuthoring intent
0 (clean)0.00No adjustment. The mundane stock is the calibration point.
1 (touched)-0.05A small drag — leak resists the harvester slightly.
2 (infused)-0.15A real drag — without crafting investment, the walker fails often.
3 (corrupted)n/aGated. See §7. Walkers without the keystone cannot invoke; walkers with the keystone harvest at leak_mod = -0.20 (still a drag, but earnable).

5.5 Worked sub-example (success roll only)

A walker with crafting = 5 invoking an extract harvest against material.silver-veined-frost (leakTier = 2):

success_rate = clamp(0.70 + 5 * 0.02 + (-0.15), 0.05, 0.95)
= clamp(0.70 + 0.10 - 0.15, 0.05, 0.95)
= clamp(0.65, 0.05, 0.95)
= 0.65

The same walker against material.thaw-mint (leakTier = 0, field-find):

success_rate = clamp(0.85 + 5 * 0.02 + 0.00, 0.05, 0.95)
= clamp(0.95, 0.05, 0.95)
= 0.95

The 0.95 cap fires — even at crafting = 0, this walker would already be at 0.85; investment past the ceiling is recoverable elsewhere (faster yield bands, lower step cost — future phases).

6. Region pools and regeneration

A region has a finite, per-walker, per-material weekly cap. The cap exists because D-007 anti-cheat philosophy is restrictive (mock-step rejection, GPS sanity, server-side reconciliation) and D-009 allows heavy offline play with a 7-day re-attestation cap — together those constrain harvest to be a flavour activity, not a primary economy loop. A walker who could mine infinite leak by re-entering a region defeats both the anti-cheat posture and the lore frame (the leak is supposed to be a limited, dangerous phenomenon, not an unlimited resource).

6.1 Cap shape

leakTierPer-walker per-region per-material weekly capRegen window
0 (clean)100 unitsDaily — replenishes 100 / 7 ≈ 15 per IRL day, rounded up.
1 (touched)30 unitsDaily — replenishes 30 / 7 ≈ 5 per IRL day, rounded up.
2 (infused)8 unitsWeekly — replenishes 8 units at the start of each IRL week (Monday 00:00 UTC).
3 (corrupted)2 unitsWeekly — replenishes 2 units at the start of each IRL week (Monday 00:00 UTC).

The cap is per (walker, region, material) — a walker can harvest 100 units of material.thaw-mint from the Frostlands and 100 units of a hypothetical mundane Plenny material in the same week. Two regions, two pools.

6.2 Regen semantics

  • Clean and touched materials regenerate daily in equal increments. A walker who harvests their full daily increment may not harvest more of that material from that region until the next IRL day boundary (Monday 00:00 UTC, then Tuesday 00:00 UTC, …).
  • Infused and corrupted materials regenerate weekly in one lump at Monday 00:00 UTC. A walker who exhausts the weekly cap on Monday morning waits until the next Monday for any further harvest of that material.
  • The walker’s local timezone does not affect regen — D-009 GDPR and EU residency align with UTC for all server-side accounting. Mobile UI may render in the walker’s TZ; the math is UTC.

6.3 What the cap does not do

  • It does not block trade. A walker who exhausts their material.silver-veined-frost cap can still buy frost from the Wolnokupiectwo / Free Merchantry (if a trade affordance exists in the region) — trade-acquired materials do not count against the harvest cap.
  • It does not block crafting. The cap only governs the harvest action; consuming materials in a kind: "craft" recipe is unrelated.
  • It does not block movement, quests, encounters, or combat. The walker walks past their cap freely; the affordance simply does not fire.

7. Leak-tier consequences

Per §5.4, leak-tier is a drag on the success roll. Beyond that, two further consequences kick in at leakTier ≥ 2:

7.1 Leak-bloom (leakTier 2+)

On every successful harvest of a leakTier ≥ 2 material, roll a leak-bloom die:

bloom_chance = 0.05 * leakTier
(so: leakTier=2 → 0.10; leakTier=3 → 0.15)
bloomed = (roll_b < bloom_chance)

A leak-bloom triggers a combat encounter against a small leak-thing (canonical first instance: the slow-vent frost-thing from combat/formulas.md §10). The encounter resolves through the existing combat layer with no special rules — see combat/formulas.md. The harvest itself is kept (the walker has already pocketed the material before the bloom triggers); the encounter happens immediately afterward, as a beat-paid consequence of touching the leak.

The hook ties the harvest mechanic into the combat layer (per 11b-2) without expanding the combat dictionary. The leak-thing’s stats and seed derivation are 12c-passthrough to combat/formulas.md.

7.2 Keystone gate (leakTier 3)

leakTier = 3 materials are not harvestable without a passive-tree keystone. The recipe’s requiredKeystoneId declares the gate; phase 12c reserves the slot but does not author the keystone (no leakTier = 3 material has been authored at the time of this writing, and the corresponding keystone is a later-phase mechanics-designer task).

The convention for the future keystone:

  • It sits on a cluster gated by the regional leak-flavour (e.g. Frostlands leak gets a frost-flavoured keystone; Mglica gets a dream-flavoured one).
  • It applies leak_mod = -0.20 for leakTier = 3 harvests (per §5.4) and removes the gate, not the drag — the walker still rolls under a drag, but they can now roll at all.
  • It adds the crafting stat in some increment (typical: crafting +5), so the keystone is also an investment in success rate, not only a gate.

Until that keystone exists, leakTier = 3 content cannot ship — the gate has no key. This is intentional: 12c freezes the shape of the gate without authoring the instances. Mechanics-designer authors the first leakTier = 3 material and the matching keystone together in a future phase.

The only existing keystone at the time of this writing is keystone.unshaken-step (from 11a-1), which is unrelated and does not satisfy the harvest gate.

8. Energy cost per harvest

The harvest action consumes energy. The shape matches combat/formulas.md §6 — per-action default = 1, parameterised per recipe — so the simulator and the backend never branch on “is this a combat or a harvest action”.

  • Per-action cost: energy_cost_per_action, default 1, configurable per recipe in a later phase. A heavy distill might cost 3; a casual field-find might cost 1. Recipe-level override slots are reserved for 12d.
  • Trigger cost: 0. Invoking the harvest affordance does not by itself consume energy; the action does.
  • Energy floor: if the walker has energy < energy_cost_per_action at invocation, the affordance is greyed out in 12d (UI concern) and rejected at the math layer with no roll. The walker keeps their energy and the region pool is untouched.
  • Energy refund on backfire: none (per §5.3). Energy is spent on the act of harvesting, not on the success of harvesting.
  • Energy regen during harvest: none. Same rule as combat — energy returns only through walking (Pillar 1).

The number of harvests a walker can complete in one session is therefore bounded by floor(walker.energy / energy_cost_per_action), matching the combat upper bound. Mixed sessions (combat + harvest) share the same energy pool.

9. Anti-exploit fingerprint

D-007 commits to a server-side reconciliation worker (post-beta — see roadmap). Harvest events must ship enough provenance for the worker to validate that the harvest actually occurred against attested, non-mock steps. The minimum fingerprint:

FieldTypeSourceWhy the worker needs it
walkerIdUUIDsessionThe (walker, region, material) cap-row key.
regionIdRegionId branded stringcontentMatch the recipe’s originRegionId.
materialIdMaterialId branded stringcontentThe output material; cap-row partition.
recipeIdRecipeId branded stringcontentThe exact recipe invoked (in case of multiple recipes per material in a future phase).
methodAtInvocationHarvestMethod enumcontentReject mismatches against the recipe’s declared method (anti-tamper).
stepDeltaintclient-attested via HealthKit / Health ConnectPer D-007: must match server-reconciled attestation.
stepDeltaWindow{ fromMs, toMs }clientThe step window the delta is computed over. The worker re-runs the attestation against the same window.
seeduint64client (derived from invocation context)The PRNG seed used for roll_s, roll_b (if applicable), roll_y. The worker replays the rolls.
rolls{ roll_s: float, roll_y?: float, roll_b?: float }clientThe client’s recorded roll values. The worker checks against its own replay.
outcome"success" | "backfire"clientThe client’s reported outcome. The worker checks against its replay.
yieldQtyintclient (on success)Quantity yielded. The worker checks against its replay.
clientTsMsint (ms epoch)clientWhen the event was authored client-side.

The worker rejects:

  • Events whose stepDelta does not match the attested step provenance for stepDeltaWindow.
  • Events whose replayed rolls do not reproduce the client’s outcome or yieldQty.
  • Events that exceed the §6 cap (walker has already harvested the per-week cap; new event is rejected).
  • Events with methodAtInvocation not matching the recipe’s declared method.
  • Events whose clientTsMs is implausibly in the future or further than the D-009 7-day offline cap in the past.

Per D-009, harvest events authored during heavy-offline play are provisional — the inventory increment shows in the client immediately, but the server may roll it back at next sync if the fingerprint fails reconciliation. The 12d UI must surface this state (provisional vs confirmed); 12c freezes the data shape.

10. Worked example — extracting material.silver-veined-frost in the Frostlands

A walker reaches a slow-vent encounter site in the Frostlands. The combat encounter from combat/formulas.md §10 has just resolved (walker won, frost-thing dissolved into residue). The harvest affordance fires.

Walker snapshot:

FieldValueSource
walkerIdwalker.a4f3...session
crafting5from passive tree (hypothetical investment)
energy10post-combat, from combat/formulas.md §10 outcome
energy_cost_per_action1base
stepDelta200steps walked between the combat exit and the harvest invocation
stepDeltaWindow{ fromMs: 1716120000000, toMs: 1716121200000 }20-minute window

Recipe / material snapshot:

FieldValueSource
recipeIdrecipe.harvest-silver-vein-frost (hypothetical 12c recipe)content (future)
materialIdmaterial.silver-veined-frostcontent
regionIdregion.frostlandscontent
methodAtInvocationextractcontent
leakTier2content
step_cost200recipe authoring (mid-band per §3)
time_floor3 minper §3

Pool state (pre-harvest):

FieldValue
Weekly cap (leakTier 2)8 units
Harvested this week0 units
Remaining8 units

Seed: 0x9F2A (hypothetical; actual derivation per the harvest trigger).

Roll stream (xorshift32 output for seed 0x9F2A; the harvester draws roll_s, then if landed roll_y, then if leakTier ≥ 2 roll_b):

DrawValueUsed for
10.4123roll_s
20.7891roll_y
30.0824roll_b

Step 1 — success roll (§5.4):

success_rate = clamp(0.70 + 5 * 0.02 + (-0.15), 0.05, 0.95)
= clamp(0.65, 0.05, 0.95)
= 0.65
roll_s (0.4123) < 0.65 → success

Step 2 — yield roll (§5.2):

yield_min = 1, yield_max = 2 (extract band, §3)
yield = round(1 + (2 - 1) * 0.7891)
= round(1.7891)
= 2 units

Step 3 — leak-bloom roll (§7.1, leakTier 2):

bloom_chance = 0.05 * 2 = 0.10
roll_b (0.0824) < 0.10 → bloom!

A leak-bloom fires. The 2 units of material.silver-veined-frost are pocketed before the bloom triggers — the walker keeps the yield. A new combat encounter spins up against a small leak-thing (stats per the combat/formulas.md §10 frost-thing or a 12c-balance variant; downstream of this spec).

Step 4 — energy and pool updates:

FieldBeforeAfter
Walker energy109 (1 spent per §8)
Pool: weekly cap remaining86 (2 yielded)

Anti-exploit fingerprint shipped:

{
walkerId: "walker.a4f3...",
regionId: "region.frostlands",
materialId: "material.silver-veined-frost",
recipeId: "recipe.harvest-silver-vein-frost",
methodAtInvocation: "extract",
stepDelta: 200,
stepDeltaWindow: { fromMs: 1716120000000, toMs: 1716121200000 },
seed: "0x9F2A",
rolls: { roll_s: 0.4123, roll_y: 0.7891, roll_b: 0.0824 },
outcome: "success",
yieldQty: 2,
clientTsMs: 1716121205000
}

Outcome summary: walker pockets 2 silver-veined frost, energy 10 → 9, weekly pool 8 → 6, leak-bloom triggers a follow-on combat encounter. Two rolls consumed for the harvest, one for the bloom check; xorshift32 against seed 0x9F2A reproduces this byte-for-byte on replay.

Replay guarantee. Given seed 0x9F2A, the snapshot tables above, and a crafting = 5 walker invoking recipe.harvest-silver-vein-frost at the stated step delta, any future invocation of the simulator must reproduce (outcome=success, yieldQty=2, bloom=true). Same shape as the §10 combat replay guarantee in combat/formulas.md; same reconciliation worker validates both.

11. Determinism and PRNG

The harvest simulator takes a 64-bit unsigned integer seed as input and draws floats from a deterministic PRNG. The PRNG family is the same one frozen in combat/formulas.md §11 (chosen at 11b-2 ship); harvest does not introduce a second PRNG.

Draw order per harvest invocation:

  1. roll_s — always drawn.
  2. roll_y — drawn only on landed success.
  3. roll_b — drawn only on landed success with leakTier ≥ 2.

If a harvest backfires (failed roll_s), only roll_s is consumed. The next invocation against the same seed resumes from the next slot — the seed-stream is per-harvest, not per-roll, so this does not cause cross-invocation leakage.

This matches the combat-side convention: variance and crit dice are skipped on a miss, and only the hit roll is consumed.

12. Open conventions for downstream phases

  • 12d (crafting UI) surfaces, at minimum: the recipe’s step_cost (a number, in the §3 band), the time_floor (a duration), the live success_rate preview (after crafting and leakTier are folded in), the pool state for (walker, region, material) with regen-window indicator, the energy cost (default 1, may be overridden per recipe), and a provisional-vs-confirmed badge for offline-authored events (per §9). For leakTier = 3 materials, the affordance is hidden when the gating keystone is unallocated; surfaced (but locked) when known but unallocated.
  • 11d-2 (faction-rep endpoint) must accept source: "harvest" rep-delta events with sourceRef: <harvest_event_id>. The faction-rep schema already declares this shape (ReputationDeltaSourceSchema includes "harvest"); the endpoint validates and applies. 12c does not author the delta values — that’s a per-recipe content question for a future phase — but the wiring is on the endpoint side: harvest = rep-delta source.
  • Post-beta backend reconciliation worker implements §9’s fingerprint validation against attested step provenance, server-side replay of the seeded rolls, and per-week cap enforcement (§6). The worker is provisional until D-007’s reconciliation phase ships; in the local-tunnel / VPS phases, harvest events are accepted on trust and stored verbatim for later batch reconciliation.
  • Future mechanics-designer phases author the first leakTier = 3 material paired with the gating keystone (§7.2). Neither ships without the other. The keystone joins the data/src/content/keystones/ catalogue and the material joins data/src/content/materials/; recipe ties them together via requiredKeystoneId.
  • narrative-designer (later phase) populates the §5.3 lore-note pool — short PL flavour strings, themed per region and per method, surfaced on backfire. 12c reserves the slot; narrative-designer authors content.
  • qa-engineer (later phase) adds harvest replay tests to the data simulator suite (data/src/sim/), parallel to the combat-replay band. The §10 worked example here is the first canonical regression test.

Status

Draft. The success / yield equations in §5, the region-pool caps in §6, the leak-bloom rate in §7.1, the keystone-gate convention in §7.2, and the backfire semantics in §5.3 are mechanics-designer autonomous decisions (B-level), logged at ops/decisions/2026-05-19-leak-harvest-mechanic.md. Phase 12d freezes the UI surface against this page; the post-beta reconciliation worker freezes the anti-exploit fingerprint (§9) against this page. Any discrepancy is a bug in the consumer, not in this page.