Skip to content

feat(server): add bundled Fruma territory resources#48

Merged
OneNoted merged 3 commits into
mainfrom
dev
May 10, 2026
Merged

feat(server): add bundled Fruma territory resources#48
OneNoted merged 3 commits into
mainfrom
dev

Conversation

@OneNoted
Copy link
Copy Markdown
Owner

@OneNoted OneNoted commented May 9, 2026

Summary

  • Add bundled Fruma territory resource metadata so new map territories render resource highlights without waiting on the supplemental feed.
  • Keep the legacy supplemental territory feed configurable through TERRITORY_EXTRA_URL, including an opt-out value for deployments that do not want to fetch it.
  • Preserve remote connection data when bundled resource data is merged.

Validation

  • cargo test -p sequoia-server extra_data_loader -- --nocapture
  • cargo check -p sequoia-server

Greptile Summary

This PR bundles Fruma territory resource metadata directly into the server so map territories render resource highlights immediately on startup, without waiting for the hourly supplemental feed. It also makes the legacy supplemental feed URL configurable via TERRITORY_EXTRA_URL with explicit opt-out support.

  • Bundled data + merge strategy: 31 Fruma territories are hardcoded with resource values; merge_extra_data applies them only when the remote feed has no resources for a territory, so live feed values always take precedence.
  • Error-path improvement: On a failed remote fetch, the code now merges bundled defaults into the existing cached state instead of discarding it, preserving any connection data that was loaded on the last successful tick.
  • TERRITORY_EXTRA_URL config: Supports none/0/false/off opt-out values; defaults to the legacy gist URL when unset.

Confidence Score: 5/5

Safe to merge; the bundled data fills gaps without displacing live feed values, and the error path now preserves cached state rather than overwriting it.

All core changes — merge semantics, error-path state preservation, and env-var opt-out — behave correctly and are backed by targeted tests. The only gap is a missing warn! when TERRITORY_EXTRA_URL contains non-unicode bytes, which is a silent no-op that could confuse operators but does not affect correctness.

No files require special attention.

Important Files Changed

Filename Overview
server/src/services/extra_data_loader.rs Adds 31 bundled Fruma territory resources and a merge strategy. Error path now preserves cached state rather than overwriting; merge_extra_data correctly guards resources behind is_empty() and connections behind non-empty source. Tests cover the key scenarios.
server/src/config.rs Renames TERREXTRA_URL to LEGACY_TERREXTRA_URL; adds territory_extra_url() with env-var opt-out logic and tests. Well-structured; minor observability gap on NotUnicode path.
server/src/state.rs Derives Default on ExtraTerrInfo to support or_default() in merge logic; updates doc comment. No correctness concerns.
.gitignore Adds .playwright-cli/ to gitignore. Unrelated to the feature; no concerns.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[run loop tick] --> B[load_extra_data]
    B --> C{territory_extra_url}
    C -- Some url --> D[fetch_extra_data\nHTTP GET + error_for_status]
    C -- None opt-out --> E[empty HashMap]
    D -- Ok --> F[remote data]
    D -- Err --> G[propagate error]
    F --> H[merge_extra_data\ntarget=remote, source=bundled]
    E --> H
    H --> I[Ok data]
    I --> J[overwrite state.extra_terr\nset dirty flag]
    G --> K[error path:\nwrite-lock existing state\nmerge bundled into cache\nset dirty flag\nwarn log]

    subgraph merge_extra_data logic
        M[for each territory in source]
        M --> N{target.resources is_empty?}
        N -- yes --> O[copy source resources]
        N -- no --> P[keep target resources]
        M --> Q{source.connections non-empty?}
        Q -- yes --> R[override connections]
        Q -- no --> S[keep target connections]
    end
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
server/src/config.rs:122
`VarError::NotUnicode` silently disables the supplemental territory feed with no log output. An operator who accidentally sets `TERRITORY_EXTRA_URL` with non-unicode bytes (e.g. via a misconfigured secrets manager) would see the feature go dark without any diagnostic message. Every other silent opt-out path below (`""`, `"0"`, etc.) is intentional and documented; this one is not.

```suggestion
        Err(VarError::NotUnicode(_)) => {
            warn!("TERRITORY_EXTRA_URL contains non-unicode bytes; supplemental feed disabled");
            None
        }
```

Reviews (2): Last reviewed commit: "fix(server): preserve territory extra op..." | Re-trigger Greptile

@OneNoted OneNoted marked this pull request as ready for review May 9, 2026 10:20
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f69129267b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/src/config.rs Outdated
Some(value)
}
})
.or_else(|| Some(LEGACY_TERREXTRA_URL.to_string()))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve explicit opt-out for TERRITORY_EXTRA_URL

The new territory_extra_url() logic cannot actually disable supplemental fetches: when TERRITORY_EXTRA_URL is set to "0", "false", "none", "off", or empty, the and_then returns None, but the trailing .or_else(|| Some(LEGACY_TERREXTRA_URL.to_string())) immediately restores the default URL. In deployments that intentionally set an opt-out value, the server will still call the legacy gist every refresh cycle, which contradicts the intended behavior and makes the opt-out ineffective.

Useful? React with 👍 / 👎.

Comment on lines +18 to +23
let data = load_extra_data(&state.http_client).await;
let count = data.len();
*state.extra_terr.write().await = data;
state.extra_data_dirty.store(true, Ordering::Release);
info!("loaded extra territory data for {count} territories");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 State reset on transient remote fetch failure

On every tick, *state.extra_terr.write().await = data unconditionally replaces the stored state with the result of load_extra_data. When the remote fetch fails, load_extra_data returns only bundled data (no connections), erasing any connection data that was successfully loaded on the previous tick. The old code left state untouched on failure; this change removes that safety net. After a single failed refresh, all territory connection data from the remote feed disappears until the next successful fetch.

Prompt To Fix With AI
This is a comment left during a code review.
Path: server/src/services/extra_data_loader.rs
Line: 18-23

Comment:
**State reset on transient remote fetch failure**

On every tick, `*state.extra_terr.write().await = data` unconditionally replaces the stored state with the result of `load_extra_data`. When the remote fetch fails, `load_extra_data` returns only bundled data (no connections), erasing any connection data that was successfully loaded on the previous tick. The old code left state untouched on failure; this change removes that safety net. After a single failed refresh, all territory connection data from the remote feed disappears until the next successful fetch.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +50 to +63
fn merge_extra_data(
target: &mut HashMap<String, ExtraTerrInfo>,
source: HashMap<String, ExtraTerrInfo>,
) {
for (name, info) in source {
let entry = target.entry(name).or_default();
if !info.resources.is_empty() {
entry.resources = info.resources;
}
if !info.connections.is_empty() {
entry.connections = info.connections;
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Bundled data permanently overrides remote resource values

merge_extra_data is called with target = remote and source = bundled, so bundled resource values unconditionally replace whatever the remote feed provides for any Fruma territory. If the supplemental feed carries more up-to-date resource counts for those territories (e.g., after a game balance patch), the bundled values will silently shadow them. The PR description frames bundled data as filling in resources "without waiting on the supplemental feed," which suggests the intent is to provide defaults — but the current merge direction makes bundled values sticky overrides instead.

Prompt To Fix With AI
This is a comment left during a code review.
Path: server/src/services/extra_data_loader.rs
Line: 50-63

Comment:
**Bundled data permanently overrides remote resource values**

`merge_extra_data` is called with `target = remote` and `source = bundled`, so bundled resource values unconditionally replace whatever the remote feed provides for any Fruma territory. If the supplemental feed carries more up-to-date resource counts for those territories (e.g., after a game balance patch), the bundled values will silently shadow them. The PR description frames bundled data as filling in resources "without waiting on the supplemental feed," which suggests the intent is to provide defaults — but the current merge direction makes bundled values sticky overrides instead.

How can I resolve this? If you propose a fix, please make it concise.

@OneNoted OneNoted merged commit c820cf2 into main May 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant