# AGENTS.md — Galactic Conquest Classic (GCC)

> Read this file before making any changes. It tells you what the project is and how to find things.

## Skills Take Precedence

The playbooks in [`Skills/`](Skills/README.md) are the source of truth for **how** to write code here. This file is background reference: it describes the game as it is, a decade of legacy included. **Where this file and a skill disagree, the skill wins.**

| Task | Skill |
|------|-------|
| Authenticated player pages (`f_*.cfm`, `Modules/Pages/`) | `Skills/game-page` |
| Public pages (`p_*.cfm`, `i_p.cfm`) | `Skills/game-public-page` |
| Queries, application scope, locking, `cf_*` tags, columns | `Skills/game-data-layer` |
| Markup, `theme.css`, themes, `gc-*` components | `Skills/game-frontend` |
| Scheduled jobs (`Z_*.cfm`) | `Skills/game-background-job` |
| Before every commit | `Skills/game-verify` |

Read the relevant skill before you start, not after. The sections below are summaries and must not be used in place of the skill.

## Identity

- **What:** Browser-based multiplayer persistent-universe strategy game (text/HTML MMORPG)
- **Stack:** ColdFusion (CF5 syntax on Lucee engine) · MySQL 8 (`gcc` schema) · Apache 2 · server-rendered HTML · jQuery-era JS
- **Version:** 2.77
- **Owner:** Wolfren Industries
- **Servers:** Real-Time (slot 1, 1 turn/8s) and Turn-Based (slot 2, 1 turn/2.5min). Slots 3–5 are dead code.

## Quick-Start Decision Tree

```
Need to change game behavior for a logged-in player?
  → f_com_*.cfm (player commands) or f_fed_*.cfm (federation)

Need to change a public/unauthenticated page?
  → p_*.cfm at root

Need to change shared logic used across features?
  → s_*.cfm at root, or Modules/Functions/Functions.cfm

Need to change the page layout, nav, or frame?
  → i_f_800.cfm (game frame), i_*.cfm (partials)

Need to change background/scheduled jobs?
  → Z_*.cfm at root

Need to change admin panel?
  → edmin/ directory

Need to change app config, datasources, or session settings?
  → Application.cfc

Need to trace a request end-to-end?
  → Start at i.cfm, follow url.f or url.p routing
```

## Request Lifecycle

All HTTP requests enter through `i.cfm`:

```
i.cfm
├─ Loads s_loadvar.cfm (basic variables)
├─ Loads Modules/Functions/Functions.cfm
├─ IF session.userid exists AND url.f present:
│   ├─ s_loadsystem.cfm (app-scope cached data)
│   ├─ i_f_800.cfm (game page frame: nav, layout, chat)
│   │   └─ Includes f_<url.f>.cfm OR Modules/Pages/<url.f>.cfm
│   └─ Renders full game page
├─ ELSE (unauthenticated):
│   ├─ i_p.cfm (public frame)
│   │   └─ Includes p_<url.p>.cfm
│   └─ Renders public page
```

**Key variables:**
- `url.f` — routes to feature files (authenticated)
- `url.p` — routes to public page files (unauthenticated)
- `session.userid` — current player ID
- `session.server` — active server slot (1 or 2)

## File Naming Map

| Prefix | Role | Count | Location |
|--------|------|-------|----------|
| `p_*.cfm` | Public pages (login, signup, landing) | ~33 | root |
| `f_com_*.cfm` | Player game commands (attack, build, research) | ~75 | root |
| `f_fed_*.cfm` | Federation features | 11 | root |
| `f_*.cfm` (other) | Misc features (PM, rank, etc.) | ~15 | root |
| `s_*.cfm` | System utilities, shared logic, value lookups | ~65 | root |
| `i_*.cfm` | Includes/partials (layout, header, nav) | ~10 | root |
| `Z_*.cfm` | Background/scheduled processes | 12 | root |
| `*.cfc` | Components (Application config, controllers) | few | root + Modules/ |

## Directory Map

```
/opt/gcc-dev/app/
├── Application.cfc            # Config: datasources, sessions, server slots, PayPal
├── i.cfm                      # Main router — ALL requests enter here
├── i_f_800.cfm                # Authenticated game page frame (nav + layout + chat)
├── i_p.cfm                    # Public page frame
│
├── f_com_*.cfm                # ~75 player command files
├── f_fed_*.cfm                # 11 federation files
├── s_*.cfm                    # ~65 system/utility files
├── p_*.cfm                    # ~33 public page files
├── Z_*.cfm                    # 12 background process files
│
├── Modules/
│   ├── Controllers/           # CFC controllers
│   │   ├── Projects/          # Project system (1.cfc–10.cfc, Update.cfc, Activate.cfc)
│   │   └── HT/Projects/      # High-tech project variants
│   ├── Pages/                 # View templates loaded via url.f routing
│   │   └── Projects/          # Project-specific views
│   ├── Functions/
│   │   └── Functions.cfm      # Shared utility functions (validateURL, formValidate, hackCheck)
│   ├── NPC/                   # NPC generation and AI
│   │   └── Fleet/             # NPC fleet logic
│   └── Ships/
│       └── Builder.cfm        # Ship construction logic
│
├── edmin/                     # Admin panel (~168 files)
│   ├── Application.cfc        # Admin-specific config
│   ├── p_login.cfm            # Admin login
│   └── f_admin_*.cfm          # Admin action files
│
├── forum2/                    # Active forum system (~25 files)
├── forum/                     # Legacy forum (dead — superseded by forum2/)
├── l/                         # Localized templates (~50 files mirror root structure)
├── text/                      # In-game manual pages (manual_*.cfm)
├── help/                      # Help documentation (HTML)
├── Stats/                     # Statistics/reporting pages
├── JS/                        # JavaScript files (~84)
├── CSS/                       # Stylesheets (~59)
└── i/                         # Image assets
```

## Core Game Concepts

Agents editing feature files need to understand these:

**Turns** — The universal action currency. Players spend turns to do everything (build, research, attack, explore, generate income). Turns accumulate automatically via `z_endturn.cfm` up to a cap. Turn spending logic lives in `s_endturn.cfm`.

**Colonies** — Planets a player owns. Each has population, buildings, ore, food. Managed via `f_com_col*.cfm`. Colony state is frequently cached in application scope.

**Ships** — Built at colonies, organized into fleets. Ship types and classes loaded from DB into app scope by `s_loadsystem.cfm`. Building logic in `Modules/Ships/Builder.cfm` and `f_com_ship*.cfm`.

**Research** — Tech tree loaded into `application` scope from `restree` table. Research pages: `f_com_research*.cfm`, `f_com_rtree*.cfm`.

**Combat** — Attack flow spans multiple files: `f_com_attack*.cfm` (UI/initiation) → `s_com_attack*.cfm` (calculation/resolution).

**Federations** — Player alliances. All fed logic in `f_fed_*.cfm`.

**Market** — Player-to-player trading. `f_com_market*.cfm` (UI) and `s_com_market*.cfm` (logic).

**Projects** — Long-running constructions. Modularized in `Modules/Controllers/Projects/` and `Modules/Pages/Projects/`.

**NPC System** — AI-controlled entities. `Modules/NPC/` handles generation and fleet behavior.

## Key Files Reference

| File | Why It Matters |
|------|---------------|
| `Application.cfc` | All config: datasources, sessions, server slots, env flags. Read first for environment issues. |
| `i.cfm` | Main router. Trace any request starting here. |
| `i_f_800.cfm` | Game page frame. Contains nav, layout, chat, resource display, turn counter. |
| `s_loadsystem.cfm` | Loads all application-scope cached data (races, ships, research, planets, etc.) on first request. |
| `s_loadvar.cfm` | Basic variable initialization, loaded before s_loadsystem. |
| `Modules/Functions/Functions.cfm` | Shared functions: `validateURL()`, `formValidate()`, `hackCheck()`, `reverseIPDisplay()`. |
| `s_endturn.cfm` | Turn spending logic (called per player action). |
| `z_endturn.cfm` | Turn generation (scheduled background process). |
| `Z_Hourly_Processes.cfm` | Master hourly job dispatcher. |
| `s_error2.cfm` | Error notification (sends email). |
| `s_loadbar.cfm` | Left navigation bar generation. |
| `theme.css` | Theme tokens (colors, fonts, radii, shadows) + component classes for all three themes. |
| `f_option_screen.cfm` | Theme picker UI. Writes the theme to `user_ui.theme` / `session.theme`. |

## Critical Rules

### 1. Security Guard — `ihasrunflag`

Most include files check `ihasrunflag` to prevent direct URL access. If you create a new `.cfm` that should only be included (not hit directly), add this at the top:

```cfml
<cfif parameterExists(ihasrunflag) IS "NO"><cfabort></cfif>
```

### 2. Database — Only Use `gcc`

The `gcc` and `gcc_log` datasources are the only ones that matter. Other datasources (`ucc`, `ucc_log`, `gcs`, `gcs_log`) are legacy and should not be used for new code.

### 3. SQL — Script `Query` Objects With Named Params

Full rules are in `Skills/game-data-layer`. Summary:

The codebase has a legacy pattern of raw variable interpolation inside tag-based `<cfquery>` blocks. **Do not write new `<cfquery>` tags, and do not add `<cfqueryparam>` to existing ones.** Every new query, and every legacy query you need to change, is written as a CFScript `Query` object with named params:

```cfml
<cfscript>
Qry = new Query();
Qry.setDatasource(application.DS);
Qry.setSQL("INSERT INTO project_user
			(userid, project, credit, turn, finishflag)
			VALUES
			(:userid, :projectid, 0, 0, 0)
			ON DUPLICATE KEY
			UPDATE userid = userid");
Qry.addParam(name="userid", value=session.userid, cfsqltype="integer");
Qry.addParam(name="projectid", value=requestedProjectId, cfsqltype="integer");
QryFetch = Qry.execute();
</cfscript>
```

Use named arguments with `addParam(name, value, cfsqltype, scale, null, maxlength)`. Lucee may not bind SQL params correctly with positional arguments. Use these short `cfsqltype` names:

| Full Name | Short Name |
|-----------|------------|
| `cf_sql_integer` | `integer` |
| `cf_sql_varchar` | `varchar` |
| `cf_sql_char` | `char` |
| `cf_sql_timestamp` | `timestamp` |
| `cf_sql_date` | `date` |
| `cf_sql_time` | `time` |
| `cf_sql_bigint` | `bigint` |
| `cf_sql_decimal` | `decimal` |
| `cf_sql_double` | `double` |
| `cf_sql_float` | `float` |
| `cf_sql_numeric` | `numeric` |
| `cf_sql_bit` | `bit` |
| `cf_sql_boolean` | `boolean` |

When you edit a legacy `<cfquery>`, convert **that query** to a `Query` object and bind all of its values. Don't convert queries you didn't need to touch, and don't partly convert one.

```cfml
<!--- WRONG: legacy tag query (~700 in the codebase) --->
<cfquery datasource="#DS#" name="q">
SELECT * FROM user WHERE userid = #session.userid# AND name = '#form.name#'
</cfquery>

<!--- ALSO WRONG: patching the tag with cfqueryparam --->
WHERE userid = <cfqueryparam value="#session.userid#" cfsqltype="cf_sql_integer">

<!--- RIGHT --->
<cfscript>
Qry = new Query();
Qry.setDatasource(application.DS);
Qry.setSQL("SELECT * FROM user WHERE userid = :userid AND name = :name");
Qry.addParam(name="userid", value=session.userid, cfsqltype="integer");
Qry.addParam(name="name",   value=form.name,      cfsqltype="varchar");
q = Qry.execute().getResult();
</cfscript>
```

Bind every value from `form.*`, `url.*`, `cgi.*`, `cookie.*`, `client.*`, and JS/AJAX requests, including values that "should" be numeric. Also bind any string that could hold player-entered or DB-stored text, and every `IN (...)` list built from request data (one named param per value).

Unchanged legacy queries you haven't opened for editing stay as they are. Don't make drive-by conversions.

Do not treat sanitization as a blanket exemption:

- `validateURL()` is a helper, not a global enforcement point.
- The top-layer `url.p` / `url.f` cleanup only proves "alphanumeric/underscore", not business validity.
- `formValidate()` strips characters but does not replace query parameters, output encoding, or allowlist checks.
- SQL identifiers, column names, sort directions, and table names cannot be safely parameterized; validate those with an explicit allowlist instead.

### 4. Output Encoding — Encode User Content

The codebase currently has zero output encoding. **All new or modified output of user-supplied data must be encoded:**

```cfml
<!--- HTML body text --->
#encodeForHTML(playerName)#

<!--- HTML attribute --->
value="#encodeForHTMLAttribute(val)#"

<!--- JavaScript string --->
var x = '#encodeForJavaScript(val)#';

<!--- URL parameter --->
?name=#encodeForURL(val)#
```

### 5. No `evaluate()` — Use Struct Bracket Notation

```cfml
<!--- WRONG --->
#evaluate("application.server#session.server#_turnmin")#

<!--- RIGHT --->
#application["server#session.server#_turnmin"]#
```

### 6. Application Scope

`s_loadsystem.cfm` caches heavily into `application.*`. Check existing application variables before adding DB queries — the data may already be loaded. Key cached data: ship classes, research tree, race data, planet types, user names, federation names, navigation bars.

### 7. Server Slots

Only slots 1 (Real-Time) and 2 (Turn-Based) are active. `this.ServerMax = 5` in Application.cfc is legacy. Code referencing servers 3–5 is dead.

## Frontend / Theming

Full rules are in `Skills/game-frontend`. Summary:

Three themes are selected via `[data-theme]` on `<body>`. The active theme is `session.theme`, which is persisted in `user_ui.theme` and chosen at `f=option_screen`. Don't read `client.theme`; it's stale.

| Theme | Notes |
|-------|-------|
| `nebula` (default) | Modern dark — navy surfaces, amber accents, Fraunces + IBM Plex fonts. |
| `classic` | Legacy look: crimson/black chrome, Arial body, starfield bg. Keep its familiar feel, but it can be changed. |
| `daylight` | High-contrast light mode — indigo accents on white. |

All tokens (colors, fonts, radii, shadows, motion) live in `theme.css` as CSS custom properties scoped per `body[data-theme]`. Use tokens (`var(--bg-surface)`, `var(--accent)`, `var(--accent-good)`, `var(--accent-hot)`, `var(--font-display)`, `var(--font-mono)`, `var(--radius-md)`, etc.) — never hardcode colors or fonts in new code.

### 1. Classic can change, just not drastically

Classic exists so long-time players keep a familiar UI. It doesn't have to stay frozen. Layout and markup changes that affect Classic are fine as long as the page still looks and reads like Classic: same colors, fonts, general structure, and information in roughly the same places. Avoid changes that would make a Classic player feel they're on a different game.

When a Classic change is more than cosmetic (sections move, tables become grids, controls get renamed), mention it in the commit or PR so it can be reviewed.

### 2. One template, themed with CSS. Avoid dual branches.

Keeping entirely separate Classic and modern markup has doubled the code to maintain. **Don't add new `<cfif isClassicTheme>` markup branches.** Write one shared template and let `theme.css` style it per theme with tokens and `body[data-theme]` scopes:

```css
.thing { /* shared structure, token-driven */ }
body[data-theme="classic"] .thing { /* bring back the legacy look where needed */ }
```

A theme check in CFML is fine for small differences, like one label or an extra class. It's not fine for duplicating a whole page. When you're already doing significant work on a page with an existing dual branch (e.g. `f_com_income.cfm`), prefer merging it into shared markup if Classic's look can be closely preserved with CSS.

### 3. Mobile-first, one-viewport target

Every new modern layout must fit a mobile viewport without scrolling. Rules:

- Write base CSS for narrow screens; widen with `@media (min-width: 520px|640px|992px)`.
- Stack side-by-side panels below 640px.
- Cut empty cells, blank rows, and decorative section spacers.
- Body 13px. Numerics in `var(--font-mono)` with `font-variant-numeric: tabular-nums`. Hero figures in `var(--font-display)`. Section labels in 10–11px uppercase mono.
- Never emit empty `<td>` padders. Use CSS grid or `<dl>` so cells appear only when they have content.
- Tables and row lists size columns to their content, never fixed px/% widths. Put the columns on the list container and give the header and each row `grid-template-columns: subgrid` so they stay aligned. Text columns use `auto`, numbers and actions use `minmax(max-content, auto)`, and non-name cells are centred. Reference: `.gc-fed-roster` in `theme.css`.
- Names (empires, federations, races) never get an ellipsis. Let them wrap with `overflow-wrap: anywhere`.
- Prefer a single dashed divider over a thick border plus 10px of padding.

### 4. Reusable modern components

Patterns established on `f_com_income.cfm`, defined in `theme.css` under the `INCOME / UPKEEP LEDGER` block. Reuse or extend rather than inventing per-page markup.

- **Hero stat cards** — `.gc-income-hero` > `.gc-income-hero__card` with `--primary` / `--positive` / `--negative` modifiers. At-a-glance metrics with signed, color-coded values and an accent edge strip.
- **Credit/debit sections** — `.gc-income-section` with `--credits` (green top-edge) / `--debits` (red top-edge) modifier, and `__head` / `__title` / `__total` children.
- **Line items** — `.gc-ledger` > `.gc-ledger__row` with `__label` + `__actual` grid, optional `__calc` muted subline for `base × mod%`, and `__empty` for placeholder text.

Class names are currently income-scoped. When redesigning a non-financial page with the same shape, treat them as a credit/debit/ledger pattern rather than literally income-only. If a truly generic need emerges, extract aliases (`.gc-hero`, `.gc-card`, `.gc-section`) before shipping.

### 5. Where styles live

- **Shared modern styling** → `theme.css`, appended at the bottom grouped by feature with a comment header.
- **Classic-specific styling** → `theme.css` under a `body[data-theme="classic"]` scope.
- **Page-specific one-offs** → avoid; prefer adding a reusable class to `theme.css`.
- **Legacy stylesheets are dead** → `Style.css`, `RenStyle.css`, `Foundation_Style.css`, and most files in `CSS/` are being deprecated. Don't add new rules to them.

## Feature Area Quick Reference

| Area | UI Files | Logic Files | DB Tables (likely) |
|------|----------|-------------|--------------------|
| Authentication | `p_login.cfm`, `p_signup.cfm` | — | `user` |
| Turns | — | `s_endturn.cfm`, `z_endturn.cfm`, `Z_Hourly_Processes.cfm` | `user` (turn count) |
| Colonies | `f_com_col*.cfm` | `s_com_col_*.cfm` | `colony`, `building` |
| Ships | `f_com_ship*.cfm` | `Modules/Ships/Builder.cfm` | `ship`, `shipclass` |
| Research | `f_com_research*.cfm`, `f_com_rtree*.cfm` | `s_com_research_*.cfm` | `restree`, `user_research` |
| Combat | `f_com_attack*.cfm` | `s_com_attack*.cfm` | `attack`, `ship` |
| Exploration | `f_com_explore*.cfm` | `s_explore_*.cfm` | `system`, `planet` |
| Federations | `f_fed_*.cfm` | — | `federation`, `fed_member` |
| Market | `f_com_market*.cfm` | `s_com_market_*.cfm` | `market` |
| Projects | `f_com_project*.cfm` | `Modules/Controllers/Projects/` | `project` |
| NPC | — | `Modules/NPC/` | `npc`, `npc_fleet` |
| Private Messages | `f_pm.cfm` | — | `pm` |
| Sector Messages | `f_com_msgsector*.cfm` | — | `msg_sector` |
| Forums | `forum2/` | `forum2/s_*.cfm` | `forum_*` |
| Admin | `edmin/` | `edmin/f_admin_*.cfm` | various |
| Donations/Store | `f_com_donate*.cfm`, `f_com_cart*.cfm` | — | `donation`, `cart` |
| Background Jobs | — | `Z_*.cfm` | various |

## Localization

The `l/` directory mirrors the root file structure with localized versions. If you modify text in a root template, check if `l/<same_filename>` exists and needs a corresponding update.

## Admin Panel

The `edmin/` directory is a semi-independent sub-application with its own `Application.cfc`. Admin files follow the same prefix convention (`p_` for pages, `f_admin_` for actions, `s_admin_` for utilities). Admin access is gated by a user-record flag. Reference files: `edmin/_ids.txt` files contain ID-to-name mappings for projects and research items.

## Background Processes

| File | Schedule | Purpose |
|------|----------|---------|
| `Z_Hourly_Processes.cfm` | Hourly | Master dispatcher — runs sub-jobs |
| `z_endturn.cfm` | Every 8s (RT) / 2.5min (TB) | Turn generation for all players |
| `Z_Upkeep_Calc.cfm` | Periodic | Ship upkeep cost calculation |
| `Z_UpdateDB*.cfm` (1–5) | As needed | Database maintenance/migrations |
| `Z_Ships.cfm` | Periodic | Ship-related batch processing |
| `Z_Ajax_Chat.cfm` | Frequent | Chat message polling |

These run via server-side scheduler, not user requests. They currently have empty `cfcatch` blocks — errors are silently swallowed.

## Known Hazards

1. **~700 SQL injection points** — raw `#variable#` in queries across 200+ files. Some routes and helper inputs are partially sanitized, but that is not a substitute for query parameterization or explicit allowlists.
2. **Zero XSS encoding** — no `encodeForHTML()` anywhere. User content renders as raw HTML.
3. **Plaintext passwords** — login files compare plaintext. Migration to bcrypt is planned.
4. **Partial / inconsistent request sanitization** — `i.cfm` contains ad hoc query-string filtering and route cleanup, and `Functions.cfm` has `validateURL()`, `formValidate()`, and `hackCheck()` helpers, but enforcement is inconsistent. `validateURL()` and `hackCheck()` are not centrally called, and the `i.cfm` hack-detection branch is disabled by `|| 1 == 2`.
5. **`evaluate()` usage** — `i_f_800.cfm` uses `evaluate()` for dynamic variable access. Replace with struct bracket notation.
6. **Hardcoded user bypasses** — `p_login.cfm` lines 89–93 have backdoors for specific user IDs.
7. **Error info disclosure** — `Application.cfc` `onError` exposes file paths and stack traces to browser.
8. **PayPal credentials in source** — `Application.cfc` lines 194–206 have hardcoded client ID and secret.

## Related Documentation

| File | Contents |
|------|----------|
| `project_overview.md` | Detailed project overview, architecture, full file naming conventions |
| `modernization_plan.md` | Phased plan for security fixes, reliability, legacy cleanup, and frontend modernization |
| `README.md` | Brief repo description |
| `help/` | In-game help pages (HTML) |
| `text/manual_*.cfm` | In-game manual content |
