Implements Gitea issue #59: per-region synthesized 4-value resource profiles and a new industry LLM lever.
What's new
public/region-resources.json — 1,800 entries, each a { oil, iron, food, industry } 0–1 profile, built by the new deterministic, idempotent scripts/build-region-resources.mjs (FNV-1a hash × area factor).
server/engine.ts — validateIndustryOps, applyIndustryOps (per-region SET; first-declared-type wins; invalid keys dropped), applyIndustryEffects (sole-owner-only economy boost: INDUSTRY_ECONOMY_BONUS = 1, +1 extra when region's industry value ≥ 0.5; 0/2+ owner regions kept but no boost; re-derive after), system + user prompt contract lines, resourceOf opt on runTurn, nationResources in user payload.
server/db.ts — legacy-row industry default migration.
server/stub.ts — stub returns industry: { projects: [] }; resources / industry added to StubPayload (never read).
server/index.ts — resourceOf wired to the jump route.
All existing test fixtures updated; new describe blocks in engine.test.ts, protocol.test.ts, regions.test.ts, db.test.ts.
## Summary
Implements Gitea issue #59: per-region synthesized 4-value resource profiles and a new `industry` LLM lever.
### What's new
- **`public/region-resources.json`** — 1,800 entries, each a `{ oil, iron, food, industry }` 0–1 profile, built by the new deterministic, idempotent `scripts/build-region-resources.mjs` (FNV-1a hash × area factor).
- **`server/regions.ts`** — `hasResources()` / `resourceOf()` optional-manifest loader (same pattern as centroids/geo).
- **`shared/types.ts`** — `RegionResources`, `IndustryType` / `INDUSTRY_TYPES` / `IndustryProject`, `GameState.industry`.
- **`shared/protocol.ts`** — `IndustryOps`, `TurnResult.industry`, `deriveNationResources` (area×population-weighted average, 2dp).
- **`server/engine.ts`** — `validateIndustryOps`, `applyIndustryOps` (per-region SET; first-declared-type wins; invalid keys dropped), `applyIndustryEffects` (sole-owner-only economy boost: `INDUSTRY_ECONOMY_BONUS = 1`, +1 extra when region's `industry` value ≥ 0.5; 0/2+ owner regions kept but no boost; re-derive after), system + user prompt contract lines, `resourceOf` opt on `runTurn`, `nationResources` in user payload.
- **`server/db.ts`** — legacy-row `industry` default migration.
- **`server/stub.ts`** — stub returns `industry: { projects: [] }`; `resources` / `industry` added to `StubPayload` (never read).
- **`server/index.ts`** — `resourceOf` wired to the jump route.
- All existing test fixtures updated; new describe blocks in `engine.test.ts`, `protocol.test.ts`, `regions.test.ts`, `db.test.ts`.
- `CHANGELOG.md` v0.36.0, `AGENTS.md` docs, `package.json` 0.36.0.
### Design notes
- No engine randomness; no `@turf` / `@types/geojson` from `.ts`; `shared/` remains runtime-dep-free.
- The stub never declares a project, so e2e (CI, `LLM_STUB=1`) is unaffected.
- A project on a region with 0 or 2+ owners is **kept** in `state.industry` but yields no boost.
- Per-region SET: a region that already hosts a project is untouched (re-declare is a no-op, type is never replaced).
Fixes #59
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Implements Gitea issue #59: per-region synthesized 4-value resource profiles and a new
industryLLM lever.What's new
public/region-resources.json— 1,800 entries, each a{ oil, iron, food, industry }0–1 profile, built by the new deterministic, idempotentscripts/build-region-resources.mjs(FNV-1a hash × area factor).server/regions.ts—hasResources()/resourceOf()optional-manifest loader (same pattern as centroids/geo).shared/types.ts—RegionResources,IndustryType/INDUSTRY_TYPES/IndustryProject,GameState.industry.shared/protocol.ts—IndustryOps,TurnResult.industry,deriveNationResources(area×population-weighted average, 2dp).server/engine.ts—validateIndustryOps,applyIndustryOps(per-region SET; first-declared-type wins; invalid keys dropped),applyIndustryEffects(sole-owner-only economy boost:INDUSTRY_ECONOMY_BONUS = 1, +1 extra when region'sindustryvalue ≥ 0.5; 0/2+ owner regions kept but no boost; re-derive after), system + user prompt contract lines,resourceOfopt onrunTurn,nationResourcesin user payload.server/db.ts— legacy-rowindustrydefault migration.server/stub.ts— stub returnsindustry: { projects: [] };resources/industryadded toStubPayload(never read).server/index.ts—resourceOfwired to the jump route.engine.test.ts,protocol.test.ts,regions.test.ts,db.test.ts.CHANGELOG.mdv0.36.0,AGENTS.mddocs,package.json0.36.0.Design notes
@turf/@types/geojsonfrom.ts;shared/remains runtime-dep-free.LLM_STUB=1) is unaffected.state.industrybut yields no boost.Fixes #59
- scripts/build-region-resources.mjs: deterministic, idempotent builder → public/region-resources.json (1,800 entries; FNV-1a hash × area factor, 0–1, 2dp) - server/regions.ts: hasResources()/resourceOf() optional-manifest loader - shared/types.ts: RegionResources, IndustryType/INDUSTRY_TYPES, IndustryProject, GameState.industry - shared/protocol.ts: IndustryOps, TurnResult.industry, deriveNationResources (area×population-weighted average, 2dp) - server/engine.ts: isIndustryType, validateIndustryOps, applyIndustryOps (per-region SET, first-declared-type wins), applyIndustryEffects (INDUSTRY_ECONOMY_BONUS=1, +1 when region industry≥0.5; sole-owner only; 0/2+ owner regions kept but no boost; re-derive after), system+user prompt contract lines, runTurn resourceOf opt, nationResources in user payload - server/db.ts: legacy-row industry default migration - server/stub.ts: industry:{projects:[]} in stub result; resources/industry in StubPayload (never read) - server/index.ts: resourceOf wired to jump route - All test fixtures updated; new describe blocks in engine.test.ts, protocol.test.ts, regions.test.ts, db.test.ts - CHANGELOG.md v0.36.0, AGENTS.md docs, package.json 0.36.0 Fixes #59